VirtualBox

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

Last change on this file since 77575 was 77248, checked in by vboxsync, 6 years ago

SharedFoldersService: Use RTFileReadAt and RTFileWriteAt so we can skip the seek. bugref:9172

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 77.0 KB
Line 
1/* $Id: vbsf.cpp 77248 2019-02-11 01:34:28Z vboxsync $ */
2/** @file
3 * Shared Folders - VBox Shared Folders.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_SHARED_FOLDERS
23#ifdef UNITTEST
24# include "testcase/tstSharedFolderService.h"
25#endif
26
27#include "vbsfpath.h"
28#include "mappings.h"
29#include "vbsf.h"
30#include "shflhandle.h"
31
32#include <VBox/AssertGuest.h>
33#include <VBox/param.h>
34#include <iprt/alloc.h>
35#include <iprt/assert.h>
36#include <iprt/asm.h>
37#include <iprt/fs.h>
38#include <iprt/dir.h>
39#include <iprt/file.h>
40#include <iprt/path.h>
41#include <iprt/string.h>
42#include <iprt/symlink.h>
43#include <iprt/uni.h>
44#include <iprt/stream.h>
45#ifdef RT_OS_DARWIN
46# include <Carbon/Carbon.h>
47#endif
48
49#ifdef UNITTEST
50# include "teststubs.h"
51#endif
52
53
54/*********************************************************************************************************************************
55* Defined Constants And Macros *
56*********************************************************************************************************************************/
57#define SHFL_RT_LINK(pClient) ((pClient)->fu32Flags & SHFL_CF_SYMLINKS ? RTPATH_F_ON_LINK : RTPATH_F_FOLLOW_LINK)
58
59
60/**
61 * @todo find a better solution for supporting the execute bit for non-windows
62 * guests on windows host. Search for "0111" to find all the relevant places.
63 */
64
65void vbsfStripLastComponent(char *pszFullPath, uint32_t cbFullPathRoot)
66{
67 RTUNICP cp;
68
69 /* Do not strip root. */
70 char *s = pszFullPath + cbFullPathRoot;
71 char *delimSecondLast = NULL;
72 char *delimLast = NULL;
73
74 LogFlowFunc(("%s -> %s\n", pszFullPath, s));
75
76 for (;;)
77 {
78 cp = RTStrGetCp(s);
79
80 if (cp == RTUNICP_INVALID || cp == 0)
81 {
82 break;
83 }
84
85 if (cp == RTPATH_DELIMITER)
86 {
87 if (delimLast != NULL)
88 {
89 delimSecondLast = delimLast;
90 }
91
92 delimLast = s;
93 }
94
95 s = RTStrNextCp(s);
96 }
97
98 if (cp == 0)
99 {
100 if (delimLast + 1 == s)
101 {
102 if (delimSecondLast)
103 {
104 *delimSecondLast = 0;
105 }
106 else if (delimLast)
107 {
108 *delimLast = 0;
109 }
110 }
111 else
112 {
113 if (delimLast)
114 {
115 *delimLast = 0;
116 }
117 }
118 }
119
120 LogFlowFunc(("%s, %s, %s\n", pszFullPath, delimLast, delimSecondLast));
121}
122
123static int vbsfBuildFullPath(SHFLCLIENTDATA *pClient, SHFLROOT root, PSHFLSTRING pPath,
124 uint32_t cbPath, char **ppszFullPath, uint32_t *pcbFullPathRoot,
125 bool fWildCard = false, bool fPreserveLastComponent = false)
126{
127 char *pszHostPath = NULL;
128 uint32_t fu32PathFlags = 0;
129 uint32_t fu32Options = VBSF_O_PATH_CHECK_ROOT_ESCAPE
130 | (fWildCard? VBSF_O_PATH_WILDCARD: 0)
131 | (fPreserveLastComponent? VBSF_O_PATH_PRESERVE_LAST_COMPONENT: 0);
132
133 int rc = vbsfPathGuestToHost(pClient, root, pPath, cbPath,
134 &pszHostPath, pcbFullPathRoot, fu32Options, &fu32PathFlags);
135 if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
136 {
137 LogRel2(("SharedFolders: GuestToHost 0x%RX32 [%.*s]->[%s] %Rrc\n", fu32PathFlags, pPath->u16Length, &pPath->String.utf8[0], pszHostPath, rc));
138 }
139 else
140 {
141 LogRel2(("SharedFolders: GuestToHost 0x%RX32 [%.*ls]->[%s] %Rrc\n", fu32PathFlags, pPath->u16Length / 2, &pPath->String.ucs2[0], pszHostPath, rc));
142 }
143
144 if (RT_SUCCESS(rc))
145 {
146 if (ppszFullPath)
147 *ppszFullPath = pszHostPath;
148 }
149 return rc;
150}
151
152static void vbsfFreeFullPath(char *pszFullPath)
153{
154 vbsfFreeHostPath(pszFullPath);
155}
156
157typedef enum VBSFCHECKACCESS
158{
159 VBSF_CHECK_ACCESS_READ = 0,
160 VBSF_CHECK_ACCESS_WRITE = 1
161} VBSFCHECKACCESS;
162
163/**
164 * Check if the handle data is valid and the operation is allowed on the shared folder.
165 *
166 * @returns IPRT status code
167 * @param pClient Data structure describing the client accessing the shared folder
168 * @param root The index of the shared folder in the table of mappings.
169 * @param pHandle Information about the file or directory object.
170 * @param enmCheckAccess Whether the operation needs read only or write access.
171 */
172static int vbsfCheckHandleAccess(SHFLCLIENTDATA *pClient, SHFLROOT root,
173 SHFLFILEHANDLE *pHandle, VBSFCHECKACCESS enmCheckAccess)
174{
175 /* Handle from the same 'root' index? */
176 if (RT_LIKELY(RT_VALID_PTR(pHandle) && root == pHandle->root))
177 { /* likely */ }
178 else
179 return VERR_INVALID_HANDLE;
180
181 /* Check if the guest is still allowed to access this share.
182 * vbsfMappingsQueryWritable returns error if the shared folder has been removed from the VM settings.
183 */
184 bool fWritable;
185 int rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
186 if (RT_SUCCESS(rc))
187 { /* likely */ }
188 else
189 return VERR_ACCESS_DENIED;
190
191 if (enmCheckAccess == VBSF_CHECK_ACCESS_WRITE)
192 {
193 /* Operation requires write access. Check if the shared folder is writable too. */
194 if (RT_LIKELY(fWritable))
195 { /* likely */ }
196 else
197 return VERR_WRITE_PROTECT;
198 }
199
200 return VINF_SUCCESS;
201}
202
203/**
204 * Convert shared folder create flags (see include/iprt/shflsvc.h) into iprt create flags.
205 *
206 * @returns iprt status code
207 * @param fWritable whether the shared folder is writable
208 * @param fShflFlags shared folder create flags
209 * @param fMode file attributes
210 * @param handleInitial initial handle
211 * @retval pfOpen iprt create flags
212 */
213static int vbsfConvertFileOpenFlags(bool fWritable, unsigned fShflFlags, RTFMODE fMode, SHFLHANDLE handleInitial, uint32_t *pfOpen)
214{
215 uint32_t fOpen = 0;
216 int rc = VINF_SUCCESS;
217
218 if ( (fMode & RTFS_DOS_MASK) != 0
219 && (fMode & RTFS_UNIX_MASK) == 0)
220 {
221 /* A DOS/Windows guest, make RTFS_UNIX_* from RTFS_DOS_*.
222 * @todo this is based on rtFsModeNormalize/rtFsModeFromDos.
223 * May be better to use RTFsModeNormalize here.
224 */
225 fMode |= RTFS_UNIX_IRUSR | RTFS_UNIX_IRGRP | RTFS_UNIX_IROTH;
226 /* x for directories. */
227 if (fMode & RTFS_DOS_DIRECTORY)
228 fMode |= RTFS_TYPE_DIRECTORY | RTFS_UNIX_IXUSR | RTFS_UNIX_IXGRP | RTFS_UNIX_IXOTH;
229 /* writable? */
230 if (!(fMode & RTFS_DOS_READONLY))
231 fMode |= RTFS_UNIX_IWUSR | RTFS_UNIX_IWGRP | RTFS_UNIX_IWOTH;
232
233 /* Set the requested mode using only allowed bits. */
234 fOpen |= ((fMode & RTFS_UNIX_MASK) << RTFILE_O_CREATE_MODE_SHIFT) & RTFILE_O_CREATE_MODE_MASK;
235 }
236 else
237 {
238 /* Old linux and solaris additions did not initialize the Info.Attr.fMode field
239 * and it contained random bits from stack. Detect this using the handle field value
240 * passed from the guest: old additions set it (incorrectly) to 0, new additions
241 * set it to SHFL_HANDLE_NIL(~0).
242 */
243 if (handleInitial == 0)
244 {
245 /* Old additions. Do nothing, use default mode. */
246 }
247 else
248 {
249 /* New additions or Windows additions. Set the requested mode using only allowed bits.
250 * Note: Windows guest set RTFS_UNIX_MASK bits to 0, which means a default mode
251 * will be set in fOpen.
252 */
253 fOpen |= ((fMode & RTFS_UNIX_MASK) << RTFILE_O_CREATE_MODE_SHIFT) & RTFILE_O_CREATE_MODE_MASK;
254 }
255 }
256
257 switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_RW))
258 {
259 default:
260 case SHFL_CF_ACCESS_NONE:
261 {
262#ifdef RT_OS_WINDOWS
263 if (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_ATTR) != SHFL_CF_ACCESS_ATTR_NONE)
264 fOpen |= RTFILE_O_ATTR_ONLY;
265 else
266#endif
267 fOpen |= RTFILE_O_READ;
268 Log(("FLAG: SHFL_CF_ACCESS_NONE\n"));
269 break;
270 }
271
272 case SHFL_CF_ACCESS_READ:
273 {
274 fOpen |= RTFILE_O_READ;
275 Log(("FLAG: SHFL_CF_ACCESS_READ\n"));
276 break;
277 }
278
279 case SHFL_CF_ACCESS_WRITE:
280 {
281 fOpen |= RTFILE_O_WRITE;
282 Log(("FLAG: SHFL_CF_ACCESS_WRITE\n"));
283 break;
284 }
285
286 case SHFL_CF_ACCESS_READWRITE:
287 {
288 fOpen |= RTFILE_O_READWRITE;
289 Log(("FLAG: SHFL_CF_ACCESS_READWRITE\n"));
290 break;
291 }
292 }
293
294 if (fShflFlags & SHFL_CF_ACCESS_APPEND)
295 {
296 fOpen |= RTFILE_O_APPEND;
297 }
298
299 switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_ATTR))
300 {
301 default:
302 case SHFL_CF_ACCESS_ATTR_NONE:
303 {
304 fOpen |= RTFILE_O_ACCESS_ATTR_DEFAULT;
305 Log(("FLAG: SHFL_CF_ACCESS_ATTR_NONE\n"));
306 break;
307 }
308
309 case SHFL_CF_ACCESS_ATTR_READ:
310 {
311 fOpen |= RTFILE_O_ACCESS_ATTR_READ;
312 Log(("FLAG: SHFL_CF_ACCESS_ATTR_READ\n"));
313 break;
314 }
315
316 case SHFL_CF_ACCESS_ATTR_WRITE:
317 {
318 fOpen |= RTFILE_O_ACCESS_ATTR_WRITE;
319 Log(("FLAG: SHFL_CF_ACCESS_ATTR_WRITE\n"));
320 break;
321 }
322
323 case SHFL_CF_ACCESS_ATTR_READWRITE:
324 {
325 fOpen |= RTFILE_O_ACCESS_ATTR_READWRITE;
326 Log(("FLAG: SHFL_CF_ACCESS_ATTR_READWRITE\n"));
327 break;
328 }
329 }
330
331 /* Sharing mask */
332 switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_DENY))
333 {
334 default:
335 case SHFL_CF_ACCESS_DENYNONE:
336 fOpen |= RTFILE_O_DENY_NONE;
337 Log(("FLAG: SHFL_CF_ACCESS_DENYNONE\n"));
338 break;
339
340 case SHFL_CF_ACCESS_DENYREAD:
341 fOpen |= RTFILE_O_DENY_READ;
342 Log(("FLAG: SHFL_CF_ACCESS_DENYREAD\n"));
343 break;
344
345 case SHFL_CF_ACCESS_DENYWRITE:
346 fOpen |= RTFILE_O_DENY_WRITE;
347 Log(("FLAG: SHFL_CF_ACCESS_DENYWRITE\n"));
348 break;
349
350 case SHFL_CF_ACCESS_DENYALL:
351 fOpen |= RTFILE_O_DENY_ALL;
352 Log(("FLAG: SHFL_CF_ACCESS_DENYALL\n"));
353 break;
354 }
355
356 /* Open/Create action mask */
357 switch (BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
358 {
359 case SHFL_CF_ACT_OPEN_IF_EXISTS:
360 if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
361 {
362 fOpen |= RTFILE_O_OPEN_CREATE;
363 Log(("FLAGS: SHFL_CF_ACT_OPEN_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
364 }
365 else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
366 {
367 fOpen |= RTFILE_O_OPEN;
368 Log(("FLAGS: SHFL_CF_ACT_OPEN_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n"));
369 }
370 else
371 {
372 Log(("FLAGS: invalid open/create action combination\n"));
373 rc = VERR_INVALID_PARAMETER;
374 }
375 break;
376 case SHFL_CF_ACT_FAIL_IF_EXISTS:
377 if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
378 {
379 fOpen |= RTFILE_O_CREATE;
380 Log(("FLAGS: SHFL_CF_ACT_FAIL_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
381 }
382 else
383 {
384 Log(("FLAGS: invalid open/create action combination\n"));
385 rc = VERR_INVALID_PARAMETER;
386 }
387 break;
388 case SHFL_CF_ACT_REPLACE_IF_EXISTS:
389 if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
390 {
391 fOpen |= RTFILE_O_CREATE_REPLACE;
392 Log(("FLAGS: SHFL_CF_ACT_REPLACE_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
393 }
394 else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
395 {
396 fOpen |= RTFILE_O_OPEN | RTFILE_O_TRUNCATE;
397 Log(("FLAGS: SHFL_CF_ACT_REPLACE_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n"));
398 }
399 else
400 {
401 Log(("FLAGS: invalid open/create action combination\n"));
402 rc = VERR_INVALID_PARAMETER;
403 }
404 break;
405 case SHFL_CF_ACT_OVERWRITE_IF_EXISTS:
406 if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
407 {
408 fOpen |= RTFILE_O_CREATE_REPLACE;
409 Log(("FLAGS: SHFL_CF_ACT_OVERWRITE_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
410 }
411 else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
412 {
413 fOpen |= RTFILE_O_OPEN | RTFILE_O_TRUNCATE;
414 Log(("FLAGS: SHFL_CF_ACT_OVERWRITE_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n"));
415 }
416 else
417 {
418 Log(("FLAGS: invalid open/create action combination\n"));
419 rc = VERR_INVALID_PARAMETER;
420 }
421 break;
422 default:
423 rc = VERR_INVALID_PARAMETER;
424 Log(("FLAG: SHFL_CF_ACT_MASK_IF_EXISTS - invalid parameter\n"));
425 }
426
427 if (RT_SUCCESS(rc))
428 {
429 if (!fWritable)
430 fOpen &= ~RTFILE_O_WRITE;
431
432 *pfOpen = fOpen;
433 }
434 return rc;
435}
436
437/**
438 * Open a file or create and open a new one.
439 *
440 * @returns IPRT status code
441 * @param pClient Data structure describing the client accessing the shared folder
442 * @param root The index of the shared folder in the table of mappings.
443 * @param pszPath Path to the file or folder on the host.
444 * @param pParms @a CreateFlags Creation or open parameters, see include/VBox/shflsvc.h
445 * @param pParms @a Info When a new file is created this specifies the initial parameters.
446 * When a file is created or overwritten, it also specifies the
447 * initial size.
448 * @retval pParms @a Resulte Shared folder status code, see include/VBox/shflsvc.h
449 * @retval pParms @a Handle On success the (shared folder) handle of the file opened or
450 * created
451 * @retval pParms @a Info On success the parameters of the file opened or created
452 */
453static int vbsfOpenFile(SHFLCLIENTDATA *pClient, SHFLROOT root, const char *pszPath, SHFLCREATEPARMS *pParms)
454{
455 LogFlow(("vbsfOpenFile: pszPath = %s, pParms = %p\n", pszPath, pParms));
456 Log(("SHFL create flags %08x\n", pParms->CreateFlags));
457
458 SHFLHANDLE handle = SHFL_HANDLE_NIL;
459 SHFLFILEHANDLE *pHandle = 0;
460 /* Open or create a file. */
461 uint32_t fOpen = 0;
462 bool fNoError = false;
463 static int cErrors;
464
465 /* is the guest allowed to write to this share? */
466 bool fWritable;
467 int rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
468 if (RT_FAILURE(rc))
469 fWritable = false;
470
471 rc = vbsfConvertFileOpenFlags(fWritable, pParms->CreateFlags, pParms->Info.Attr.fMode, pParms->Handle, &fOpen);
472 if (RT_SUCCESS(rc))
473 {
474 rc = VERR_NO_MEMORY; /* Default error. */
475 handle = vbsfAllocFileHandle(pClient);
476 if (handle != SHFL_HANDLE_NIL)
477 {
478 pHandle = vbsfQueryFileHandle(pClient, handle);
479 if (pHandle)
480 {
481 pHandle->root = root;
482 pHandle->file.fOpenFlags = fOpen;
483 rc = RTFileOpen(&pHandle->file.Handle, pszPath, fOpen);
484 }
485 }
486 }
487 if (RT_FAILURE(rc))
488 {
489 switch (rc)
490 {
491 case VERR_FILE_NOT_FOUND:
492 pParms->Result = SHFL_FILE_NOT_FOUND;
493
494 /* This actually isn't an error, so correct the rc before return later,
495 because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
496 fNoError = true;
497 break;
498 case VERR_PATH_NOT_FOUND:
499 pParms->Result = SHFL_PATH_NOT_FOUND;
500
501 /* This actually isn't an error, so correct the rc before return later,
502 because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
503 fNoError = true;
504 break;
505 case VERR_ALREADY_EXISTS:
506 RTFSOBJINFO info;
507
508 /** @todo Possible race left here. */
509 if (RT_SUCCESS(RTPathQueryInfoEx(pszPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient))))
510 {
511#ifdef RT_OS_WINDOWS
512 info.Attr.fMode |= 0111;
513#endif
514 vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
515 }
516 pParms->Result = SHFL_FILE_EXISTS;
517
518 /* This actually isn't an error, so correct the rc before return later,
519 because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
520 fNoError = true;
521 break;
522 case VERR_TOO_MANY_OPEN_FILES:
523 if (cErrors < 32)
524 {
525 LogRel(("SharedFolders host service: Cannot open '%s' -- too many open files.\n", pszPath));
526#if defined RT_OS_LINUX || defined(RT_OS_SOLARIS)
527 if (cErrors < 1)
528 LogRel(("SharedFolders host service: Try to increase the limit for open files (ulimit -n)\n"));
529#endif
530 cErrors++;
531 }
532 pParms->Result = SHFL_NO_RESULT;
533 break;
534 default:
535 pParms->Result = SHFL_NO_RESULT;
536 }
537 }
538 else
539 {
540 /** @note The shared folder status code is very approximate, as the runtime
541 * does not really provide this information. */
542 pParms->Result = SHFL_FILE_EXISTS; /* We lost the information as to whether it was
543 created when we eliminated the race. */
544 if ( ( SHFL_CF_ACT_REPLACE_IF_EXISTS
545 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
546 || ( SHFL_CF_ACT_OVERWRITE_IF_EXISTS
547 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS)))
548 {
549 /* For now, we do not treat a failure here as fatal. */
550 /** @todo Also set the size for SHFL_CF_ACT_CREATE_IF_NEW if
551 SHFL_CF_ACT_FAIL_IF_EXISTS is set. */
552 RTFileSetSize(pHandle->file.Handle, pParms->Info.cbObject);
553 pParms->Result = SHFL_FILE_REPLACED;
554 }
555 if ( ( SHFL_CF_ACT_FAIL_IF_EXISTS
556 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
557 || ( SHFL_CF_ACT_CREATE_IF_NEW
558 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW)))
559 {
560 pParms->Result = SHFL_FILE_CREATED;
561 }
562#if 0
563 /** @todo */
564 /* Set new attributes. */
565 if ( ( SHFL_CF_ACT_REPLACE_IF_EXISTS
566 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
567 || ( SHFL_CF_ACT_CREATE_IF_NEW
568 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW)))
569 {
570 RTFileSetTimes(pHandle->file.Handle,
571 &pParms->Info.AccessTime,
572 &pParms->Info.ModificationTime,
573 &pParms->Info.ChangeTime,
574 &pParms->Info.BirthTime
575 );
576
577 RTFileSetMode (pHandle->file.Handle, pParms->Info.Attr.fMode);
578 }
579#endif
580 RTFSOBJINFO info;
581
582 /* Get file information */
583 rc = RTFileQueryInfo(pHandle->file.Handle, &info, RTFSOBJATTRADD_NOTHING);
584 if (RT_SUCCESS(rc))
585 {
586#ifdef RT_OS_WINDOWS
587 info.Attr.fMode |= 0111;
588#endif
589 vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
590 }
591 }
592 /* Free resources if any part of the function has failed. */
593 if (RT_FAILURE(rc))
594 {
595 if ( (0 != pHandle)
596 && (NIL_RTFILE != pHandle->file.Handle)
597 && (0 != pHandle->file.Handle))
598 {
599 RTFileClose(pHandle->file.Handle);
600 pHandle->file.Handle = NIL_RTFILE;
601 }
602 if (SHFL_HANDLE_NIL != handle)
603 {
604 vbsfFreeFileHandle(pClient, handle);
605 }
606 pParms->Handle = SHFL_HANDLE_NIL;
607 }
608 else
609 {
610 pParms->Handle = handle;
611 }
612
613 /* Report the driver that all is okay, we're done here */
614 if (fNoError)
615 rc = VINF_SUCCESS;
616
617 LogFlow(("vbsfOpenFile: rc = %Rrc\n", rc));
618 return rc;
619}
620
621/**
622 * Open a folder or create and open a new one.
623 *
624 * @returns IPRT status code
625 * @param pClient Data structure describing the client accessing the shared folder
626 * @param root The index of the shared folder in the table of mappings.
627 * @param pszPath Path to the file or folder on the host.
628 * @param pParms @a CreateFlags Creation or open parameters, see include/VBox/shflsvc.h
629 * @retval pParms @a Result Shared folder status code, see include/VBox/shflsvc.h
630 * @retval pParms @a Handle On success the (shared folder) handle of the folder opened or
631 * created
632 * @retval pParms @a Info On success the parameters of the folder opened or created
633 *
634 * @note folders are created with fMode = 0777
635 */
636static int vbsfOpenDir(SHFLCLIENTDATA *pClient, SHFLROOT root, const char *pszPath,
637 SHFLCREATEPARMS *pParms)
638{
639 LogFlow(("vbsfOpenDir: pszPath = %s, pParms = %p\n", pszPath, pParms));
640 Log(("SHFL create flags %08x\n", pParms->CreateFlags));
641
642 int rc = VERR_NO_MEMORY;
643 SHFLHANDLE handle = vbsfAllocDirHandle(pClient);
644 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, handle);
645 if (0 != pHandle)
646 {
647 pHandle->root = root;
648 rc = VINF_SUCCESS;
649 pParms->Result = SHFL_FILE_EXISTS; /* May be overwritten with SHFL_FILE_CREATED. */
650 /** @todo Can anyone think of a sensible, race-less way to do this? Although
651 I suspect that the race is inherent, due to the API available... */
652 /* Try to create the folder first if "create if new" is specified. If this
653 fails, and "open if exists" is specified, then we ignore the failure and try
654 to open the folder anyway. */
655 if ( SHFL_CF_ACT_CREATE_IF_NEW
656 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW))
657 {
658 /** @todo render supplied attributes.
659 * bird: The guest should specify this. For windows guests RTFS_DOS_DIRECTORY should suffice. */
660 RTFMODE fMode = 0777;
661
662 pParms->Result = SHFL_FILE_CREATED;
663 rc = RTDirCreate(pszPath, fMode, 0);
664 if (RT_FAILURE(rc))
665 {
666 switch (rc)
667 {
668 case VERR_ALREADY_EXISTS:
669 pParms->Result = SHFL_FILE_EXISTS;
670 break;
671 case VERR_PATH_NOT_FOUND:
672 pParms->Result = SHFL_PATH_NOT_FOUND;
673 break;
674 default:
675 pParms->Result = SHFL_NO_RESULT;
676 }
677 }
678 }
679 if ( RT_SUCCESS(rc)
680 || (SHFL_CF_ACT_OPEN_IF_EXISTS == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS)))
681 {
682 /* Open the directory now */
683 rc = RTDirOpenFiltered(&pHandle->dir.Handle, pszPath, RTDIRFILTER_NONE, 0 /*fFlags*/);
684 if (RT_SUCCESS(rc))
685 {
686 RTFSOBJINFO info;
687
688 rc = RTDirQueryInfo(pHandle->dir.Handle, &info, RTFSOBJATTRADD_NOTHING);
689 if (RT_SUCCESS(rc))
690 {
691 vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
692 }
693 }
694 else
695 {
696 switch (rc)
697 {
698 case VERR_FILE_NOT_FOUND: /* Does this make sense? */
699 pParms->Result = SHFL_FILE_NOT_FOUND;
700 break;
701 case VERR_PATH_NOT_FOUND:
702 pParms->Result = SHFL_PATH_NOT_FOUND;
703 break;
704 case VERR_ACCESS_DENIED:
705 pParms->Result = SHFL_FILE_EXISTS;
706 break;
707 default:
708 pParms->Result = SHFL_NO_RESULT;
709 }
710 }
711 }
712 }
713 if (RT_FAILURE(rc))
714 {
715 if ( (0 != pHandle)
716 && (0 != pHandle->dir.Handle))
717 {
718 RTDirClose(pHandle->dir.Handle);
719 pHandle->dir.Handle = 0;
720 }
721 if (SHFL_HANDLE_NIL != handle)
722 {
723 vbsfFreeFileHandle(pClient, handle);
724 }
725 pParms->Handle = SHFL_HANDLE_NIL;
726 }
727 else
728 {
729 pParms->Handle = handle;
730 }
731 LogFlow(("vbsfOpenDir: rc = %Rrc\n", rc));
732 return rc;
733}
734
735static int vbsfCloseDir(SHFLFILEHANDLE *pHandle)
736{
737 int rc = VINF_SUCCESS;
738
739 LogFlow(("vbsfCloseDir: Handle = %08X Search Handle = %08X\n",
740 pHandle->dir.Handle, pHandle->dir.SearchHandle));
741
742 RTDirClose(pHandle->dir.Handle);
743
744 if (pHandle->dir.SearchHandle)
745 RTDirClose(pHandle->dir.SearchHandle);
746
747 if (pHandle->dir.pLastValidEntry)
748 {
749 RTMemFree(pHandle->dir.pLastValidEntry);
750 pHandle->dir.pLastValidEntry = NULL;
751 }
752
753 LogFlow(("vbsfCloseDir: rc = %d\n", rc));
754
755 return rc;
756}
757
758
759static int vbsfCloseFile(SHFLFILEHANDLE *pHandle)
760{
761 int rc = VINF_SUCCESS;
762
763 LogFlow(("vbsfCloseFile: Handle = %08X\n",
764 pHandle->file.Handle));
765
766 rc = RTFileClose(pHandle->file.Handle);
767
768 LogFlow(("vbsfCloseFile: rc = %d\n", rc));
769
770 return rc;
771}
772
773/**
774 * Look up file or folder information by host path.
775 *
776 * @returns iprt status code (currently VINF_SUCCESS)
777 * @param pClient client data
778 * @param pszPath The path of the file to be looked up
779 * @retval pParms->Result Status of the operation (success or error)
780 * @retval pParms->Info On success, information returned about the file
781 */
782static int vbsfLookupFile(SHFLCLIENTDATA *pClient, char *pszPath, SHFLCREATEPARMS *pParms)
783{
784 RTFSOBJINFO info;
785 int rc;
786
787 rc = RTPathQueryInfoEx(pszPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
788 LogFlow(("SHFL_CF_LOOKUP\n"));
789 /* Client just wants to know if the object exists. */
790 switch (rc)
791 {
792 case VINF_SUCCESS:
793 {
794#ifdef RT_OS_WINDOWS
795 info.Attr.fMode |= 0111;
796#endif
797 vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
798 pParms->Result = SHFL_FILE_EXISTS;
799 break;
800 }
801
802 case VERR_FILE_NOT_FOUND:
803 {
804 pParms->Result = SHFL_FILE_NOT_FOUND;
805 rc = VINF_SUCCESS;
806 break;
807 }
808
809 case VERR_PATH_NOT_FOUND:
810 {
811 pParms->Result = SHFL_PATH_NOT_FOUND;
812 rc = VINF_SUCCESS;
813 break;
814 }
815 }
816 pParms->Handle = SHFL_HANDLE_NIL;
817 return rc;
818}
819
820#ifdef UNITTEST
821/** Unit test the SHFL_FN_CREATE API. Located here as a form of API
822 * documentation. */
823void testCreate(RTTEST hTest)
824{
825 /* Simple opening of an existing file. */
826 testCreateFileSimple(hTest);
827 testCreateFileSimpleCaseInsensitive(hTest);
828 /* Simple opening of an existing directory. */
829 /** @todo How do wildcards in the path name work? */
830 testCreateDirSimple(hTest);
831 /* If the number or types of parameters are wrong the API should fail. */
832 testCreateBadParameters(hTest);
833 /* Add tests as required... */
834}
835#endif
836
837/**
838 * Create or open a file or folder. Perform character set and case
839 * conversion on the file name if necessary.
840 *
841 * @returns IPRT status code, but see note below
842 * @param pClient Data structure describing the client accessing the shared
843 * folder
844 * @param root The index of the shared folder in the table of mappings.
845 * The host path of the shared folder is found using this.
846 * @param pPath The path of the file or folder relative to the host path
847 * indexed by root.
848 * @param cbPath Presumably the length of the path in pPath. Actually
849 * ignored, as pPath contains a length parameter.
850 * @param pParms @a Info If a new file is created or an old one overwritten, set
851 * these attributes
852 * @retval pParms @a Result Shared folder result code, see include/VBox/shflsvc.h
853 * @retval pParms @a Handle Shared folder handle to the newly opened file
854 * @retval pParms @a Info Attributes of the file or folder opened
855 *
856 * @note This function returns success if a "non-exceptional" error occurred,
857 * such as "no such file". In this case, the caller should check the
858 * pParms->Result return value and whether pParms->Handle is valid.
859 */
860int vbsfCreate(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, SHFLCREATEPARMS *pParms)
861{
862 int rc = VINF_SUCCESS;
863
864 LogFlow(("vbsfCreate: pClient = %p, pPath = %p, cbPath = %d, pParms = %p CreateFlags=%x\n",
865 pClient, pPath, cbPath, pParms, pParms->CreateFlags));
866
867 /* Check the client access rights to the root. */
868 /** @todo */
869
870 /* Build a host full path for the given path, handle file name case issues (if the guest
871 * expects case-insensitive paths but the host is case-sensitive) and convert ucs2 to utf8 if
872 * necessary.
873 */
874 char *pszFullPath = NULL;
875 uint32_t cbFullPathRoot = 0;
876
877 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot);
878 if (RT_SUCCESS(rc))
879 {
880 /* Reset return value in case client forgot to do so.
881 * pParms->Handle must not be reset here, as it is used
882 * in vbsfOpenFile to detect old additions.
883 */
884 pParms->Result = SHFL_NO_RESULT;
885
886 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_LOOKUP))
887 {
888 rc = vbsfLookupFile(pClient, pszFullPath, pParms);
889 }
890 else
891 {
892 /* Query path information. */
893 RTFSOBJINFO info;
894
895 rc = RTPathQueryInfoEx(pszFullPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
896 LogFlow(("RTPathQueryInfoEx returned %Rrc\n", rc));
897
898 if (RT_SUCCESS(rc))
899 {
900 /* Mark it as a directory in case the caller didn't. */
901 /**
902 * @todo I left this in in order not to change the behaviour of the
903 * function too much. Is it really needed, and should it really be
904 * here?
905 */
906 if (BIT_FLAG(info.Attr.fMode, RTFS_DOS_DIRECTORY))
907 {
908 pParms->CreateFlags |= SHFL_CF_DIRECTORY;
909 }
910
911 /**
912 * @todo This should be in the Windows Guest Additions, as no-one else
913 * needs it.
914 */
915 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_OPEN_TARGET_DIRECTORY))
916 {
917 vbsfStripLastComponent(pszFullPath, cbFullPathRoot);
918 pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_EXISTS;
919 pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_NEW;
920 pParms->CreateFlags |= SHFL_CF_DIRECTORY;
921 pParms->CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS;
922 pParms->CreateFlags |= SHFL_CF_ACT_FAIL_IF_NEW;
923 }
924 }
925
926 rc = VINF_SUCCESS;
927
928 /* Note: do not check the SHFL_CF_ACCESS_WRITE here, only check if the open operation
929 * will cause changes.
930 *
931 * Actual operations (write, set attr, etc), which can write to a shared folder, have
932 * the check and will return VERR_WRITE_PROTECT if the folder is not writable.
933 */
934 if ( (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_REPLACE_IF_EXISTS
935 || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_OVERWRITE_IF_EXISTS
936 || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_NEW) == SHFL_CF_ACT_CREATE_IF_NEW
937 )
938 {
939 /* is the guest allowed to write to this share? */
940 bool fWritable;
941 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
942 if (RT_FAILURE(rc) || !fWritable)
943 rc = VERR_WRITE_PROTECT;
944 }
945
946 if (RT_SUCCESS(rc))
947 {
948 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_DIRECTORY))
949 {
950 rc = vbsfOpenDir(pClient, root, pszFullPath, pParms);
951 }
952 else
953 {
954 rc = vbsfOpenFile(pClient, root, pszFullPath, pParms);
955 }
956 }
957 else
958 {
959 pParms->Handle = SHFL_HANDLE_NIL;
960 }
961 }
962
963 /* free the path string */
964 vbsfFreeFullPath(pszFullPath);
965 }
966
967 Log(("vbsfCreate: handle = %RX64 rc = %Rrc result=%x\n", (uint64_t)pParms->Handle, rc, pParms->Result));
968
969 return rc;
970}
971
972#ifdef UNITTEST
973/** Unit test the SHFL_FN_CLOSE API. Located here as a form of API
974 * documentation. */
975void testClose(RTTEST hTest)
976{
977 /* If the API parameters are invalid the API should fail. */
978 testCloseBadParameters(hTest);
979 /* Add tests as required... */
980}
981#endif
982
983int vbsfClose(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
984{
985 LogFunc(("pClient = %p, root 0x%RX32, Handle = 0x%RX64\n",
986 pClient, root, Handle));
987
988 int rc = VERR_INVALID_HANDLE;
989 uint32_t type = vbsfQueryHandleType(pClient, Handle);
990 Assert((type & ~(SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE)) == 0);
991 switch (type & (SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE))
992 {
993 case SHFL_HF_TYPE_DIR:
994 {
995 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
996 if (RT_LIKELY(pHandle && root == pHandle->root))
997 {
998 rc = vbsfCloseDir(pHandle);
999 vbsfFreeFileHandle(pClient, Handle);
1000 }
1001 break;
1002 }
1003 case SHFL_HF_TYPE_FILE:
1004 {
1005 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1006 if (RT_LIKELY(pHandle && root == pHandle->root))
1007 {
1008 rc = vbsfCloseFile(pHandle);
1009 vbsfFreeFileHandle(pClient, Handle);
1010 }
1011 break;
1012 }
1013 default:
1014 break;
1015 }
1016
1017 LogFunc(("rc = %Rrc\n", rc));
1018 return rc;
1019}
1020
1021/**
1022 * Helper for vbsfReadPages and vbsfWritePages that creates a S/G buffer from a
1023 * pages parameter.
1024 */
1025static int vbsfPagesToSgBuf(VBOXHGCMSVCPARMPAGES const *pPages, uint32_t cbLeft, PRTSGBUF pSgBuf)
1026{
1027 PRTSGSEG paSegs = (PRTSGSEG)RTMemTmpAlloc(sizeof(paSegs[0]) * pPages->cPages);
1028 if (paSegs)
1029 {
1030 /*
1031 * Convert the pages to segments.
1032 */
1033 uint32_t iSeg = 0;
1034 uint32_t iPage = 0;
1035 for (;;)
1036 {
1037 Assert(iSeg < pPages->cPages);
1038 Assert(iPage < pPages->cPages);
1039
1040 /* Current page. */
1041 void *pvSeg;
1042 paSegs[iSeg].pvSeg = pvSeg = pPages->papvPages[iPage];
1043 uint32_t cbSeg = PAGE_SIZE - (uint32_t)((uintptr_t)pvSeg & PAGE_OFFSET_MASK);
1044 iPage++;
1045
1046 /* Adjacent to the next page? */
1047 while ( iPage < pPages->cPages
1048 && (uintptr_t)pvSeg + cbSeg == (uintptr_t)pPages->papvPages[iPage])
1049 {
1050 iPage++;
1051 cbSeg += PAGE_SIZE;
1052 }
1053
1054 /* Adjust for max size. */
1055 if (cbLeft <= cbSeg)
1056 {
1057 paSegs[iSeg++].cbSeg = cbLeft;
1058 break;
1059 }
1060 paSegs[iSeg++].cbSeg = cbSeg;
1061 cbLeft -= cbSeg;
1062 }
1063
1064 /*
1065 * Initialize the s/g buffer and execute the read.
1066 */
1067 RTSgBufInit(pSgBuf, paSegs, iSeg);
1068 return VINF_SUCCESS;
1069 }
1070 pSgBuf->paSegs = NULL;
1071 return VERR_NO_TMP_MEMORY;
1072}
1073
1074
1075#ifdef UNITTEST
1076/** Unit test the SHFL_FN_READ API. Located here as a form of API
1077 * documentation. */
1078void testRead(RTTEST hTest)
1079{
1080 /* If the number or types of parameters are wrong the API should fail. */
1081 testReadBadParameters(hTest);
1082 /* Basic reading from a file. */
1083 testReadFileSimple(hTest);
1084 /* Add tests as required... */
1085}
1086#endif
1087int vbsfRead(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer)
1088{
1089 LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64, offset 0x%RX64, bytes 0x%RX32\n",
1090 pClient, root, Handle, offset, pcbBuffer? *pcbBuffer: 0));
1091
1092 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1093
1094 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1095 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1096 if (RT_SUCCESS(rc))
1097 {
1098 size_t const cbToRead = *pcbBuffer;
1099 if (RT_LIKELY(cbToRead > 0))
1100 {
1101 size_t cbActual = 0;
1102 rc = RTFileReadAt(pHandle->file.Handle, offset, pBuffer, cbToRead, &cbActual);
1103 *pcbBuffer = (uint32_t)cbActual;
1104 }
1105 else
1106 {
1107 /* Reading zero bytes always succeeds. */
1108 rc = VINF_SUCCESS;
1109 }
1110 }
1111 else
1112 *pcbBuffer = 0;
1113
1114 LogFunc(("%Rrc bytes read 0x%RX32\n", rc, *pcbBuffer));
1115 return rc;
1116}
1117
1118/**
1119 * SHFL_FN_READ w/o bounce buffering.
1120 */
1121int vbsfReadPages(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hFile, uint64_t offFile,
1122 uint32_t *pcbRead, PVBOXHGCMSVCPARMPAGES pPages)
1123{
1124 LogFunc(("pClient %p, idRoot %#RX32, hFile %#RX64, offFile %#RX64, cbRead %#RX32, cPages %#x\n",
1125 pClient, idRoot, hFile, offFile, *pcbRead, pPages->cPages));
1126
1127 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1128
1129 size_t cbTotal = 0;
1130 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, hFile);
1131 int rc = vbsfCheckHandleAccess(pClient, idRoot, pHandle, VBSF_CHECK_ACCESS_READ);
1132 if (RT_SUCCESS(rc))
1133 {
1134 uint32_t const cbToRead = *pcbRead;
1135 if (cbToRead > 0)
1136 {
1137 ASSERT_GUEST_RETURN(pPages->cPages > 0, VERR_INTERNAL_ERROR_3);
1138
1139 /*
1140 * Convert to a scatter-gather buffer.
1141 *
1142 * We need not do any platform specific code here as the RTSGBUF
1143 * segment array maps directly onto the posix iovec structure.
1144 * Windows does currently benefit much from this conversion, but
1145 * so be it.
1146 */
1147 RTSGBUF SgBuf;
1148 rc = vbsfPagesToSgBuf(pPages, cbToRead, &SgBuf);
1149 if (RT_SUCCESS(rc))
1150 {
1151 rc = RTFileSgReadAt(pHandle->file.Handle, offFile, &SgBuf, cbToRead, &cbTotal);
1152 while (rc == VERR_INTERRUPTED)
1153 {
1154 RTSgBufReset(&SgBuf);
1155 rc = RTFileSgReadAt(pHandle->file.Handle, offFile, &SgBuf, cbToRead, &cbTotal);
1156 }
1157
1158 RTMemTmpFree((void *)SgBuf.paSegs);
1159 }
1160 else
1161 rc = VERR_NO_TMP_MEMORY;
1162
1163 *pcbRead = (uint32_t)cbTotal;
1164 }
1165 else
1166 {
1167 /* Reading zero bytes always succeeds. */
1168 rc = VINF_SUCCESS;
1169 }
1170 }
1171 else
1172 *pcbRead = 0;
1173
1174 LogFunc(("%Rrc bytes read %#zx\n", rc, cbTotal));
1175 return rc;
1176}
1177
1178/**
1179 * Helps with writes to RTFILE_O_APPEND files.
1180 */
1181static uint64_t vbsfWriteCalcPostAppendFilePosition(RTFILE hFile, uint64_t offGuessed)
1182{
1183 RTFSOBJINFO ObjInfo;
1184 int rc2 = RTFileQueryInfo(hFile, &ObjInfo, RTFSOBJATTRADD_NOTHING);
1185 if (RT_SUCCESS(rc2) && (uint64_t)ObjInfo.cbObject >= offGuessed)
1186 return ObjInfo.cbObject;
1187 return offGuessed;
1188}
1189
1190#ifdef UNITTEST
1191/** Unit test the SHFL_FN_WRITE API. Located here as a form of API
1192 * documentation. */
1193void testWrite(RTTEST hTest)
1194{
1195 /* If the number or types of parameters are wrong the API should fail. */
1196 testWriteBadParameters(hTest);
1197 /* Simple test of writing to a file. */
1198 testWriteFileSimple(hTest);
1199 /* Add tests as required... */
1200}
1201#endif
1202int vbsfWrite(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hFile, uint64_t *poffFile,
1203 uint32_t *pcbBuffer, uint8_t *pBuffer)
1204{
1205 uint64_t offFile = *poffFile;
1206 LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64, offFile 0x%RX64, bytes 0x%RX32\n",
1207 pClient, idRoot, hFile, offFile, *pcbBuffer));
1208
1209 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1210
1211 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, hFile);
1212 int rc = vbsfCheckHandleAccess(pClient, idRoot, pHandle, VBSF_CHECK_ACCESS_WRITE);
1213 if (RT_SUCCESS(rc))
1214 {
1215 size_t const cbToWrite = *pcbBuffer;
1216 if (RT_LIKELY(cbToWrite != 0))
1217 {
1218 size_t cbWritten = 0;
1219 if (!(pHandle->file.fOpenFlags & RTFILE_O_APPEND))
1220 rc = RTFileWriteAt(pHandle->file.Handle, offFile, pBuffer, cbToWrite, &cbWritten);
1221 else
1222 {
1223 rc = RTFileSeek(pHandle->file.Handle, offFile, RTFILE_SEEK_BEGIN, NULL);
1224 AssertRC(rc);
1225 if (RT_SUCCESS(rc))
1226 {
1227 rc = RTFileWrite(pHandle->file.Handle, pBuffer, cbToWrite, &cbWritten);
1228 *pcbBuffer = (uint32_t)cbWritten;
1229 }
1230 }
1231
1232 /* Update the file offset (mainly for RTFILE_O_APPEND), */
1233 if (RT_SUCCESS(rc))
1234 {
1235 offFile += cbWritten;
1236 if (!(pHandle->file.fOpenFlags & RTFILE_O_APPEND))
1237 *poffFile = offFile;
1238 else
1239 *poffFile = vbsfWriteCalcPostAppendFilePosition(pHandle->file.Handle, offFile);
1240 }
1241 }
1242 else
1243 {
1244 /** @todo What writing zero bytes should do? */
1245 rc = VINF_SUCCESS;
1246 }
1247 }
1248 else
1249 *pcbBuffer = 0;
1250 LogFunc(("%Rrc bytes written 0x%RX32\n", rc, *pcbBuffer));
1251 return rc;
1252}
1253
1254/**
1255 * SHFL_FN_WRITE w/o bounce buffering.
1256 */
1257int vbsfWritePages(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hFile, uint64_t *poffFile,
1258 uint32_t *pcbWrite, PVBOXHGCMSVCPARMPAGES pPages)
1259{
1260 uint64_t offFile = *poffFile;
1261 LogFunc(("pClient %p, idRoot %#RX32, hFile %#RX64, offFile %#RX64, cbWrite %#RX32, cPages %#x\n",
1262 pClient, idRoot, hFile, offFile, *pcbWrite, pPages->cPages));
1263
1264 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1265
1266 size_t cbTotal = 0;
1267 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, hFile);
1268 int rc = vbsfCheckHandleAccess(pClient, idRoot, pHandle, VBSF_CHECK_ACCESS_WRITE);
1269 if (RT_SUCCESS(rc))
1270 {
1271 uint32_t const cbToWrite = *pcbWrite;
1272 if (cbToWrite > 0)
1273 {
1274 ASSERT_GUEST_RETURN(pPages->cPages > 0, VERR_INTERNAL_ERROR_3);
1275
1276 /*
1277 * Convert to a scatter-gather buffer.
1278 *
1279 * We need not do any platform specific code here as the RTSGBUF
1280 * segment array maps directly onto the posix iovec structure.
1281 * Windows does currently benefit much from this conversion, but
1282 * so be it.
1283 */
1284 RTSGBUF SgBuf;
1285 rc = vbsfPagesToSgBuf(pPages, cbToWrite, &SgBuf);
1286 if (RT_SUCCESS(rc))
1287 {
1288#ifndef RT_OS_LINUX
1289 /* Cannot use RTFileSgWriteAt or RTFileWriteAt when opened with
1290 RTFILE_O_APPEND, except for on linux where the offset is
1291 then ignored by the low level kernel API. */
1292 if (pHandle->file.fOpenFlags & RTFILE_O_APPEND)
1293 {
1294 /* paranoia */
1295 RTFileSeek(pHandle->file.Handle, 0, RTFILE_SEEK_END, NULL);
1296
1297 for (size_t iSeg = 0; iSeg < SgBuf.cSegs; iSeg++)
1298 {
1299 size_t cbWrittenNow = 0;
1300 do
1301 rc = RTFileWrite(pHandle->file.Handle, SgBuf.paSegs[iSeg].pvSeg,
1302 SgBuf.paSegs[iSeg].cbSeg, &cbWrittenNow);
1303 while (rc == VERR_INTERRUPTED);
1304 if (RT_SUCCESS(rc))
1305 {
1306 cbTotal += cbWrittenNow;
1307 if (cbWrittenNow < SgBuf.paSegs[iSeg].cbSeg)
1308 break;
1309 }
1310 else
1311 {
1312 if (cbTotal > 0)
1313 rc = VINF_SUCCESS;
1314 break;
1315 }
1316 }
1317 }
1318 else
1319#endif
1320 {
1321 rc = RTFileSgWriteAt(pHandle->file.Handle, offFile, &SgBuf, cbToWrite, &cbTotal);
1322 while (rc == VERR_INTERRUPTED)
1323 {
1324 RTSgBufReset(&SgBuf);
1325 rc = RTFileSgWriteAt(pHandle->file.Handle, offFile, &SgBuf, cbToWrite, &cbTotal);
1326 }
1327 }
1328
1329 RTMemTmpFree((void *)SgBuf.paSegs);
1330
1331 /* Update the file offset (mainly for RTFILE_O_APPEND), */
1332 if (RT_SUCCESS(rc))
1333 {
1334 offFile += cbTotal;
1335 if (!(pHandle->file.fOpenFlags & RTFILE_O_APPEND))
1336 *poffFile = offFile;
1337 else
1338 *poffFile = vbsfWriteCalcPostAppendFilePosition(pHandle->file.Handle, offFile);
1339 }
1340 }
1341 else
1342 rc = VERR_NO_TMP_MEMORY;
1343
1344 *pcbWrite = (uint32_t)cbTotal;
1345 }
1346 else
1347 {
1348 /* Writing zero bytes always succeeds. */
1349 rc = VINF_SUCCESS;
1350 }
1351 }
1352 else
1353 *pcbWrite = 0;
1354
1355 LogFunc(("%Rrc bytes written %#zx\n", rc, cbTotal));
1356 return rc;
1357}
1358
1359
1360#ifdef UNITTEST
1361/** Unit test the SHFL_FN_FLUSH API. Located here as a form of API
1362 * documentation. */
1363void testFlush(RTTEST hTest)
1364{
1365 /* If the number or types of parameters are wrong the API should fail. */
1366 testFlushBadParameters(hTest);
1367 /* Simple opening and flushing of a file. */
1368 testFlushFileSimple(hTest);
1369 /* Add tests as required... */
1370}
1371#endif
1372
1373int vbsfFlush(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
1374{
1375 LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64\n",
1376 pClient, root, Handle));
1377
1378 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1379
1380 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1381 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1382 if (RT_SUCCESS(rc))
1383 { /* likely */ }
1384 else
1385 return rc;
1386
1387 rc = RTFileFlush(pHandle->file.Handle);
1388
1389 LogFunc(("%Rrc\n", rc));
1390 return rc;
1391}
1392
1393#ifdef UNITTEST
1394/** Unit test the SHFL_FN_LIST API. Located here as a form of API
1395 * documentation. */
1396void testDirList(RTTEST hTest)
1397{
1398 /* If the number or types of parameters are wrong the API should fail. */
1399 testDirListBadParameters(hTest);
1400 /* Test listing an empty directory (simple edge case). */
1401 testDirListEmpty(hTest);
1402 /* Add tests as required... */
1403}
1404#endif
1405int vbsfDirList(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, SHFLSTRING *pPath, uint32_t flags,
1406 uint32_t *pcbBuffer, uint8_t *pBuffer, uint32_t *pIndex, uint32_t *pcFiles)
1407{
1408 PRTDIRENTRYEX pDirEntry = 0, pDirEntryOrg;
1409 uint32_t cbDirEntry, cbBufferOrg;
1410 PSHFLDIRINFO pSFDEntry;
1411 PRTUTF16 pwszString;
1412 RTDIR hDir;
1413 const bool fUtf8 = BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8) != 0;
1414
1415 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1416
1417 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
1418 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1419 if (RT_SUCCESS(rc))
1420 { /* likely */ }
1421 else
1422 return rc;
1423
1424 Assert(*pIndex == 0);
1425
1426 cbDirEntry = 4096;
1427 pDirEntryOrg = pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry);
1428 if (pDirEntry == 0)
1429 {
1430 AssertFailed();
1431 return VERR_NO_MEMORY;
1432 }
1433
1434 cbBufferOrg = *pcbBuffer;
1435 *pcbBuffer = 0;
1436 pSFDEntry = (PSHFLDIRINFO)pBuffer;
1437
1438 *pIndex = 1; /* not yet complete */
1439 *pcFiles = 0;
1440
1441 if (!pPath)
1442 hDir = pHandle->dir.Handle;
1443 else
1444 {
1445 if (pHandle->dir.SearchHandle == 0)
1446 {
1447 /* Build a host full path for the given path
1448 * and convert ucs2 to utf8 if necessary.
1449 */
1450 char *pszFullPath = NULL;
1451
1452 Assert(pHandle->dir.pLastValidEntry == 0);
1453
1454 rc = vbsfBuildFullPath(pClient, root, pPath, pPath->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPath, NULL, true);
1455
1456 if (RT_SUCCESS(rc))
1457 {
1458 rc = RTDirOpenFiltered(&pHandle->dir.SearchHandle, pszFullPath, RTDIRFILTER_WINNT, 0 /*fFlags*/);
1459
1460 /* free the path string */
1461 vbsfFreeFullPath(pszFullPath);
1462
1463 if (RT_FAILURE(rc))
1464 goto end;
1465 }
1466 else
1467 goto end;
1468 flags &= ~SHFL_LIST_RESTART;
1469 }
1470 Assert(pHandle->dir.SearchHandle);
1471 hDir = pHandle->dir.SearchHandle;
1472 }
1473
1474 if (flags & SHFL_LIST_RESTART)
1475 {
1476 rc = RTDirRewind(hDir);
1477 if (RT_FAILURE(rc))
1478 goto end;
1479 }
1480
1481 while (cbBufferOrg)
1482 {
1483 size_t cbDirEntrySize = cbDirEntry;
1484 uint32_t cbNeeded;
1485
1486 /* Do we still have a valid last entry for the active search? If so, then return it here */
1487 if (pHandle->dir.pLastValidEntry)
1488 {
1489 pDirEntry = pHandle->dir.pLastValidEntry;
1490 }
1491 else
1492 {
1493 pDirEntry = pDirEntryOrg;
1494
1495 rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
1496 if (rc == VERR_NO_MORE_FILES)
1497 {
1498 *pIndex = 0; /* listing completed */
1499 break;
1500 }
1501
1502 if ( rc != VINF_SUCCESS
1503 && rc != VWRN_NO_DIRENT_INFO)
1504 {
1505 //AssertFailed();
1506 if ( rc == VERR_NO_TRANSLATION
1507 || rc == VERR_INVALID_UTF8_ENCODING)
1508 continue;
1509 break;
1510 }
1511 }
1512
1513 cbNeeded = RT_OFFSETOF(SHFLDIRINFO, name.String);
1514 if (fUtf8)
1515 cbNeeded += pDirEntry->cbName + 1;
1516 else
1517 /* Overestimating, but that's ok */
1518 cbNeeded += (pDirEntry->cbName + 1) * 2;
1519
1520 if (cbBufferOrg < cbNeeded)
1521 {
1522 /* No room, so save this directory entry, or else it's lost forever */
1523 pHandle->dir.pLastValidEntry = pDirEntry;
1524
1525 if (*pcFiles == 0)
1526 {
1527 AssertFailed();
1528 return VINF_BUFFER_OVERFLOW; /* Return directly and don't free pDirEntry */
1529 }
1530 return VINF_SUCCESS; /* Return directly and don't free pDirEntry */
1531 }
1532
1533#ifdef RT_OS_WINDOWS
1534 pDirEntry->Info.Attr.fMode |= 0111;
1535#endif
1536 vbfsCopyFsObjInfoFromIprt(&pSFDEntry->Info, &pDirEntry->Info);
1537 pSFDEntry->cucShortName = 0;
1538
1539 if (fUtf8)
1540 {
1541 void *src, *dst;
1542
1543 src = &pDirEntry->szName[0];
1544 dst = &pSFDEntry->name.String.utf8[0];
1545
1546 memcpy(dst, src, pDirEntry->cbName + 1);
1547
1548 pSFDEntry->name.u16Size = pDirEntry->cbName + 1;
1549 pSFDEntry->name.u16Length = pDirEntry->cbName;
1550 }
1551 else
1552 {
1553 pSFDEntry->name.String.ucs2[0] = 0;
1554 pwszString = pSFDEntry->name.String.ucs2;
1555 int rc2 = RTStrToUtf16Ex(pDirEntry->szName, RTSTR_MAX, &pwszString, pDirEntry->cbName+1, NULL);
1556 AssertRC(rc2);
1557
1558#ifdef RT_OS_DARWIN
1559/** @todo This belongs in rtPathToNative or in the windows shared folder file system driver...
1560 * The question is simply whether the NFD normalization is actually applied on a (virtual) file
1561 * system level in darwin, or just by the user mode application libs. */
1562 {
1563 // Convert to
1564 // Normalization Form C (composed Unicode). We need this because
1565 // Mac OS X file system uses NFD (Normalization Form D :decomposed Unicode)
1566 // while most other OS', server-side programs usually expect NFC.
1567 uint16_t ucs2Length;
1568 CFRange rangeCharacters;
1569 CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, 0);
1570
1571 ::CFStringAppendCharacters(inStr, (UniChar *)pwszString, RTUtf16Len(pwszString));
1572 ::CFStringNormalize(inStr, kCFStringNormalizationFormC);
1573 ucs2Length = ::CFStringGetLength(inStr);
1574
1575 rangeCharacters.location = 0;
1576 rangeCharacters.length = ucs2Length;
1577 ::CFStringGetCharacters(inStr, rangeCharacters, pwszString);
1578 pwszString[ucs2Length] = 0x0000; // NULL terminated
1579
1580 CFRelease(inStr);
1581 }
1582#endif
1583 pSFDEntry->name.u16Length = (uint32_t)RTUtf16Len(pSFDEntry->name.String.ucs2) * 2;
1584 pSFDEntry->name.u16Size = pSFDEntry->name.u16Length + 2;
1585
1586 Log(("SHFL: File name size %d\n", pSFDEntry->name.u16Size));
1587 Log(("SHFL: File name %ls\n", &pSFDEntry->name.String.ucs2));
1588
1589 // adjust cbNeeded (it was overestimated before)
1590 cbNeeded = RT_OFFSETOF(SHFLDIRINFO, name.String) + pSFDEntry->name.u16Size;
1591 }
1592
1593 pSFDEntry = (PSHFLDIRINFO)((uintptr_t)pSFDEntry + cbNeeded);
1594 *pcbBuffer += cbNeeded;
1595 cbBufferOrg-= cbNeeded;
1596
1597 *pcFiles += 1;
1598
1599 /* Free the saved last entry, that we've just returned */
1600 if (pHandle->dir.pLastValidEntry)
1601 {
1602 RTMemFree(pHandle->dir.pLastValidEntry);
1603 pHandle->dir.pLastValidEntry = NULL;
1604
1605 /* And use the newly allocated buffer from now. */
1606 pDirEntry = pDirEntryOrg;
1607 }
1608
1609 if (flags & SHFL_LIST_RETURN_ONE)
1610 break; /* we're done */
1611 }
1612 Assert(rc != VINF_SUCCESS || *pcbBuffer > 0);
1613
1614end:
1615 if (pDirEntry)
1616 RTMemFree(pDirEntry);
1617
1618 return rc;
1619}
1620
1621#ifdef UNITTEST
1622/** Unit test the SHFL_FN_READLINK API. Located here as a form of API
1623 * documentation. */
1624void testReadLink(RTTEST hTest)
1625{
1626 /* If the number or types of parameters are wrong the API should fail. */
1627 testReadLinkBadParameters(hTest);
1628 /* Add tests as required... */
1629}
1630#endif
1631int vbsfReadLink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint8_t *pBuffer, uint32_t cbBuffer)
1632{
1633 int rc = VINF_SUCCESS;
1634
1635 if (pPath == 0 || pBuffer == 0)
1636 {
1637 AssertFailed();
1638 return VERR_INVALID_PARAMETER;
1639 }
1640
1641 /* Build a host full path for the given path, handle file name case issues
1642 * (if the guest expects case-insensitive paths but the host is
1643 * case-sensitive) and convert ucs2 to utf8 if necessary.
1644 */
1645 char *pszFullPath = NULL;
1646 uint32_t cbFullPathRoot = 0;
1647
1648 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot);
1649
1650 if (RT_SUCCESS(rc))
1651 {
1652 rc = RTSymlinkRead(pszFullPath, (char *) pBuffer, cbBuffer, 0);
1653 if (RT_SUCCESS(rc))
1654 {
1655 /* Convert the slashes in the link target to the guest path separator characters. */
1656 char *psz = (char *)pBuffer;
1657 while (*psz != '\0')
1658 {
1659 if (*psz == RTPATH_DELIMITER)
1660 *psz = pClient->PathDelimiter;
1661 psz++;
1662 }
1663 }
1664
1665 /* free the path string */
1666 vbsfFreeFullPath(pszFullPath);
1667 }
1668
1669 return rc;
1670}
1671
1672int vbsfQueryFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
1673 uint32_t *pcbBuffer, uint8_t *pBuffer)
1674{
1675 RT_NOREF1(flags);
1676 uint32_t type = vbsfQueryHandleType(pClient, Handle);
1677 int rc = VINF_SUCCESS;
1678 SHFLFSOBJINFO *pObjInfo = (SHFLFSOBJINFO *)pBuffer;
1679 RTFSOBJINFO fileinfo;
1680
1681
1682 AssertReturn(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE, VERR_INVALID_PARAMETER);
1683 AssertReturn(pcbBuffer != NULL, VERR_INVALID_PARAMETER);
1684 AssertReturn(pObjInfo != NULL, VERR_INVALID_PARAMETER);
1685 AssertReturn(*pcbBuffer >= sizeof(SHFLFSOBJINFO), VERR_INVALID_PARAMETER);
1686
1687 /** @todo other options */
1688 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_FILE));
1689
1690 *pcbBuffer = 0;
1691
1692 if (type == SHFL_HF_TYPE_DIR)
1693 {
1694 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
1695 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1696 if (RT_SUCCESS(rc))
1697 rc = RTDirQueryInfo(pHandle->dir.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1698 }
1699 else
1700 {
1701 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1702 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1703 if (RT_SUCCESS(rc))
1704 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1705#ifdef RT_OS_WINDOWS
1706 if (RT_SUCCESS(rc) && RTFS_IS_FILE(pObjInfo->Attr.fMode))
1707 pObjInfo->Attr.fMode |= 0111;
1708#endif
1709 }
1710 if (rc == VINF_SUCCESS)
1711 {
1712 vbfsCopyFsObjInfoFromIprt(pObjInfo, &fileinfo);
1713 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1714 }
1715 else
1716 AssertFailed();
1717
1718 return rc;
1719}
1720
1721static int vbsfSetFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
1722 uint32_t *pcbBuffer, uint8_t *pBuffer)
1723{
1724 RT_NOREF1(flags);
1725 uint32_t type = vbsfQueryHandleType(pClient, Handle);
1726 int rc = VINF_SUCCESS;
1727 SHFLFSOBJINFO *pSFDEntry;
1728
1729 if ( !(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE)
1730 || pcbBuffer == 0
1731 || pBuffer == 0
1732 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
1733 {
1734 AssertFailed();
1735 return VERR_INVALID_PARAMETER;
1736 }
1737
1738 *pcbBuffer = 0;
1739 pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
1740
1741 Assert(flags == (SHFL_INFO_SET | SHFL_INFO_FILE));
1742
1743 /* Change only the time values that are not zero */
1744 if (type == SHFL_HF_TYPE_DIR)
1745 {
1746 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
1747 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1748 if (RT_SUCCESS(rc))
1749 rc = RTDirSetTimes(pHandle->dir.Handle,
1750 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1751 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1752 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1753 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1754 );
1755 }
1756 else
1757 {
1758 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1759 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1760 if (RT_SUCCESS(rc))
1761 rc = RTFileSetTimes(pHandle->file.Handle,
1762 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1763 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1764 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1765 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1766 );
1767 }
1768 if (rc != VINF_SUCCESS)
1769 {
1770 Log(("RTFileSetTimes failed with %Rrc\n", rc));
1771 Log(("AccessTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->AccessTime)));
1772 Log(("ModificationTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ModificationTime)));
1773 Log(("ChangeTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ChangeTime)));
1774 Log(("BirthTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->BirthTime)));
1775 /* temporary hack */
1776 rc = VINF_SUCCESS;
1777 }
1778
1779 if (type == SHFL_HF_TYPE_FILE)
1780 {
1781 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1782 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1783 if (RT_SUCCESS(rc))
1784 {
1785 /* Change file attributes if necessary */
1786 if (pSFDEntry->Attr.fMode)
1787 {
1788 RTFMODE fMode = pSFDEntry->Attr.fMode;
1789
1790#ifndef RT_OS_WINDOWS
1791 /* Don't allow the guest to clear the own bit, otherwise the guest wouldn't be
1792 * able to access this file anymore. Only for guests, which set the UNIX mode.
1793 * Also, clear bits which we don't pass through for security reasons. */
1794 if (fMode & RTFS_UNIX_MASK)
1795 {
1796 fMode |= RTFS_UNIX_IRUSR;
1797 fMode &= ~(RTFS_UNIX_ISUID | RTFS_UNIX_ISGID | RTFS_UNIX_ISTXT);
1798 }
1799#endif
1800
1801 rc = RTFileSetMode(pHandle->file.Handle, fMode);
1802 if (rc != VINF_SUCCESS)
1803 {
1804 Log(("RTFileSetMode %x failed with %Rrc\n", fMode, rc));
1805 /* silent failure, because this tends to fail with e.g. windows guest & linux host */
1806 rc = VINF_SUCCESS;
1807 }
1808 }
1809 }
1810 }
1811 /** @todo mode for directories */
1812
1813 if (rc == VINF_SUCCESS)
1814 {
1815 uint32_t bufsize = sizeof(*pSFDEntry);
1816
1817 rc = vbsfQueryFileInfo(pClient, root, Handle, SHFL_INFO_GET|SHFL_INFO_FILE, &bufsize, (uint8_t *)pSFDEntry);
1818 if (rc == VINF_SUCCESS)
1819 {
1820 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1821 }
1822 else
1823 AssertFailed();
1824 }
1825
1826 return rc;
1827}
1828
1829
1830/**
1831 * Handles SHFL_FN_SET_FILE_SIZE.
1832 */
1833int vbsfSetFileSize(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hHandle, uint64_t cbNewSize)
1834{
1835 /*
1836 * Resolve handle and validate write access.
1837 */
1838 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, hHandle);
1839 ASSERT_GUEST_RETURN(pHandle, VERR_INVALID_HANDLE);
1840
1841 int rc = vbsfCheckHandleAccess(pClient, idRoot, pHandle, VBSF_CHECK_ACCESS_WRITE);
1842 if (RT_SUCCESS(rc))
1843 {
1844 /*
1845 * Execute the request.
1846 */
1847 rc = RTFileSetSize(pHandle->file.Handle, cbNewSize);
1848 }
1849 return rc;
1850}
1851
1852
1853static int vbsfSetEndOfFile(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
1854 uint32_t *pcbBuffer, uint8_t *pBuffer)
1855{
1856 RT_NOREF1(flags);
1857 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1858 SHFLFSOBJINFO *pSFDEntry;
1859
1860 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
1861 {
1862 AssertFailed();
1863 return VERR_INVALID_PARAMETER;
1864 }
1865
1866 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1867 if (RT_SUCCESS(rc))
1868 { /* likely */ }
1869 else
1870 return rc;
1871
1872 *pcbBuffer = 0;
1873 pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
1874
1875 if (flags & SHFL_INFO_SIZE)
1876 {
1877 rc = RTFileSetSize(pHandle->file.Handle, pSFDEntry->cbObject);
1878 if (rc != VINF_SUCCESS)
1879 AssertFailed();
1880 }
1881 else
1882 AssertFailed();
1883
1884 if (rc == VINF_SUCCESS)
1885 {
1886 RTFSOBJINFO fileinfo;
1887
1888 /* Query the new object info and return it */
1889 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1890 if (rc == VINF_SUCCESS)
1891 {
1892#ifdef RT_OS_WINDOWS
1893 fileinfo.Attr.fMode |= 0111;
1894#endif
1895 vbfsCopyFsObjInfoFromIprt(pSFDEntry, &fileinfo);
1896 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1897 }
1898 else
1899 AssertFailed();
1900 }
1901
1902 return rc;
1903}
1904
1905int vbsfQueryVolumeInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1906{
1907 RT_NOREF2(root, flags);
1908 int rc = VINF_SUCCESS;
1909 SHFLVOLINFO *pSFDEntry;
1910 char *pszFullPath = NULL;
1911 union
1912 {
1913 SHFLSTRING Dummy;
1914 uint8_t abDummy[SHFLSTRING_HEADER_SIZE + sizeof(RTUTF16)];
1915 } Buf;
1916
1917 if (pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLVOLINFO))
1918 {
1919 AssertFailed();
1920 return VERR_INVALID_PARAMETER;
1921 }
1922
1923 /** @todo other options */
1924 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_VOLUME));
1925
1926 *pcbBuffer = 0;
1927 pSFDEntry = (PSHFLVOLINFO)pBuffer;
1928
1929 ShflStringInitBuffer(&Buf.Dummy, sizeof(Buf));
1930 Buf.Dummy.String.ucs2[0] = '\0';
1931 rc = vbsfBuildFullPath(pClient, root, &Buf.Dummy, sizeof(Buf), &pszFullPath, NULL);
1932
1933 if (RT_SUCCESS(rc))
1934 {
1935 rc = RTFsQuerySizes(pszFullPath, &pSFDEntry->ullTotalAllocationBytes, &pSFDEntry->ullAvailableAllocationBytes, &pSFDEntry->ulBytesPerAllocationUnit, &pSFDEntry->ulBytesPerSector);
1936 if (rc != VINF_SUCCESS)
1937 goto exit;
1938
1939 rc = RTFsQuerySerial(pszFullPath, &pSFDEntry->ulSerial);
1940 if (rc != VINF_SUCCESS)
1941 goto exit;
1942
1943 RTFSPROPERTIES FsProperties;
1944 rc = RTFsQueryProperties(pszFullPath, &FsProperties);
1945 if (rc != VINF_SUCCESS)
1946 goto exit;
1947 vbfsCopyFsPropertiesFromIprt(&pSFDEntry->fsProperties, &FsProperties);
1948
1949 *pcbBuffer = sizeof(SHFLVOLINFO);
1950 }
1951 else AssertFailed();
1952
1953exit:
1954 AssertMsg(rc == VINF_SUCCESS, ("failure: rc = %Rrc\n", rc));
1955 /* free the path string */
1956 vbsfFreeFullPath(pszFullPath);
1957 return rc;
1958}
1959
1960int vbsfQueryFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1961{
1962 if (pcbBuffer == 0 || pBuffer == 0)
1963 {
1964 AssertFailed();
1965 return VERR_INVALID_PARAMETER;
1966 }
1967
1968 if (flags & SHFL_INFO_FILE)
1969 return vbsfQueryFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1970
1971 if (flags & SHFL_INFO_VOLUME)
1972 return vbsfQueryVolumeInfo(pClient, root, flags, pcbBuffer, pBuffer);
1973
1974 AssertFailed();
1975 return VERR_INVALID_PARAMETER;
1976}
1977
1978#ifdef UNITTEST
1979/** Unit test the SHFL_FN_INFORMATION API. Located here as a form of API
1980 * documentation. */
1981void testFSInfo(RTTEST hTest)
1982{
1983 /* If the number or types of parameters are wrong the API should fail. */
1984 testFSInfoBadParameters(hTest);
1985 /* Basic get and set file size test. */
1986 testFSInfoQuerySetFMode(hTest);
1987 /* Basic get and set dir atime test. */
1988 testFSInfoQuerySetDirATime(hTest);
1989 /* Basic get and set file atime test. */
1990 testFSInfoQuerySetFileATime(hTest);
1991 /* Basic set end of file. */
1992 testFSInfoQuerySetEndOfFile(hTest);
1993 /* Add tests as required... */
1994}
1995#endif
1996int vbsfSetFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1997{
1998 uint32_t type = vbsfQueryHandleType(pClient, Handle)
1999 & (SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE|SHFL_HF_TYPE_VOLUME);
2000
2001 if (type == 0 || pcbBuffer == 0 || pBuffer == 0)
2002 {
2003 AssertFailed();
2004 return VERR_INVALID_PARAMETER;
2005 }
2006
2007 if (flags & SHFL_INFO_FILE)
2008 return vbsfSetFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2009
2010 if (flags & SHFL_INFO_SIZE)
2011 return vbsfSetEndOfFile(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2012
2013// if (flags & SHFL_INFO_VOLUME)
2014// return vbsfVolumeInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2015 AssertFailed();
2016 return VERR_INVALID_PARAMETER;
2017}
2018
2019#ifdef UNITTEST
2020/** Unit test the SHFL_FN_LOCK API. Located here as a form of API
2021 * documentation. */
2022void testLock(RTTEST hTest)
2023{
2024 /* If the number or types of parameters are wrong the API should fail. */
2025 testLockBadParameters(hTest);
2026 /* Simple file locking and unlocking test. */
2027 testLockFileSimple(hTest);
2028 /* Add tests as required... */
2029}
2030#endif
2031
2032int vbsfLock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
2033{
2034 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
2035 uint32_t fRTLock = 0;
2036
2037 Assert((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL);
2038
2039 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
2040 if (RT_SUCCESS(rc))
2041 { /* likely */ }
2042 else
2043 return rc;
2044
2045 if ( ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL)
2046 || (flags & SHFL_LOCK_ENTIRE)
2047 )
2048 {
2049 AssertFailed();
2050 return VERR_INVALID_PARAMETER;
2051 }
2052
2053 /* Lock type */
2054 switch(flags & SHFL_LOCK_MODE_MASK)
2055 {
2056 case SHFL_LOCK_SHARED:
2057 fRTLock = RTFILE_LOCK_READ;
2058 break;
2059
2060 case SHFL_LOCK_EXCLUSIVE:
2061 fRTLock = RTFILE_LOCK_READ | RTFILE_LOCK_WRITE;
2062 break;
2063
2064 default:
2065 AssertFailed();
2066 return VERR_INVALID_PARAMETER;
2067 }
2068
2069 /* Lock wait type */
2070 if (flags & SHFL_LOCK_WAIT)
2071 fRTLock |= RTFILE_LOCK_WAIT;
2072 else
2073 fRTLock |= RTFILE_LOCK_IMMEDIATELY;
2074
2075#ifdef RT_OS_WINDOWS
2076 rc = RTFileLock(pHandle->file.Handle, fRTLock, offset, length);
2077 if (rc != VINF_SUCCESS)
2078 Log(("RTFileLock %RTfile %RX64 %RX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
2079#else
2080 Log(("vbsfLock: Pretend success handle=%x\n", Handle));
2081 rc = VINF_SUCCESS;
2082 RT_NOREF2(offset, length);
2083#endif
2084 return rc;
2085}
2086
2087int vbsfUnlock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
2088{
2089 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
2090
2091 Assert((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL);
2092
2093 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
2094 if (RT_SUCCESS(rc))
2095 { /* likely */ }
2096 else
2097 return rc;
2098
2099 if ( ((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL)
2100 || (flags & SHFL_LOCK_ENTIRE)
2101 )
2102 {
2103 return VERR_INVALID_PARAMETER;
2104 }
2105
2106#ifdef RT_OS_WINDOWS
2107 rc = RTFileUnlock(pHandle->file.Handle, offset, length);
2108 if (rc != VINF_SUCCESS)
2109 Log(("RTFileUnlock %RTfile %RX64 %RTX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
2110#else
2111 Log(("vbsfUnlock: Pretend success handle=%x\n", Handle));
2112 rc = VINF_SUCCESS;
2113 RT_NOREF2(offset, length);
2114#endif
2115
2116 return rc;
2117}
2118
2119
2120#ifdef UNITTEST
2121/** Unit test the SHFL_FN_REMOVE API. Located here as a form of API
2122 * documentation. */
2123void testRemove(RTTEST hTest)
2124{
2125 /* If the number or types of parameters are wrong the API should fail. */
2126 testRemoveBadParameters(hTest);
2127 /* Add tests as required... */
2128}
2129#endif
2130int vbsfRemove(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint32_t flags)
2131{
2132 int rc = VINF_SUCCESS;
2133
2134 /* Validate input */
2135 if ( flags & ~(SHFL_REMOVE_FILE|SHFL_REMOVE_DIR|SHFL_REMOVE_SYMLINK)
2136 || cbPath == 0
2137 || pPath == 0)
2138 {
2139 AssertFailed();
2140 return VERR_INVALID_PARAMETER;
2141 }
2142
2143 /* Build a host full path for the given path
2144 * and convert ucs2 to utf8 if necessary.
2145 */
2146 char *pszFullPath = NULL;
2147
2148 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, NULL);
2149 if (RT_SUCCESS(rc))
2150 {
2151 /* is the guest allowed to write to this share? */
2152 bool fWritable;
2153 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
2154 if (RT_FAILURE(rc) || !fWritable)
2155 rc = VERR_WRITE_PROTECT;
2156
2157 if (RT_SUCCESS(rc))
2158 {
2159 if (flags & SHFL_REMOVE_SYMLINK)
2160 rc = RTSymlinkDelete(pszFullPath, 0);
2161 else if (flags & SHFL_REMOVE_FILE)
2162 rc = RTFileDelete(pszFullPath);
2163 else
2164 rc = RTDirRemove(pszFullPath);
2165 }
2166
2167#ifndef DEBUG_dmik
2168 // VERR_ACCESS_DENIED for example?
2169 // Assert(rc == VINF_SUCCESS || rc == VERR_DIR_NOT_EMPTY);
2170#endif
2171 /* free the path string */
2172 vbsfFreeFullPath(pszFullPath);
2173 }
2174 return rc;
2175}
2176
2177
2178#ifdef UNITTEST
2179/** Unit test the SHFL_FN_RENAME API. Located here as a form of API
2180 * documentation. */
2181void testRename(RTTEST hTest)
2182{
2183 /* If the number or types of parameters are wrong the API should fail. */
2184 testRenameBadParameters(hTest);
2185 /* Add tests as required... */
2186}
2187#endif
2188int vbsfRename(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pSrc, SHFLSTRING *pDest, uint32_t flags)
2189{
2190 int rc = VINF_SUCCESS;
2191
2192 /* Validate input */
2193 if ( flags & ~(SHFL_RENAME_FILE|SHFL_RENAME_DIR|SHFL_RENAME_REPLACE_IF_EXISTS)
2194 || pSrc == 0
2195 || pDest == 0)
2196 {
2197 AssertFailed();
2198 return VERR_INVALID_PARAMETER;
2199 }
2200
2201 /* Build a host full path for the given path
2202 * and convert ucs2 to utf8 if necessary.
2203 */
2204 char *pszFullPathSrc = NULL;
2205 char *pszFullPathDest = NULL;
2206
2207 rc = vbsfBuildFullPath(pClient, root, pSrc, pSrc->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPathSrc, NULL);
2208 if (rc != VINF_SUCCESS)
2209 return rc;
2210
2211 rc = vbsfBuildFullPath(pClient, root, pDest, pDest->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPathDest, NULL, false, true);
2212 if (RT_SUCCESS (rc))
2213 {
2214 Log(("Rename %s to %s\n", pszFullPathSrc, pszFullPathDest));
2215
2216 /* is the guest allowed to write to this share? */
2217 bool fWritable;
2218 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
2219 if (RT_FAILURE(rc) || !fWritable)
2220 rc = VERR_WRITE_PROTECT;
2221
2222 if (RT_SUCCESS(rc))
2223 {
2224 if ((flags & (SHFL_RENAME_FILE | SHFL_RENAME_DIR)) == (SHFL_RENAME_FILE | SHFL_RENAME_DIR))
2225 {
2226 rc = RTPathRename(pszFullPathSrc, pszFullPathDest,
2227 flags & SHFL_RENAME_REPLACE_IF_EXISTS ? RTPATHRENAME_FLAGS_REPLACE : 0);
2228 }
2229 else if (flags & SHFL_RENAME_FILE)
2230 {
2231 rc = RTFileMove(pszFullPathSrc, pszFullPathDest,
2232 ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTFILEMOVE_FLAGS_REPLACE : 0));
2233 }
2234 else
2235 {
2236 /* NT ignores the REPLACE flag and simply return and already exists error. */
2237 rc = RTDirRename(pszFullPathSrc, pszFullPathDest,
2238 ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTPATHRENAME_FLAGS_REPLACE : 0));
2239 }
2240 }
2241
2242 /* free the path string */
2243 vbsfFreeFullPath(pszFullPathDest);
2244 }
2245 /* free the path string */
2246 vbsfFreeFullPath(pszFullPathSrc);
2247 return rc;
2248}
2249
2250#ifdef UNITTEST
2251/** Unit test the SHFL_FN_SYMLINK API. Located here as a form of API
2252 * documentation. */
2253void testSymlink(RTTEST hTest)
2254{
2255 /* If the number or types of parameters are wrong the API should fail. */
2256 testSymlinkBadParameters(hTest);
2257 /* Add tests as required... */
2258}
2259#endif
2260int vbsfSymlink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pNewPath, SHFLSTRING *pOldPath, SHFLFSOBJINFO *pInfo)
2261{
2262 int rc = VINF_SUCCESS;
2263
2264 char *pszFullNewPath = NULL;
2265 char *pszFullOldPath = NULL;
2266
2267 /* XXX: no support for UCS2 at the moment. */
2268 if (!BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
2269 return VERR_NOT_IMPLEMENTED;
2270
2271 bool fSymlinksCreate;
2272 rc = vbsfMappingsQuerySymlinksCreate(pClient, root, &fSymlinksCreate);
2273 AssertRCReturn(rc, rc);
2274 if (!fSymlinksCreate)
2275 return VERR_WRITE_PROTECT; /* XXX or VERR_TOO_MANY_SYMLINKS? */
2276
2277 rc = vbsfBuildFullPath(pClient, root, pNewPath, pNewPath->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullNewPath, NULL);
2278 AssertRCReturn(rc, rc);
2279
2280 /* Verify that the link target can be a valid host path, i.e. does not contain invalid characters. */
2281 uint32_t fu32PathFlags = 0;
2282 uint32_t fu32Options = 0;
2283 rc = vbsfPathGuestToHost(pClient, root, pOldPath, pOldPath->u16Size + SHFLSTRING_HEADER_SIZE,
2284 &pszFullOldPath, NULL, fu32Options, &fu32PathFlags);
2285 if (RT_FAILURE(rc))
2286 {
2287 vbsfFreeFullPath(pszFullNewPath);
2288 return rc;
2289 }
2290
2291 rc = RTSymlinkCreate(pszFullNewPath, (const char *)pOldPath->String.utf8,
2292 RTSYMLINKTYPE_UNKNOWN, 0);
2293 if (RT_SUCCESS(rc))
2294 {
2295 RTFSOBJINFO info;
2296 rc = RTPathQueryInfoEx(pszFullNewPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
2297 if (RT_SUCCESS(rc))
2298 vbfsCopyFsObjInfoFromIprt(pInfo, &info);
2299 }
2300
2301 vbsfFreeFullPath(pszFullOldPath);
2302 vbsfFreeFullPath(pszFullNewPath);
2303
2304 return rc;
2305}
2306
2307/*
2308 * Clean up our mess by freeing all handles that are still valid.
2309 *
2310 */
2311int vbsfDisconnect(SHFLCLIENTDATA *pClient)
2312{
2313 for (int i = 0; i < SHFLHANDLE_MAX; ++i)
2314 {
2315 SHFLFILEHANDLE *pHandle = NULL;
2316 SHFLHANDLE Handle = (SHFLHANDLE)i;
2317
2318 uint32_t type = vbsfQueryHandleType(pClient, Handle);
2319 switch (type & (SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE))
2320 {
2321 case SHFL_HF_TYPE_DIR:
2322 {
2323 pHandle = vbsfQueryDirHandle(pClient, Handle);
2324 break;
2325 }
2326 case SHFL_HF_TYPE_FILE:
2327 {
2328 pHandle = vbsfQueryFileHandle(pClient, Handle);
2329 break;
2330 }
2331 default:
2332 break;
2333 }
2334
2335 if (pHandle)
2336 {
2337 LogFunc(("Opened handle 0x%08x\n", i));
2338 vbsfClose(pClient, pHandle->root, Handle);
2339 }
2340 }
2341
2342 for (uint32_t i = 0; i < RT_ELEMENTS(pClient->acMappings); i++)
2343 if (pClient->acMappings[i])
2344 {
2345 uint16_t cMappings = pClient->acMappings[i];
2346 while (cMappings-- > 0)
2347 vbsfUnmapFolder(pClient, i);
2348 }
2349
2350 return VINF_SUCCESS;
2351}
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