VirtualBox

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

Last change on this file since 78018 was 77861, checked in by vboxsync, 6 years ago

SharedFolderSvc: another todo. bugref:9172

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 80.9 KB
Line 
1/* $Id: vbsf.cpp 77861 2019-03-24 02:42:58Z 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, PCSHFLSTRING 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, uint64_t *pfOpen)
214{
215 uint64_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 RTFILEACTION enmActionTaken = RTFILEACTION_INVALID;
459 SHFLHANDLE handle = SHFL_HANDLE_NIL;
460 SHFLFILEHANDLE *pHandle = NULL;
461
462 /* is the guest allowed to write to this share? */
463 bool fWritable;
464 int rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
465 if (RT_FAILURE(rc))
466 fWritable = false;
467
468 uint64_t fOpen = 0;
469 rc = vbsfConvertFileOpenFlags(fWritable, pParms->CreateFlags, pParms->Info.Attr.fMode, pParms->Handle, &fOpen);
470 if (RT_SUCCESS(rc))
471 {
472 rc = VERR_NO_MEMORY; /* Default error. */
473 handle = vbsfAllocFileHandle(pClient);
474 if (handle != SHFL_HANDLE_NIL)
475 {
476 pHandle = vbsfQueryFileHandle(pClient, handle);
477 if (pHandle)
478 {
479 pHandle->root = root;
480 pHandle->file.fOpenFlags = fOpen;
481 rc = RTFileOpenEx(pszPath, fOpen, &pHandle->file.Handle, &enmActionTaken);
482 }
483 }
484 }
485 bool fNoError = false;
486 if (RT_FAILURE(rc))
487 {
488 switch (rc)
489 {
490 case VERR_FILE_NOT_FOUND:
491 pParms->Result = SHFL_FILE_NOT_FOUND;
492
493 /* This actually isn't an error, so correct the rc before return later,
494 because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
495 fNoError = true;
496 break;
497
498 case VERR_PATH_NOT_FOUND:
499 pParms->Result = SHFL_PATH_NOT_FOUND;
500 fNoError = true; /* Not an error either (see above). */
501 break;
502
503 case VERR_ALREADY_EXISTS:
504 {
505 RTFSOBJINFO info;
506
507 /** @todo Possible race left here. */
508 if (RT_SUCCESS(RTPathQueryInfoEx(pszPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient))))
509 {
510#ifdef RT_OS_WINDOWS
511 info.Attr.fMode |= 0111;
512#endif
513 vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
514 }
515 pParms->Result = SHFL_FILE_EXISTS;
516
517 /* This actually isn't an error, so correct the rc before return later,
518 because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
519 fNoError = true;
520 break;
521 }
522
523 case VERR_TOO_MANY_OPEN_FILES:
524 {
525 static int s_cErrors;
526 if (s_cErrors < 32)
527 {
528 LogRel(("SharedFolders host service: Cannot open '%s' -- too many open files.\n", pszPath));
529#if defined RT_OS_LINUX || defined(RT_OS_SOLARIS)
530 if (s_cErrors < 1)
531 LogRel(("SharedFolders host service: Try to increase the limit for open files (ulimit -n)\n"));
532#endif
533 s_cErrors++;
534 }
535 pParms->Result = SHFL_NO_RESULT;
536 break;
537 }
538
539 default:
540 pParms->Result = SHFL_NO_RESULT;
541 }
542 }
543 else
544 {
545 switch (enmActionTaken)
546 {
547 default:
548 AssertFailed();
549 RT_FALL_THRU();
550 case RTFILEACTION_OPENED:
551 pParms->Result = SHFL_FILE_EXISTS;
552 break;
553 case RTFILEACTION_CREATED:
554 pParms->Result = SHFL_FILE_CREATED;
555 break;
556 case RTFILEACTION_REPLACED:
557 case RTFILEACTION_TRUNCATED: /* not quite right */
558 pParms->Result = SHFL_FILE_REPLACED;
559 break;
560 }
561
562 if ( (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_REPLACE_IF_EXISTS
563 || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_OVERWRITE_IF_EXISTS)
564 {
565 /* For now, we do not treat a failure here as fatal. */
566 /** @todo Also set the size for SHFL_CF_ACT_CREATE_IF_NEW if SHFL_CF_ACT_FAIL_IF_EXISTS is set. */
567 /** @todo r=bird: Exactly document cbObject usage and see what we can get
568 * away with here. I suspect it is only needed for windows and only
569 * with SHFL_FILE_CREATED and SHFL_FILE_REPLACED, and only if
570 * cbObject is non-zero. */
571 RTFileSetSize(pHandle->file.Handle, pParms->Info.cbObject);
572 }
573#if 0
574 /** @todo */
575 /* Set new attributes. */
576 if ( ( SHFL_CF_ACT_REPLACE_IF_EXISTS
577 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
578 || ( SHFL_CF_ACT_CREATE_IF_NEW
579 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW)))
580 {
581 RTFileSetTimes(pHandle->file.Handle,
582 &pParms->Info.AccessTime,
583 &pParms->Info.ModificationTime,
584 &pParms->Info.ChangeTime,
585 &pParms->Info.BirthTime
586 );
587
588 RTFileSetMode (pHandle->file.Handle, pParms->Info.Attr.fMode);
589 }
590#endif
591 RTFSOBJINFO info;
592
593 /* Get file information */
594 rc = RTFileQueryInfo(pHandle->file.Handle, &info, RTFSOBJATTRADD_NOTHING);
595 if (RT_SUCCESS(rc))
596 {
597#ifdef RT_OS_WINDOWS
598 info.Attr.fMode |= 0111;
599#endif
600 vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
601 }
602 }
603 /* Free resources if any part of the function has failed. */
604 if (RT_FAILURE(rc))
605 {
606 if ( (0 != pHandle)
607 && (NIL_RTFILE != pHandle->file.Handle)
608 && (0 != pHandle->file.Handle))
609 {
610 RTFileClose(pHandle->file.Handle);
611 pHandle->file.Handle = NIL_RTFILE;
612 }
613 if (SHFL_HANDLE_NIL != handle)
614 {
615 vbsfFreeFileHandle(pClient, handle);
616 }
617 pParms->Handle = SHFL_HANDLE_NIL;
618 }
619 else
620 {
621 pParms->Handle = handle;
622 }
623
624 /* Report the driver that all is okay, we're done here */
625 if (fNoError)
626 rc = VINF_SUCCESS;
627
628 LogFlow(("vbsfOpenFile: rc = %Rrc\n", rc));
629 return rc;
630}
631
632/**
633 * Open a folder or create and open a new one.
634 *
635 * @returns IPRT status code
636 * @param pClient Data structure describing the client accessing the shared folder
637 * @param root The index of the shared folder in the table of mappings.
638 * @param pszPath Path to the file or folder on the host.
639 * @param pParms @a CreateFlags Creation or open parameters, see include/VBox/shflsvc.h
640 * @retval pParms @a Result Shared folder status code, see include/VBox/shflsvc.h
641 * @retval pParms @a Handle On success the (shared folder) handle of the folder opened or
642 * created
643 * @retval pParms @a Info On success the parameters of the folder opened or created
644 *
645 * @note folders are created with fMode = 0777
646 */
647static int vbsfOpenDir(SHFLCLIENTDATA *pClient, SHFLROOT root, const char *pszPath,
648 SHFLCREATEPARMS *pParms)
649{
650 LogFlow(("vbsfOpenDir: pszPath = %s, pParms = %p\n", pszPath, pParms));
651 Log(("SHFL create flags %08x\n", pParms->CreateFlags));
652
653 int rc = VERR_NO_MEMORY;
654 SHFLHANDLE handle = vbsfAllocDirHandle(pClient);
655 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, handle);
656 if (0 != pHandle)
657 {
658 pHandle->root = root;
659 rc = VINF_SUCCESS;
660 pParms->Result = SHFL_FILE_EXISTS; /* May be overwritten with SHFL_FILE_CREATED. */
661 /** @todo Can anyone think of a sensible, race-less way to do this? Although
662 I suspect that the race is inherent, due to the API available... */
663 /* Try to create the folder first if "create if new" is specified. If this
664 fails, and "open if exists" is specified, then we ignore the failure and try
665 to open the folder anyway. */
666 if ( SHFL_CF_ACT_CREATE_IF_NEW
667 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW))
668 {
669 /** @todo render supplied attributes.
670 * bird: The guest should specify this. For windows guests RTFS_DOS_DIRECTORY should suffice. */
671 RTFMODE fMode = 0777;
672
673 pParms->Result = SHFL_FILE_CREATED;
674 rc = RTDirCreate(pszPath, fMode, 0);
675 if (RT_FAILURE(rc))
676 {
677 switch (rc)
678 {
679 case VERR_ALREADY_EXISTS:
680 pParms->Result = SHFL_FILE_EXISTS;
681 break;
682 case VERR_PATH_NOT_FOUND:
683 pParms->Result = SHFL_PATH_NOT_FOUND;
684 break;
685 default:
686 pParms->Result = SHFL_NO_RESULT;
687 }
688 }
689 }
690 if ( RT_SUCCESS(rc)
691 || (SHFL_CF_ACT_OPEN_IF_EXISTS == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS)))
692 {
693 /* Open the directory now */
694 rc = RTDirOpenFiltered(&pHandle->dir.Handle, pszPath, RTDIRFILTER_NONE, 0 /*fFlags*/);
695 if (RT_SUCCESS(rc))
696 {
697 RTFSOBJINFO info;
698
699 rc = RTDirQueryInfo(pHandle->dir.Handle, &info, RTFSOBJATTRADD_NOTHING);
700 if (RT_SUCCESS(rc))
701 {
702 vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
703 }
704 }
705 else
706 {
707 switch (rc)
708 {
709 case VERR_FILE_NOT_FOUND: /* Does this make sense? */
710 pParms->Result = SHFL_FILE_NOT_FOUND;
711 break;
712 case VERR_PATH_NOT_FOUND:
713 pParms->Result = SHFL_PATH_NOT_FOUND;
714 break;
715 case VERR_ACCESS_DENIED:
716 pParms->Result = SHFL_FILE_EXISTS;
717 break;
718 default:
719 pParms->Result = SHFL_NO_RESULT;
720 }
721 }
722 }
723 }
724 if (RT_FAILURE(rc))
725 {
726 if ( (0 != pHandle)
727 && (0 != pHandle->dir.Handle))
728 {
729 RTDirClose(pHandle->dir.Handle);
730 pHandle->dir.Handle = 0;
731 }
732 if (SHFL_HANDLE_NIL != handle)
733 {
734 vbsfFreeFileHandle(pClient, handle);
735 }
736 pParms->Handle = SHFL_HANDLE_NIL;
737 }
738 else
739 {
740 pParms->Handle = handle;
741 }
742 LogFlow(("vbsfOpenDir: rc = %Rrc\n", rc));
743 return rc;
744}
745
746static int vbsfCloseDir(SHFLFILEHANDLE *pHandle)
747{
748 int rc = VINF_SUCCESS;
749
750 LogFlow(("vbsfCloseDir: Handle = %08X Search Handle = %08X\n",
751 pHandle->dir.Handle, pHandle->dir.SearchHandle));
752
753 RTDirClose(pHandle->dir.Handle);
754
755 if (pHandle->dir.SearchHandle)
756 RTDirClose(pHandle->dir.SearchHandle);
757
758 if (pHandle->dir.pLastValidEntry)
759 {
760 RTMemFree(pHandle->dir.pLastValidEntry);
761 pHandle->dir.pLastValidEntry = NULL;
762 }
763
764 LogFlow(("vbsfCloseDir: rc = %d\n", rc));
765
766 return rc;
767}
768
769
770static int vbsfCloseFile(SHFLFILEHANDLE *pHandle)
771{
772 int rc = VINF_SUCCESS;
773
774 LogFlow(("vbsfCloseFile: Handle = %08X\n",
775 pHandle->file.Handle));
776
777 rc = RTFileClose(pHandle->file.Handle);
778
779 LogFlow(("vbsfCloseFile: rc = %d\n", rc));
780
781 return rc;
782}
783
784/**
785 * Look up file or folder information by host path.
786 *
787 * @returns iprt status code (currently VINF_SUCCESS)
788 * @param pClient client data
789 * @param pszPath The path of the file to be looked up
790 * @retval pParms->Result Status of the operation (success or error)
791 * @retval pParms->Info On success, information returned about the file
792 */
793static int vbsfLookupFile(SHFLCLIENTDATA *pClient, char *pszPath, SHFLCREATEPARMS *pParms)
794{
795 RTFSOBJINFO info;
796 int rc;
797
798 rc = RTPathQueryInfoEx(pszPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
799 LogFlow(("SHFL_CF_LOOKUP\n"));
800 /* Client just wants to know if the object exists. */
801 switch (rc)
802 {
803 case VINF_SUCCESS:
804 {
805#ifdef RT_OS_WINDOWS
806 info.Attr.fMode |= 0111;
807#endif
808 vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
809 pParms->Result = SHFL_FILE_EXISTS;
810 break;
811 }
812
813 case VERR_FILE_NOT_FOUND:
814 {
815 pParms->Result = SHFL_FILE_NOT_FOUND;
816 rc = VINF_SUCCESS;
817 break;
818 }
819
820 case VERR_PATH_NOT_FOUND:
821 {
822 pParms->Result = SHFL_PATH_NOT_FOUND;
823 rc = VINF_SUCCESS;
824 break;
825 }
826 }
827 pParms->Handle = SHFL_HANDLE_NIL;
828 return rc;
829}
830
831#ifdef UNITTEST
832/** Unit test the SHFL_FN_CREATE API. Located here as a form of API
833 * documentation. */
834void testCreate(RTTEST hTest)
835{
836 /* Simple opening of an existing file. */
837 testCreateFileSimple(hTest);
838 testCreateFileSimpleCaseInsensitive(hTest);
839 /* Simple opening of an existing directory. */
840 /** @todo How do wildcards in the path name work? */
841 testCreateDirSimple(hTest);
842 /* If the number or types of parameters are wrong the API should fail. */
843 testCreateBadParameters(hTest);
844 /* Add tests as required... */
845}
846#endif
847
848/**
849 * Create or open a file or folder. Perform character set and case
850 * conversion on the file name if necessary.
851 *
852 * @returns IPRT status code, but see note below
853 * @param pClient Data structure describing the client accessing the shared
854 * folder
855 * @param root The index of the shared folder in the table of mappings.
856 * The host path of the shared folder is found using this.
857 * @param pPath The path of the file or folder relative to the host path
858 * indexed by root.
859 * @param cbPath Presumably the length of the path in pPath. Actually
860 * ignored, as pPath contains a length parameter.
861 * @param pParms @a Info If a new file is created or an old one overwritten, set
862 * these attributes
863 * @retval pParms @a Result Shared folder result code, see include/VBox/shflsvc.h
864 * @retval pParms @a Handle Shared folder handle to the newly opened file
865 * @retval pParms @a Info Attributes of the file or folder opened
866 *
867 * @note This function returns success if a "non-exceptional" error occurred,
868 * such as "no such file". In this case, the caller should check the
869 * pParms->Result return value and whether pParms->Handle is valid.
870 */
871int vbsfCreate(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, SHFLCREATEPARMS *pParms)
872{
873 int rc = VINF_SUCCESS;
874
875 LogFlow(("vbsfCreate: pClient = %p, pPath = %p, cbPath = %d, pParms = %p CreateFlags=%x\n",
876 pClient, pPath, cbPath, pParms, pParms->CreateFlags));
877
878 /* Check the client access rights to the root. */
879 /** @todo */
880
881 /* Build a host full path for the given path, handle file name case issues (if the guest
882 * expects case-insensitive paths but the host is case-sensitive) and convert ucs2 to utf8 if
883 * necessary.
884 */
885 char *pszFullPath = NULL;
886 uint32_t cbFullPathRoot = 0;
887
888 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot);
889 if (RT_SUCCESS(rc))
890 {
891 /* Reset return value in case client forgot to do so.
892 * pParms->Handle must not be reset here, as it is used
893 * in vbsfOpenFile to detect old additions.
894 */
895 pParms->Result = SHFL_NO_RESULT;
896
897 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_LOOKUP))
898 {
899 rc = vbsfLookupFile(pClient, pszFullPath, pParms);
900 }
901 else
902 {
903 /* Query path information. */
904 RTFSOBJINFO info;
905
906 rc = RTPathQueryInfoEx(pszFullPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
907 LogFlow(("RTPathQueryInfoEx returned %Rrc\n", rc));
908
909 if (RT_SUCCESS(rc))
910 {
911 /* Mark it as a directory in case the caller didn't. */
912 /**
913 * @todo I left this in in order not to change the behaviour of the
914 * function too much. Is it really needed, and should it really be
915 * here?
916 */
917 if (BIT_FLAG(info.Attr.fMode, RTFS_DOS_DIRECTORY))
918 {
919 pParms->CreateFlags |= SHFL_CF_DIRECTORY;
920 }
921
922 /**
923 * @todo This should be in the Windows Guest Additions, as no-one else
924 * needs it.
925 */
926 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_OPEN_TARGET_DIRECTORY))
927 {
928 vbsfStripLastComponent(pszFullPath, cbFullPathRoot);
929 pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_EXISTS;
930 pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_NEW;
931 pParms->CreateFlags |= SHFL_CF_DIRECTORY;
932 pParms->CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS;
933 pParms->CreateFlags |= SHFL_CF_ACT_FAIL_IF_NEW;
934 }
935 }
936
937 rc = VINF_SUCCESS;
938
939 /* Note: do not check the SHFL_CF_ACCESS_WRITE here, only check if the open operation
940 * will cause changes.
941 *
942 * Actual operations (write, set attr, etc), which can write to a shared folder, have
943 * the check and will return VERR_WRITE_PROTECT if the folder is not writable.
944 */
945 if ( (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_REPLACE_IF_EXISTS
946 || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_OVERWRITE_IF_EXISTS
947 || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_NEW) == SHFL_CF_ACT_CREATE_IF_NEW
948 )
949 {
950 /* is the guest allowed to write to this share? */
951 bool fWritable;
952 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
953 if (RT_FAILURE(rc) || !fWritable)
954 rc = VERR_WRITE_PROTECT;
955 }
956
957 if (RT_SUCCESS(rc))
958 {
959 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_DIRECTORY))
960 {
961 rc = vbsfOpenDir(pClient, root, pszFullPath, pParms);
962 }
963 else
964 {
965 rc = vbsfOpenFile(pClient, root, pszFullPath, pParms);
966 }
967 }
968 else
969 {
970 pParms->Handle = SHFL_HANDLE_NIL;
971 }
972 }
973
974 /* free the path string */
975 vbsfFreeFullPath(pszFullPath);
976 }
977
978 Log(("vbsfCreate: handle = %RX64 rc = %Rrc result=%x\n", (uint64_t)pParms->Handle, rc, pParms->Result));
979
980 return rc;
981}
982
983#ifdef UNITTEST
984/** Unit test the SHFL_FN_CLOSE API. Located here as a form of API
985 * documentation. */
986void testClose(RTTEST hTest)
987{
988 /* If the API parameters are invalid the API should fail. */
989 testCloseBadParameters(hTest);
990 /* Add tests as required... */
991}
992#endif
993
994int vbsfClose(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
995{
996 LogFunc(("pClient = %p, root 0x%RX32, Handle = 0x%RX64\n",
997 pClient, root, Handle));
998
999 int rc = VERR_INVALID_HANDLE;
1000 uint32_t type = vbsfQueryHandleType(pClient, Handle);
1001 Assert((type & ~(SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE)) == 0);
1002 switch (type & (SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE))
1003 {
1004 case SHFL_HF_TYPE_DIR:
1005 {
1006 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
1007 if (RT_LIKELY(pHandle && root == pHandle->root))
1008 {
1009 rc = vbsfCloseDir(pHandle);
1010 vbsfFreeFileHandle(pClient, Handle);
1011 }
1012 break;
1013 }
1014 case SHFL_HF_TYPE_FILE:
1015 {
1016 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1017 if (RT_LIKELY(pHandle && root == pHandle->root))
1018 {
1019 rc = vbsfCloseFile(pHandle);
1020 vbsfFreeFileHandle(pClient, Handle);
1021 }
1022 break;
1023 }
1024 default:
1025 break;
1026 }
1027
1028 LogFunc(("rc = %Rrc\n", rc));
1029 return rc;
1030}
1031
1032/**
1033 * Helper for vbsfReadPages and vbsfWritePages that creates a S/G buffer from a
1034 * pages parameter.
1035 */
1036static int vbsfPagesToSgBuf(VBOXHGCMSVCPARMPAGES const *pPages, uint32_t cbLeft, PRTSGBUF pSgBuf)
1037{
1038 PRTSGSEG paSegs = (PRTSGSEG)RTMemTmpAlloc(sizeof(paSegs[0]) * pPages->cPages);
1039 if (paSegs)
1040 {
1041 /*
1042 * Convert the pages to segments.
1043 */
1044 uint32_t iSeg = 0;
1045 uint32_t iPage = 0;
1046 for (;;)
1047 {
1048 Assert(iSeg < pPages->cPages);
1049 Assert(iPage < pPages->cPages);
1050
1051 /* Current page. */
1052 void *pvSeg;
1053 paSegs[iSeg].pvSeg = pvSeg = pPages->papvPages[iPage];
1054 uint32_t cbSeg = PAGE_SIZE - (uint32_t)((uintptr_t)pvSeg & PAGE_OFFSET_MASK);
1055 iPage++;
1056
1057 /* Adjacent to the next page? */
1058 while ( iPage < pPages->cPages
1059 && (uintptr_t)pvSeg + cbSeg == (uintptr_t)pPages->papvPages[iPage])
1060 {
1061 iPage++;
1062 cbSeg += PAGE_SIZE;
1063 }
1064
1065 /* Adjust for max size. */
1066 if (cbLeft <= cbSeg)
1067 {
1068 paSegs[iSeg++].cbSeg = cbLeft;
1069 break;
1070 }
1071 paSegs[iSeg++].cbSeg = cbSeg;
1072 cbLeft -= cbSeg;
1073 }
1074
1075 /*
1076 * Initialize the s/g buffer and execute the read.
1077 */
1078 RTSgBufInit(pSgBuf, paSegs, iSeg);
1079 return VINF_SUCCESS;
1080 }
1081 pSgBuf->paSegs = NULL;
1082 return VERR_NO_TMP_MEMORY;
1083}
1084
1085
1086#ifdef UNITTEST
1087/** Unit test the SHFL_FN_READ API. Located here as a form of API
1088 * documentation. */
1089void testRead(RTTEST hTest)
1090{
1091 /* If the number or types of parameters are wrong the API should fail. */
1092 testReadBadParameters(hTest);
1093 /* Basic reading from a file. */
1094 testReadFileSimple(hTest);
1095 /* Add tests as required... */
1096}
1097#endif
1098int vbsfRead(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer)
1099{
1100 LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64, offset 0x%RX64, bytes 0x%RX32\n",
1101 pClient, root, Handle, offset, pcbBuffer? *pcbBuffer: 0));
1102
1103 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1104
1105 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1106 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1107 if (RT_SUCCESS(rc))
1108 {
1109 size_t const cbToRead = *pcbBuffer;
1110 if (RT_LIKELY(cbToRead > 0))
1111 {
1112 size_t cbActual = 0;
1113 rc = RTFileReadAt(pHandle->file.Handle, offset, pBuffer, cbToRead, &cbActual);
1114 *pcbBuffer = (uint32_t)cbActual;
1115 }
1116 else
1117 {
1118 /* Reading zero bytes always succeeds. */
1119 rc = VINF_SUCCESS;
1120 }
1121 }
1122 else
1123 *pcbBuffer = 0;
1124
1125 LogFunc(("%Rrc bytes read 0x%RX32\n", rc, *pcbBuffer));
1126 return rc;
1127}
1128
1129/**
1130 * SHFL_FN_READ w/o bounce buffering.
1131 */
1132int vbsfReadPages(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hFile, uint64_t offFile,
1133 uint32_t *pcbRead, PVBOXHGCMSVCPARMPAGES pPages)
1134{
1135 LogFunc(("pClient %p, idRoot %#RX32, hFile %#RX64, offFile %#RX64, cbRead %#RX32, cPages %#x\n",
1136 pClient, idRoot, hFile, offFile, *pcbRead, pPages->cPages));
1137
1138 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1139
1140 size_t cbTotal = 0;
1141 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, hFile);
1142 int rc = vbsfCheckHandleAccess(pClient, idRoot, pHandle, VBSF_CHECK_ACCESS_READ);
1143 if (RT_SUCCESS(rc))
1144 {
1145 uint32_t const cbToRead = *pcbRead;
1146 if (cbToRead > 0)
1147 {
1148 ASSERT_GUEST_RETURN(pPages->cPages > 0, VERR_INTERNAL_ERROR_3);
1149
1150 /*
1151 * Convert to a scatter-gather buffer.
1152 *
1153 * We need not do any platform specific code here as the RTSGBUF
1154 * segment array maps directly onto the posix iovec structure.
1155 * Windows does currently benefit much from this conversion, but
1156 * so be it.
1157 */
1158 RTSGBUF SgBuf;
1159 rc = vbsfPagesToSgBuf(pPages, cbToRead, &SgBuf);
1160 if (RT_SUCCESS(rc))
1161 {
1162 rc = RTFileSgReadAt(pHandle->file.Handle, offFile, &SgBuf, cbToRead, &cbTotal);
1163 while (rc == VERR_INTERRUPTED)
1164 {
1165 RTSgBufReset(&SgBuf);
1166 rc = RTFileSgReadAt(pHandle->file.Handle, offFile, &SgBuf, cbToRead, &cbTotal);
1167 }
1168
1169 RTMemTmpFree((void *)SgBuf.paSegs);
1170 }
1171 else
1172 rc = VERR_NO_TMP_MEMORY;
1173
1174 *pcbRead = (uint32_t)cbTotal;
1175 }
1176 else
1177 {
1178 /* Reading zero bytes always succeeds. */
1179 rc = VINF_SUCCESS;
1180 }
1181 }
1182 else
1183 *pcbRead = 0;
1184
1185 LogFunc(("%Rrc bytes read %#zx\n", rc, cbTotal));
1186 return rc;
1187}
1188
1189/**
1190 * Helps with writes to RTFILE_O_APPEND files.
1191 */
1192static uint64_t vbsfWriteCalcPostAppendFilePosition(RTFILE hFile, uint64_t offGuessed)
1193{
1194 RTFSOBJINFO ObjInfo;
1195 int rc2 = RTFileQueryInfo(hFile, &ObjInfo, RTFSOBJATTRADD_NOTHING);
1196 if (RT_SUCCESS(rc2) && (uint64_t)ObjInfo.cbObject >= offGuessed)
1197 return ObjInfo.cbObject;
1198 return offGuessed;
1199}
1200
1201#ifdef UNITTEST
1202/** Unit test the SHFL_FN_WRITE API. Located here as a form of API
1203 * documentation. */
1204void testWrite(RTTEST hTest)
1205{
1206 /* If the number or types of parameters are wrong the API should fail. */
1207 testWriteBadParameters(hTest);
1208 /* Simple test of writing to a file. */
1209 testWriteFileSimple(hTest);
1210 /* Add tests as required... */
1211}
1212#endif
1213int vbsfWrite(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hFile, uint64_t *poffFile,
1214 uint32_t *pcbBuffer, uint8_t *pBuffer)
1215{
1216 uint64_t offFile = *poffFile;
1217 LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64, offFile 0x%RX64, bytes 0x%RX32\n",
1218 pClient, idRoot, hFile, offFile, *pcbBuffer));
1219
1220 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1221
1222 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, hFile);
1223 int rc = vbsfCheckHandleAccess(pClient, idRoot, pHandle, VBSF_CHECK_ACCESS_WRITE);
1224 if (RT_SUCCESS(rc))
1225 {
1226 size_t const cbToWrite = *pcbBuffer;
1227 if (RT_LIKELY(cbToWrite != 0))
1228 {
1229 size_t cbWritten = 0;
1230 if (!(pHandle->file.fOpenFlags & RTFILE_O_APPEND))
1231 rc = RTFileWriteAt(pHandle->file.Handle, offFile, pBuffer, cbToWrite, &cbWritten);
1232 else
1233 {
1234 rc = RTFileSeek(pHandle->file.Handle, offFile, RTFILE_SEEK_BEGIN, NULL);
1235 AssertRC(rc);
1236 if (RT_SUCCESS(rc))
1237 {
1238 rc = RTFileWrite(pHandle->file.Handle, pBuffer, cbToWrite, &cbWritten);
1239 *pcbBuffer = (uint32_t)cbWritten;
1240 }
1241 }
1242
1243 /* Update the file offset (mainly for RTFILE_O_APPEND), */
1244 if (RT_SUCCESS(rc))
1245 {
1246 offFile += cbWritten;
1247 if (!(pHandle->file.fOpenFlags & RTFILE_O_APPEND))
1248 *poffFile = offFile;
1249 else
1250 *poffFile = vbsfWriteCalcPostAppendFilePosition(pHandle->file.Handle, offFile);
1251 }
1252 }
1253 else
1254 {
1255 /** @todo What writing zero bytes should do? */
1256 rc = VINF_SUCCESS;
1257 }
1258 }
1259 else
1260 *pcbBuffer = 0;
1261 LogFunc(("%Rrc bytes written 0x%RX32\n", rc, *pcbBuffer));
1262 return rc;
1263}
1264
1265/**
1266 * SHFL_FN_WRITE w/o bounce buffering.
1267 */
1268int vbsfWritePages(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hFile, uint64_t *poffFile,
1269 uint32_t *pcbWrite, PVBOXHGCMSVCPARMPAGES pPages)
1270{
1271 uint64_t offFile = *poffFile;
1272 LogFunc(("pClient %p, idRoot %#RX32, hFile %#RX64, offFile %#RX64, cbWrite %#RX32, cPages %#x\n",
1273 pClient, idRoot, hFile, offFile, *pcbWrite, pPages->cPages));
1274
1275 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1276
1277 size_t cbTotal = 0;
1278 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, hFile);
1279 int rc = vbsfCheckHandleAccess(pClient, idRoot, pHandle, VBSF_CHECK_ACCESS_WRITE);
1280 if (RT_SUCCESS(rc))
1281 {
1282 uint32_t const cbToWrite = *pcbWrite;
1283 if (cbToWrite > 0)
1284 {
1285 ASSERT_GUEST_RETURN(pPages->cPages > 0, VERR_INTERNAL_ERROR_3);
1286
1287 /*
1288 * Convert to a scatter-gather buffer.
1289 *
1290 * We need not do any platform specific code here as the RTSGBUF
1291 * segment array maps directly onto the posix iovec structure.
1292 * Windows does currently benefit much from this conversion, but
1293 * so be it.
1294 */
1295 RTSGBUF SgBuf;
1296 rc = vbsfPagesToSgBuf(pPages, cbToWrite, &SgBuf);
1297 if (RT_SUCCESS(rc))
1298 {
1299#ifndef RT_OS_LINUX
1300 /* Cannot use RTFileSgWriteAt or RTFileWriteAt when opened with
1301 RTFILE_O_APPEND, except for on linux where the offset is
1302 then ignored by the low level kernel API. */
1303 if (pHandle->file.fOpenFlags & RTFILE_O_APPEND)
1304 {
1305 /* paranoia */
1306 RTFileSeek(pHandle->file.Handle, 0, RTFILE_SEEK_END, NULL);
1307
1308 for (size_t iSeg = 0; iSeg < SgBuf.cSegs; iSeg++)
1309 {
1310 size_t cbWrittenNow = 0;
1311 do
1312 rc = RTFileWrite(pHandle->file.Handle, SgBuf.paSegs[iSeg].pvSeg,
1313 SgBuf.paSegs[iSeg].cbSeg, &cbWrittenNow);
1314 while (rc == VERR_INTERRUPTED);
1315 if (RT_SUCCESS(rc))
1316 {
1317 cbTotal += cbWrittenNow;
1318 if (cbWrittenNow < SgBuf.paSegs[iSeg].cbSeg)
1319 break;
1320 }
1321 else
1322 {
1323 if (cbTotal > 0)
1324 rc = VINF_SUCCESS;
1325 break;
1326 }
1327 }
1328 }
1329 else
1330#endif
1331 {
1332 rc = RTFileSgWriteAt(pHandle->file.Handle, offFile, &SgBuf, cbToWrite, &cbTotal);
1333 while (rc == VERR_INTERRUPTED)
1334 {
1335 RTSgBufReset(&SgBuf);
1336 rc = RTFileSgWriteAt(pHandle->file.Handle, offFile, &SgBuf, cbToWrite, &cbTotal);
1337 }
1338 }
1339
1340 RTMemTmpFree((void *)SgBuf.paSegs);
1341
1342 /* Update the file offset (mainly for RTFILE_O_APPEND), */
1343 if (RT_SUCCESS(rc))
1344 {
1345 offFile += cbTotal;
1346 if (!(pHandle->file.fOpenFlags & RTFILE_O_APPEND))
1347 *poffFile = offFile;
1348 else
1349 *poffFile = vbsfWriteCalcPostAppendFilePosition(pHandle->file.Handle, offFile);
1350 }
1351 }
1352 else
1353 rc = VERR_NO_TMP_MEMORY;
1354
1355 *pcbWrite = (uint32_t)cbTotal;
1356 }
1357 else
1358 {
1359 /* Writing zero bytes always succeeds. */
1360 rc = VINF_SUCCESS;
1361 }
1362 }
1363 else
1364 *pcbWrite = 0;
1365
1366 LogFunc(("%Rrc bytes written %#zx\n", rc, cbTotal));
1367 return rc;
1368}
1369
1370/**
1371 * Implements SHFL_FN_COPY_FILE_PART (wrapping RTFileCopyPart).
1372 */
1373int vbsfCopyFilePart(SHFLCLIENTDATA *pClient, SHFLROOT idRootSrc, SHFLHANDLE hFileSrc, uint64_t offSrc,
1374 SHFLROOT idRootDst, SHFLHANDLE hFileDst, uint64_t offDst, uint64_t *pcbToCopy, uint32_t fFlags)
1375{
1376 /*
1377 * Validate and translates handles.
1378 */
1379 uint64_t const cbToCopy = *pcbToCopy;
1380 *pcbToCopy = 0;
1381 LogFunc(("pClient %p, idRootSrc %#RX32, hFileSrc %#RX64, offSrc %#RX64, idRootSrc %#RX32, hFileSrc %#RX64, offSrc %#RX64, cbToCopy %#RX64, fFlags %#x\n",
1382 pClient, idRootSrc, hFileSrc, offSrc, idRootDst, hFileDst, offDst, cbToCopy, fFlags));
1383
1384 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1385
1386 uint64_t cbTotal = 0;
1387
1388 SHFLFILEHANDLE *pHandleSrc = vbsfQueryFileHandle(pClient, hFileSrc);
1389 int rc = vbsfCheckHandleAccess(pClient, idRootSrc, pHandleSrc, VBSF_CHECK_ACCESS_READ);
1390 if (RT_SUCCESS(rc))
1391 {
1392 SHFLFILEHANDLE *pHandleDst = vbsfQueryFileHandle(pClient, hFileDst);
1393 rc = vbsfCheckHandleAccess(pClient, idRootDst, pHandleDst, VBSF_CHECK_ACCESS_WRITE);
1394 if (RT_SUCCESS(rc))
1395 {
1396 /*
1397 * Do the job.
1398 */
1399 rc = RTFileCopyPart(pHandleSrc->file.Handle, offSrc, pHandleDst->file.Handle, offDst, cbToCopy, 0, &cbTotal);
1400 *pcbToCopy = cbTotal;
1401 }
1402 }
1403
1404 RT_NOREF(fFlags);
1405 LogFunc(("%Rrc bytes written %#zx\n", rc, cbTotal));
1406 return rc;
1407}
1408
1409#ifdef UNITTEST
1410/** Unit test the SHFL_FN_FLUSH API. Located here as a form of API
1411 * documentation. */
1412void testFlush(RTTEST hTest)
1413{
1414 /* If the number or types of parameters are wrong the API should fail. */
1415 testFlushBadParameters(hTest);
1416 /* Simple opening and flushing of a file. */
1417 testFlushFileSimple(hTest);
1418 /* Add tests as required... */
1419}
1420#endif
1421
1422int vbsfFlush(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
1423{
1424 LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64\n",
1425 pClient, root, Handle));
1426
1427 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1428
1429 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1430 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1431 if (RT_SUCCESS(rc))
1432 { /* likely */ }
1433 else
1434 return rc;
1435
1436 rc = RTFileFlush(pHandle->file.Handle);
1437
1438 LogFunc(("%Rrc\n", rc));
1439 return rc;
1440}
1441
1442#ifdef UNITTEST
1443/** Unit test the SHFL_FN_LIST API. Located here as a form of API
1444 * documentation. */
1445void testDirList(RTTEST hTest)
1446{
1447 /* If the number or types of parameters are wrong the API should fail. */
1448 testDirListBadParameters(hTest);
1449 /* Test listing an empty directory (simple edge case). */
1450 testDirListEmpty(hTest);
1451 /* Add tests as required... */
1452}
1453#endif
1454int vbsfDirList(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, SHFLSTRING *pPath, uint32_t flags,
1455 uint32_t *pcbBuffer, uint8_t *pBuffer, uint32_t *pIndex, uint32_t *pcFiles)
1456{
1457 PRTDIRENTRYEX pDirEntry = 0, pDirEntryOrg;
1458 uint32_t cbDirEntry, cbBufferOrg;
1459 PSHFLDIRINFO pSFDEntry;
1460 PRTUTF16 pwszString;
1461 RTDIR hDir;
1462 const bool fUtf8 = BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8) != 0;
1463
1464 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1465
1466 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
1467 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1468 if (RT_SUCCESS(rc))
1469 { /* likely */ }
1470 else
1471 return rc;
1472
1473 Assert(*pIndex == 0);
1474
1475 cbDirEntry = 4096;
1476 pDirEntryOrg = pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry);
1477 if (pDirEntry == 0)
1478 {
1479 AssertFailed();
1480 return VERR_NO_MEMORY;
1481 }
1482
1483 cbBufferOrg = *pcbBuffer;
1484 *pcbBuffer = 0;
1485 pSFDEntry = (PSHFLDIRINFO)pBuffer;
1486
1487 *pIndex = 1; /* not yet complete */
1488 *pcFiles = 0;
1489
1490 if (!pPath)
1491 hDir = pHandle->dir.Handle;
1492 else
1493 {
1494 if (pHandle->dir.SearchHandle == 0)
1495 {
1496 /* Build a host full path for the given path
1497 * and convert ucs2 to utf8 if necessary.
1498 */
1499 char *pszFullPath = NULL;
1500
1501 Assert(pHandle->dir.pLastValidEntry == 0);
1502
1503 rc = vbsfBuildFullPath(pClient, root, pPath, pPath->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPath, NULL, true);
1504
1505 if (RT_SUCCESS(rc))
1506 {
1507 rc = RTDirOpenFiltered(&pHandle->dir.SearchHandle, pszFullPath, RTDIRFILTER_WINNT, 0 /*fFlags*/);
1508
1509 /* free the path string */
1510 vbsfFreeFullPath(pszFullPath);
1511
1512 if (RT_FAILURE(rc))
1513 goto end;
1514 }
1515 else
1516 goto end;
1517 flags &= ~SHFL_LIST_RESTART;
1518 }
1519 Assert(pHandle->dir.SearchHandle);
1520 hDir = pHandle->dir.SearchHandle;
1521 }
1522
1523 if (flags & SHFL_LIST_RESTART)
1524 {
1525 rc = RTDirRewind(hDir);
1526 if (RT_FAILURE(rc))
1527 goto end;
1528 }
1529
1530 while (cbBufferOrg)
1531 {
1532 size_t cbDirEntrySize = cbDirEntry;
1533 uint32_t cbNeeded;
1534
1535 /* Do we still have a valid last entry for the active search? If so, then return it here */
1536 if (pHandle->dir.pLastValidEntry)
1537 {
1538 pDirEntry = pHandle->dir.pLastValidEntry;
1539 }
1540 else
1541 {
1542 pDirEntry = pDirEntryOrg;
1543
1544 rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
1545 if (rc == VERR_NO_MORE_FILES)
1546 {
1547 *pIndex = 0; /* listing completed */
1548 break;
1549 }
1550
1551 if ( rc != VINF_SUCCESS
1552 && rc != VWRN_NO_DIRENT_INFO)
1553 {
1554 //AssertFailed();
1555 if ( rc == VERR_NO_TRANSLATION
1556 || rc == VERR_INVALID_UTF8_ENCODING)
1557 continue;
1558 break;
1559 }
1560 }
1561
1562 cbNeeded = RT_OFFSETOF(SHFLDIRINFO, name.String);
1563 if (fUtf8)
1564 cbNeeded += pDirEntry->cbName + 1;
1565 else
1566 /* Overestimating, but that's ok */
1567 cbNeeded += (pDirEntry->cbName + 1) * 2;
1568
1569 if (cbBufferOrg < cbNeeded)
1570 {
1571 /* No room, so save this directory entry, or else it's lost forever */
1572 pHandle->dir.pLastValidEntry = pDirEntry;
1573
1574 if (*pcFiles == 0)
1575 {
1576 AssertFailed();
1577 return VINF_BUFFER_OVERFLOW; /* Return directly and don't free pDirEntry */
1578 }
1579 return VINF_SUCCESS; /* Return directly and don't free pDirEntry */
1580 }
1581
1582#ifdef RT_OS_WINDOWS
1583 pDirEntry->Info.Attr.fMode |= 0111;
1584#endif
1585 vbfsCopyFsObjInfoFromIprt(&pSFDEntry->Info, &pDirEntry->Info);
1586 pSFDEntry->cucShortName = 0;
1587
1588 if (fUtf8)
1589 {
1590 void *src, *dst;
1591
1592 src = &pDirEntry->szName[0];
1593 dst = &pSFDEntry->name.String.utf8[0];
1594
1595 memcpy(dst, src, pDirEntry->cbName + 1);
1596
1597 pSFDEntry->name.u16Size = pDirEntry->cbName + 1;
1598 pSFDEntry->name.u16Length = pDirEntry->cbName;
1599 }
1600 else
1601 {
1602 pSFDEntry->name.String.ucs2[0] = 0;
1603 pwszString = pSFDEntry->name.String.ucs2;
1604 int rc2 = RTStrToUtf16Ex(pDirEntry->szName, RTSTR_MAX, &pwszString, pDirEntry->cbName+1, NULL);
1605 AssertRC(rc2);
1606
1607#ifdef RT_OS_DARWIN
1608/** @todo This belongs in rtPathToNative or in the windows shared folder file system driver...
1609 * The question is simply whether the NFD normalization is actually applied on a (virtual) file
1610 * system level in darwin, or just by the user mode application libs. */
1611 {
1612 // Convert to
1613 // Normalization Form C (composed Unicode). We need this because
1614 // Mac OS X file system uses NFD (Normalization Form D :decomposed Unicode)
1615 // while most other OS', server-side programs usually expect NFC.
1616 uint16_t ucs2Length;
1617 CFRange rangeCharacters;
1618 CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, 0);
1619
1620 ::CFStringAppendCharacters(inStr, (UniChar *)pwszString, RTUtf16Len(pwszString));
1621 ::CFStringNormalize(inStr, kCFStringNormalizationFormC);
1622 ucs2Length = ::CFStringGetLength(inStr);
1623
1624 rangeCharacters.location = 0;
1625 rangeCharacters.length = ucs2Length;
1626 ::CFStringGetCharacters(inStr, rangeCharacters, pwszString);
1627 pwszString[ucs2Length] = 0x0000; // NULL terminated
1628
1629 CFRelease(inStr);
1630 }
1631#endif
1632 pSFDEntry->name.u16Length = (uint32_t)RTUtf16Len(pSFDEntry->name.String.ucs2) * 2;
1633 pSFDEntry->name.u16Size = pSFDEntry->name.u16Length + 2;
1634
1635 Log(("SHFL: File name size %d\n", pSFDEntry->name.u16Size));
1636 Log(("SHFL: File name %ls\n", &pSFDEntry->name.String.ucs2));
1637
1638 // adjust cbNeeded (it was overestimated before)
1639 cbNeeded = RT_OFFSETOF(SHFLDIRINFO, name.String) + pSFDEntry->name.u16Size;
1640 }
1641
1642 pSFDEntry = (PSHFLDIRINFO)((uintptr_t)pSFDEntry + cbNeeded);
1643 *pcbBuffer += cbNeeded;
1644 cbBufferOrg-= cbNeeded;
1645
1646 *pcFiles += 1;
1647
1648 /* Free the saved last entry, that we've just returned */
1649 if (pHandle->dir.pLastValidEntry)
1650 {
1651 RTMemFree(pHandle->dir.pLastValidEntry);
1652 pHandle->dir.pLastValidEntry = NULL;
1653
1654 /* And use the newly allocated buffer from now. */
1655 pDirEntry = pDirEntryOrg;
1656 }
1657
1658 if (flags & SHFL_LIST_RETURN_ONE)
1659 break; /* we're done */
1660 }
1661 Assert(rc != VINF_SUCCESS || *pcbBuffer > 0);
1662
1663end:
1664 if (pDirEntry)
1665 RTMemFree(pDirEntry);
1666
1667 return rc;
1668}
1669
1670#ifdef UNITTEST
1671/** Unit test the SHFL_FN_READLINK API. Located here as a form of API
1672 * documentation. */
1673void testReadLink(RTTEST hTest)
1674{
1675 /* If the number or types of parameters are wrong the API should fail. */
1676 testReadLinkBadParameters(hTest);
1677 /* Add tests as required... */
1678}
1679#endif
1680int vbsfReadLink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint8_t *pBuffer, uint32_t cbBuffer)
1681{
1682 int rc = VINF_SUCCESS;
1683
1684 if (pPath == 0 || pBuffer == 0)
1685 {
1686 AssertFailed();
1687 return VERR_INVALID_PARAMETER;
1688 }
1689
1690 /* Build a host full path for the given path, handle file name case issues
1691 * (if the guest expects case-insensitive paths but the host is
1692 * case-sensitive) and convert ucs2 to utf8 if necessary.
1693 */
1694 char *pszFullPath = NULL;
1695 uint32_t cbFullPathRoot = 0;
1696
1697 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot);
1698
1699 if (RT_SUCCESS(rc))
1700 {
1701 rc = RTSymlinkRead(pszFullPath, (char *) pBuffer, cbBuffer, 0);
1702 if (RT_SUCCESS(rc))
1703 {
1704 /* Convert the slashes in the link target to the guest path separator characters. */
1705 /** @todo r=bird: for some messed up reason, we return UTF-8 here rather than
1706 * the character set selected by the client. We also don't return the
1707 * length, so the clients are paranoid about the zero termination behavior. */
1708 char ch;
1709 char *psz = (char *)pBuffer;
1710 while ((ch = *psz) != '\0')
1711 {
1712 if (RTPATH_IS_SLASH(ch))
1713 *psz = pClient->PathDelimiter;
1714 psz++;
1715 }
1716 }
1717
1718 /* free the path string */
1719 vbsfFreeFullPath(pszFullPath);
1720 }
1721
1722 return rc;
1723}
1724
1725int vbsfQueryFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
1726 uint32_t *pcbBuffer, uint8_t *pBuffer)
1727{
1728 RT_NOREF1(flags);
1729 uint32_t type = vbsfQueryHandleType(pClient, Handle);
1730 int rc = VINF_SUCCESS;
1731 SHFLFSOBJINFO *pObjInfo = (SHFLFSOBJINFO *)pBuffer;
1732 RTFSOBJINFO fileinfo;
1733
1734
1735 AssertReturn(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE, VERR_INVALID_PARAMETER);
1736 AssertReturn(pcbBuffer != NULL, VERR_INVALID_PARAMETER);
1737 AssertReturn(pObjInfo != NULL, VERR_INVALID_PARAMETER);
1738 AssertReturn(*pcbBuffer >= sizeof(SHFLFSOBJINFO), VERR_INVALID_PARAMETER);
1739
1740 /** @todo other options */
1741 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_FILE));
1742
1743 *pcbBuffer = 0;
1744
1745 if (type == SHFL_HF_TYPE_DIR)
1746 {
1747 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
1748 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1749 if (RT_SUCCESS(rc))
1750 rc = RTDirQueryInfo(pHandle->dir.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1751 }
1752 else
1753 {
1754 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1755 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1756 if (RT_SUCCESS(rc))
1757 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1758#ifdef RT_OS_WINDOWS
1759 if (RT_SUCCESS(rc) && RTFS_IS_FILE(pObjInfo->Attr.fMode))
1760 pObjInfo->Attr.fMode |= 0111;
1761#endif
1762 }
1763 if (rc == VINF_SUCCESS)
1764 {
1765 vbfsCopyFsObjInfoFromIprt(pObjInfo, &fileinfo);
1766 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1767 }
1768 else
1769 AssertFailed();
1770
1771 return rc;
1772}
1773
1774static int vbsfSetFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
1775 uint32_t *pcbBuffer, uint8_t *pBuffer)
1776{
1777 RT_NOREF1(flags);
1778 uint32_t type = vbsfQueryHandleType(pClient, Handle);
1779 int rc = VINF_SUCCESS;
1780 SHFLFSOBJINFO *pSFDEntry;
1781
1782 if ( !(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE)
1783 || pcbBuffer == 0
1784 || pBuffer == 0
1785 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
1786 {
1787 AssertFailed();
1788 return VERR_INVALID_PARAMETER;
1789 }
1790
1791 *pcbBuffer = 0;
1792 pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
1793
1794 Assert(flags == (SHFL_INFO_SET | SHFL_INFO_FILE));
1795
1796 /* Change only the time values that are not zero */
1797 if (type == SHFL_HF_TYPE_DIR)
1798 {
1799 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
1800 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1801 if (RT_SUCCESS(rc))
1802 rc = RTDirSetTimes(pHandle->dir.Handle,
1803 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1804 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1805 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1806 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1807 );
1808 }
1809 else
1810 {
1811 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1812 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1813 if (RT_SUCCESS(rc))
1814 rc = RTFileSetTimes(pHandle->file.Handle,
1815 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1816 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1817 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1818 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1819 );
1820 }
1821 if (rc != VINF_SUCCESS)
1822 {
1823 Log(("RTFileSetTimes failed with %Rrc\n", rc));
1824 Log(("AccessTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->AccessTime)));
1825 Log(("ModificationTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ModificationTime)));
1826 Log(("ChangeTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ChangeTime)));
1827 Log(("BirthTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->BirthTime)));
1828 /* temporary hack */
1829 rc = VINF_SUCCESS;
1830 }
1831
1832 if (type == SHFL_HF_TYPE_FILE)
1833 {
1834 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1835 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1836 if (RT_SUCCESS(rc))
1837 {
1838 /* Change file attributes if necessary */
1839 if (pSFDEntry->Attr.fMode)
1840 {
1841 RTFMODE fMode = pSFDEntry->Attr.fMode;
1842
1843#ifndef RT_OS_WINDOWS
1844 /* Don't allow the guest to clear the own bit, otherwise the guest wouldn't be
1845 * able to access this file anymore. Only for guests, which set the UNIX mode.
1846 * Also, clear bits which we don't pass through for security reasons. */
1847 if (fMode & RTFS_UNIX_MASK)
1848 {
1849 fMode |= RTFS_UNIX_IRUSR;
1850 fMode &= ~(RTFS_UNIX_ISUID | RTFS_UNIX_ISGID | RTFS_UNIX_ISTXT);
1851 }
1852#endif
1853
1854 rc = RTFileSetMode(pHandle->file.Handle, fMode);
1855 if (rc != VINF_SUCCESS)
1856 {
1857 Log(("RTFileSetMode %x failed with %Rrc\n", fMode, rc));
1858 /* silent failure, because this tends to fail with e.g. windows guest & linux host */
1859 rc = VINF_SUCCESS;
1860 }
1861 }
1862 }
1863 }
1864 /** @todo mode for directories */
1865
1866 if (rc == VINF_SUCCESS)
1867 {
1868 uint32_t bufsize = sizeof(*pSFDEntry);
1869
1870 rc = vbsfQueryFileInfo(pClient, root, Handle, SHFL_INFO_GET|SHFL_INFO_FILE, &bufsize, (uint8_t *)pSFDEntry);
1871 if (rc == VINF_SUCCESS)
1872 {
1873 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1874 }
1875 else
1876 AssertFailed();
1877 }
1878
1879 return rc;
1880}
1881
1882
1883/**
1884 * Handles SHFL_FN_SET_FILE_SIZE.
1885 */
1886int vbsfSetFileSize(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hHandle, uint64_t cbNewSize)
1887{
1888 /*
1889 * Resolve handle and validate write access.
1890 */
1891 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, hHandle);
1892 ASSERT_GUEST_RETURN(pHandle, VERR_INVALID_HANDLE);
1893
1894 int rc = vbsfCheckHandleAccess(pClient, idRoot, pHandle, VBSF_CHECK_ACCESS_WRITE);
1895 if (RT_SUCCESS(rc))
1896 {
1897 /*
1898 * Execute the request.
1899 */
1900 rc = RTFileSetSize(pHandle->file.Handle, cbNewSize);
1901 }
1902 return rc;
1903}
1904
1905
1906static int vbsfSetEndOfFile(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
1907 uint32_t *pcbBuffer, uint8_t *pBuffer)
1908{
1909 RT_NOREF1(flags);
1910 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1911 SHFLFSOBJINFO *pSFDEntry;
1912
1913 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
1914 {
1915 AssertFailed();
1916 return VERR_INVALID_PARAMETER;
1917 }
1918
1919 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1920 if (RT_SUCCESS(rc))
1921 { /* likely */ }
1922 else
1923 return rc;
1924
1925 *pcbBuffer = 0;
1926 pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
1927
1928 if (flags & SHFL_INFO_SIZE)
1929 {
1930 rc = RTFileSetSize(pHandle->file.Handle, pSFDEntry->cbObject);
1931 if (rc != VINF_SUCCESS)
1932 AssertFailed();
1933 }
1934 else
1935 AssertFailed();
1936
1937 if (rc == VINF_SUCCESS)
1938 {
1939 RTFSOBJINFO fileinfo;
1940
1941 /* Query the new object info and return it */
1942 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1943 if (rc == VINF_SUCCESS)
1944 {
1945#ifdef RT_OS_WINDOWS
1946 fileinfo.Attr.fMode |= 0111;
1947#endif
1948 vbfsCopyFsObjInfoFromIprt(pSFDEntry, &fileinfo);
1949 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1950 }
1951 else
1952 AssertFailed();
1953 }
1954
1955 return rc;
1956}
1957
1958int vbsfQueryVolumeInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1959{
1960 RT_NOREF2(root, flags);
1961 int rc = VINF_SUCCESS;
1962 SHFLVOLINFO *pSFDEntry;
1963 char *pszFullPath = NULL;
1964 union
1965 {
1966 SHFLSTRING Dummy;
1967 uint8_t abDummy[SHFLSTRING_HEADER_SIZE + sizeof(RTUTF16)];
1968 } Buf;
1969
1970 if (pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLVOLINFO))
1971 {
1972 AssertFailed();
1973 return VERR_INVALID_PARAMETER;
1974 }
1975
1976 /** @todo other options */
1977 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_VOLUME));
1978
1979 *pcbBuffer = 0;
1980 pSFDEntry = (PSHFLVOLINFO)pBuffer;
1981
1982 ShflStringInitBuffer(&Buf.Dummy, sizeof(Buf));
1983 Buf.Dummy.String.ucs2[0] = '\0';
1984 rc = vbsfBuildFullPath(pClient, root, &Buf.Dummy, sizeof(Buf), &pszFullPath, NULL);
1985
1986 if (RT_SUCCESS(rc))
1987 {
1988 rc = RTFsQuerySizes(pszFullPath, &pSFDEntry->ullTotalAllocationBytes, &pSFDEntry->ullAvailableAllocationBytes, &pSFDEntry->ulBytesPerAllocationUnit, &pSFDEntry->ulBytesPerSector);
1989 if (rc != VINF_SUCCESS)
1990 goto exit;
1991
1992 rc = RTFsQuerySerial(pszFullPath, &pSFDEntry->ulSerial);
1993 if (rc != VINF_SUCCESS)
1994 goto exit;
1995
1996 RTFSPROPERTIES FsProperties;
1997 rc = RTFsQueryProperties(pszFullPath, &FsProperties);
1998 if (rc != VINF_SUCCESS)
1999 goto exit;
2000 vbfsCopyFsPropertiesFromIprt(&pSFDEntry->fsProperties, &FsProperties);
2001
2002 *pcbBuffer = sizeof(SHFLVOLINFO);
2003 }
2004 else AssertFailed();
2005
2006exit:
2007 AssertMsg(rc == VINF_SUCCESS, ("failure: rc = %Rrc\n", rc));
2008 /* free the path string */
2009 vbsfFreeFullPath(pszFullPath);
2010 return rc;
2011}
2012
2013int vbsfQueryFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
2014{
2015 if (pcbBuffer == 0 || pBuffer == 0)
2016 {
2017 AssertFailed();
2018 return VERR_INVALID_PARAMETER;
2019 }
2020
2021 if (flags & SHFL_INFO_FILE)
2022 return vbsfQueryFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2023
2024 if (flags & SHFL_INFO_VOLUME)
2025 return vbsfQueryVolumeInfo(pClient, root, flags, pcbBuffer, pBuffer);
2026
2027 AssertFailed();
2028 return VERR_INVALID_PARAMETER;
2029}
2030
2031#ifdef UNITTEST
2032/** Unit test the SHFL_FN_INFORMATION API. Located here as a form of API
2033 * documentation. */
2034void testFSInfo(RTTEST hTest)
2035{
2036 /* If the number or types of parameters are wrong the API should fail. */
2037 testFSInfoBadParameters(hTest);
2038 /* Basic get and set file size test. */
2039 testFSInfoQuerySetFMode(hTest);
2040 /* Basic get and set dir atime test. */
2041 testFSInfoQuerySetDirATime(hTest);
2042 /* Basic get and set file atime test. */
2043 testFSInfoQuerySetFileATime(hTest);
2044 /* Basic set end of file. */
2045 testFSInfoQuerySetEndOfFile(hTest);
2046 /* Add tests as required... */
2047}
2048#endif
2049int vbsfSetFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
2050{
2051 uint32_t type = vbsfQueryHandleType(pClient, Handle)
2052 & (SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE|SHFL_HF_TYPE_VOLUME);
2053
2054 if (type == 0 || pcbBuffer == 0 || pBuffer == 0)
2055 {
2056 AssertFailed();
2057 return VERR_INVALID_PARAMETER;
2058 }
2059
2060 if (flags & SHFL_INFO_FILE)
2061 return vbsfSetFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2062
2063 if (flags & SHFL_INFO_SIZE)
2064 return vbsfSetEndOfFile(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2065
2066// if (flags & SHFL_INFO_VOLUME)
2067// return vbsfVolumeInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2068 AssertFailed();
2069 return VERR_INVALID_PARAMETER;
2070}
2071
2072#ifdef UNITTEST
2073/** Unit test the SHFL_FN_LOCK API. Located here as a form of API
2074 * documentation. */
2075void testLock(RTTEST hTest)
2076{
2077 /* If the number or types of parameters are wrong the API should fail. */
2078 testLockBadParameters(hTest);
2079 /* Simple file locking and unlocking test. */
2080 testLockFileSimple(hTest);
2081 /* Add tests as required... */
2082}
2083#endif
2084
2085int vbsfLock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
2086{
2087 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
2088 uint32_t fRTLock = 0;
2089
2090 Assert((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL);
2091
2092 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
2093 if (RT_SUCCESS(rc))
2094 { /* likely */ }
2095 else
2096 return rc;
2097
2098 if ( ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL)
2099 || (flags & SHFL_LOCK_ENTIRE)
2100 )
2101 {
2102 AssertFailed();
2103 return VERR_INVALID_PARAMETER;
2104 }
2105
2106 /* Lock type */
2107 switch(flags & SHFL_LOCK_MODE_MASK)
2108 {
2109 case SHFL_LOCK_SHARED:
2110 fRTLock = RTFILE_LOCK_READ;
2111 break;
2112
2113 case SHFL_LOCK_EXCLUSIVE:
2114 fRTLock = RTFILE_LOCK_READ | RTFILE_LOCK_WRITE;
2115 break;
2116
2117 default:
2118 AssertFailed();
2119 return VERR_INVALID_PARAMETER;
2120 }
2121
2122 /* Lock wait type */
2123 if (flags & SHFL_LOCK_WAIT)
2124 fRTLock |= RTFILE_LOCK_WAIT;
2125 else
2126 fRTLock |= RTFILE_LOCK_IMMEDIATELY;
2127
2128#ifdef RT_OS_WINDOWS
2129 rc = RTFileLock(pHandle->file.Handle, fRTLock, offset, length);
2130 if (rc != VINF_SUCCESS)
2131 Log(("RTFileLock %RTfile %RX64 %RX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
2132#else
2133 Log(("vbsfLock: Pretend success handle=%x\n", Handle));
2134 rc = VINF_SUCCESS;
2135 RT_NOREF2(offset, length);
2136#endif
2137 return rc;
2138}
2139
2140int vbsfUnlock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
2141{
2142 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
2143
2144 Assert((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL);
2145
2146 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
2147 if (RT_SUCCESS(rc))
2148 { /* likely */ }
2149 else
2150 return rc;
2151
2152 if ( ((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL)
2153 || (flags & SHFL_LOCK_ENTIRE)
2154 )
2155 {
2156 return VERR_INVALID_PARAMETER;
2157 }
2158
2159#ifdef RT_OS_WINDOWS
2160 rc = RTFileUnlock(pHandle->file.Handle, offset, length);
2161 if (rc != VINF_SUCCESS)
2162 Log(("RTFileUnlock %RTfile %RX64 %RTX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
2163#else
2164 Log(("vbsfUnlock: Pretend success handle=%x\n", Handle));
2165 rc = VINF_SUCCESS;
2166 RT_NOREF2(offset, length);
2167#endif
2168
2169 return rc;
2170}
2171
2172
2173#ifdef UNITTEST
2174/** Unit test the SHFL_FN_REMOVE API. Located here as a form of API
2175 * documentation. */
2176void testRemove(RTTEST hTest)
2177{
2178 /* If the number or types of parameters are wrong the API should fail. */
2179 testRemoveBadParameters(hTest);
2180 /* Add tests as required... */
2181}
2182#endif
2183int vbsfRemove(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint32_t flags)
2184{
2185 int rc = VINF_SUCCESS;
2186
2187 /* Validate input */
2188 if ( flags & ~(SHFL_REMOVE_FILE|SHFL_REMOVE_DIR|SHFL_REMOVE_SYMLINK)
2189 || cbPath == 0
2190 || pPath == 0)
2191 {
2192 AssertFailed();
2193 return VERR_INVALID_PARAMETER;
2194 }
2195
2196 /* Build a host full path for the given path
2197 * and convert ucs2 to utf8 if necessary.
2198 */
2199 char *pszFullPath = NULL;
2200
2201 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, NULL);
2202 if (RT_SUCCESS(rc))
2203 {
2204 /* is the guest allowed to write to this share? */
2205 bool fWritable;
2206 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
2207 if (RT_FAILURE(rc) || !fWritable)
2208 rc = VERR_WRITE_PROTECT;
2209
2210 if (RT_SUCCESS(rc))
2211 {
2212 if (flags & SHFL_REMOVE_SYMLINK)
2213 rc = RTSymlinkDelete(pszFullPath, 0);
2214 else if (flags & SHFL_REMOVE_FILE)
2215 rc = RTFileDelete(pszFullPath);
2216 else
2217 rc = RTDirRemove(pszFullPath);
2218 }
2219
2220#ifndef DEBUG_dmik
2221 // VERR_ACCESS_DENIED for example?
2222 // Assert(rc == VINF_SUCCESS || rc == VERR_DIR_NOT_EMPTY);
2223#endif
2224 /* free the path string */
2225 vbsfFreeFullPath(pszFullPath);
2226 }
2227 return rc;
2228}
2229
2230
2231#ifdef UNITTEST
2232/** Unit test the SHFL_FN_RENAME API. Located here as a form of API
2233 * documentation. */
2234void testRename(RTTEST hTest)
2235{
2236 /* If the number or types of parameters are wrong the API should fail. */
2237 testRenameBadParameters(hTest);
2238 /* Add tests as required... */
2239}
2240#endif
2241int vbsfRename(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pSrc, SHFLSTRING *pDest, uint32_t flags)
2242{
2243 int rc = VINF_SUCCESS;
2244
2245 /* Validate input */
2246 if ( flags & ~(SHFL_RENAME_FILE|SHFL_RENAME_DIR|SHFL_RENAME_REPLACE_IF_EXISTS)
2247 || pSrc == 0
2248 || pDest == 0)
2249 {
2250 AssertFailed();
2251 return VERR_INVALID_PARAMETER;
2252 }
2253
2254 /* Build a host full path for the given path
2255 * and convert ucs2 to utf8 if necessary.
2256 */
2257 char *pszFullPathSrc = NULL;
2258 char *pszFullPathDest = NULL;
2259
2260 rc = vbsfBuildFullPath(pClient, root, pSrc, pSrc->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPathSrc, NULL);
2261 if (rc != VINF_SUCCESS)
2262 return rc;
2263
2264 rc = vbsfBuildFullPath(pClient, root, pDest, pDest->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPathDest, NULL, false, true);
2265 if (RT_SUCCESS (rc))
2266 {
2267 Log(("Rename %s to %s\n", pszFullPathSrc, pszFullPathDest));
2268
2269 /* is the guest allowed to write to this share? */
2270 bool fWritable;
2271 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
2272 if (RT_FAILURE(rc) || !fWritable)
2273 rc = VERR_WRITE_PROTECT;
2274
2275 if (RT_SUCCESS(rc))
2276 {
2277 if ((flags & (SHFL_RENAME_FILE | SHFL_RENAME_DIR)) == (SHFL_RENAME_FILE | SHFL_RENAME_DIR))
2278 {
2279 rc = RTPathRename(pszFullPathSrc, pszFullPathDest,
2280 flags & SHFL_RENAME_REPLACE_IF_EXISTS ? RTPATHRENAME_FLAGS_REPLACE : 0);
2281 }
2282 else if (flags & SHFL_RENAME_FILE)
2283 {
2284 rc = RTFileMove(pszFullPathSrc, pszFullPathDest,
2285 ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTFILEMOVE_FLAGS_REPLACE : 0));
2286 }
2287 else
2288 {
2289 /* NT ignores the REPLACE flag and simply return and already exists error. */
2290 rc = RTDirRename(pszFullPathSrc, pszFullPathDest,
2291 ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTPATHRENAME_FLAGS_REPLACE : 0));
2292 }
2293 }
2294
2295 /* free the path string */
2296 vbsfFreeFullPath(pszFullPathDest);
2297 }
2298 /* free the path string */
2299 vbsfFreeFullPath(pszFullPathSrc);
2300 return rc;
2301}
2302
2303/**
2304 * Implements SHFL_FN_COPY_FILE (wrapping RTFileCopy).
2305 */
2306int vbsfCopyFile(SHFLCLIENTDATA *pClient, SHFLROOT idRootSrc, PCSHFLSTRING pStrPathSrc,
2307 SHFLROOT idRootDst, PCSHFLSTRING pStrPathDst, uint32_t fFlags)
2308{
2309 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
2310 if (pClient->fu32Flags & SHFL_CF_UTF8)
2311 LogFunc(("pClient %p, idRootSrc %#RX32, '%.*s', idRootSrc %#RX32, '%.*s', fFlags %#x\n", pClient, idRootSrc,
2312 pStrPathSrc->u16Length, pStrPathSrc->String.ach, idRootDst, pStrPathDst->u16Length, pStrPathDst->String.ach, fFlags));
2313 else
2314 LogFunc(("pClient %p, idRootSrc %#RX32, '%.*ls', idRootSrc %#RX32, '%.*ls', fFlags %#x\n", pClient,
2315 idRootSrc, pStrPathSrc->u16Length / sizeof(RTUTF16), pStrPathSrc->String.ach,
2316 idRootDst, pStrPathDst->u16Length / sizeof(RTUTF16), pStrPathDst->String.ach, fFlags));
2317
2318 /*
2319 * Build host paths.
2320 */
2321 char *pszPathSrc = NULL;
2322 int rc = vbsfBuildFullPath(pClient, idRootSrc, pStrPathSrc, pStrPathSrc->u16Size + SHFLSTRING_HEADER_SIZE, &pszPathSrc, NULL);
2323 if (RT_SUCCESS(rc))
2324 {
2325 char *pszPathDst = NULL;
2326 rc = vbsfBuildFullPath(pClient, idRootDst, pStrPathDst, pStrPathDst->u16Size + SHFLSTRING_HEADER_SIZE, &pszPathDst, NULL);
2327 if (RT_SUCCESS(rc))
2328 {
2329 /*
2330 * Do the job.
2331 */
2332 rc = RTFileCopy(pszPathSrc, pszPathDst);
2333
2334 vbsfFreeFullPath(pszPathDst);
2335 }
2336 vbsfFreeFullPath(pszPathSrc);
2337 }
2338
2339 RT_NOREF(fFlags);
2340 LogFunc(("returns %Rrc\n", rc));
2341 return rc;
2342}
2343
2344#ifdef UNITTEST
2345/** Unit test the SHFL_FN_SYMLINK API. Located here as a form of API
2346 * documentation. */
2347void testSymlink(RTTEST hTest)
2348{
2349 /* If the number or types of parameters are wrong the API should fail. */
2350 testSymlinkBadParameters(hTest);
2351 /* Add tests as required... */
2352}
2353#endif
2354int vbsfSymlink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pNewPath, SHFLSTRING *pOldPath, SHFLFSOBJINFO *pInfo)
2355{
2356 int rc = VINF_SUCCESS;
2357
2358 char *pszFullNewPath = NULL;
2359 char *pszFullOldPath = NULL;
2360
2361 /* XXX: no support for UCS2 at the moment. */
2362 if (!BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
2363 return VERR_NOT_IMPLEMENTED;
2364
2365 bool fSymlinksCreate;
2366 rc = vbsfMappingsQuerySymlinksCreate(pClient, root, &fSymlinksCreate);
2367 AssertRCReturn(rc, rc);
2368 if (!fSymlinksCreate)
2369 return VERR_WRITE_PROTECT; /* XXX or VERR_TOO_MANY_SYMLINKS? */
2370
2371 rc = vbsfBuildFullPath(pClient, root, pNewPath, pNewPath->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullNewPath, NULL);
2372 AssertRCReturn(rc, rc);
2373
2374 /* Verify that the link target can be a valid host path, i.e. does not contain invalid characters. */
2375 uint32_t fu32PathFlags = 0;
2376 uint32_t fu32Options = 0;
2377 rc = vbsfPathGuestToHost(pClient, root, pOldPath, pOldPath->u16Size + SHFLSTRING_HEADER_SIZE,
2378 &pszFullOldPath, NULL, fu32Options, &fu32PathFlags);
2379 if (RT_FAILURE(rc))
2380 {
2381 vbsfFreeFullPath(pszFullNewPath);
2382 return rc;
2383 }
2384
2385 /** @todo r=bird: We _must_ perform slash conversion on the target (what this
2386 * code calls 'pOldPath' for some peculiar reason)! */
2387
2388 rc = RTSymlinkCreate(pszFullNewPath, (const char *)pOldPath->String.utf8,
2389 RTSYMLINKTYPE_UNKNOWN, 0);
2390 if (RT_SUCCESS(rc))
2391 {
2392 RTFSOBJINFO info;
2393 rc = RTPathQueryInfoEx(pszFullNewPath, &info, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
2394 if (RT_SUCCESS(rc))
2395 vbfsCopyFsObjInfoFromIprt(pInfo, &info);
2396 }
2397
2398 vbsfFreeFullPath(pszFullOldPath);
2399 vbsfFreeFullPath(pszFullNewPath);
2400
2401 return rc;
2402}
2403
2404/*
2405 * Clean up our mess by freeing all handles that are still valid.
2406 *
2407 */
2408int vbsfDisconnect(SHFLCLIENTDATA *pClient)
2409{
2410 for (int i = 0; i < SHFLHANDLE_MAX; ++i)
2411 {
2412 SHFLFILEHANDLE *pHandle = NULL;
2413 SHFLHANDLE Handle = (SHFLHANDLE)i;
2414
2415 uint32_t type = vbsfQueryHandleType(pClient, Handle);
2416 switch (type & (SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE))
2417 {
2418 case SHFL_HF_TYPE_DIR:
2419 {
2420 pHandle = vbsfQueryDirHandle(pClient, Handle);
2421 break;
2422 }
2423 case SHFL_HF_TYPE_FILE:
2424 {
2425 pHandle = vbsfQueryFileHandle(pClient, Handle);
2426 break;
2427 }
2428 default:
2429 break;
2430 }
2431
2432 if (pHandle)
2433 {
2434 LogFunc(("Opened handle 0x%08x\n", i));
2435 vbsfClose(pClient, pHandle->root, Handle);
2436 }
2437 }
2438
2439 for (uint32_t i = 0; i < RT_ELEMENTS(pClient->acMappings); i++)
2440 if (pClient->acMappings[i])
2441 {
2442 uint16_t cMappings = pClient->acMappings[i];
2443 while (cMappings-- > 0)
2444 vbsfUnmapFolder(pClient, i);
2445 }
2446
2447 return VINF_SUCCESS;
2448}
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