VirtualBox

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

Last change on this file since 77847 was 77838, checked in by vboxsync, 6 years ago

SharedFoldersSvc: Added new function for server-side file content copying (SHFL_FN_COPY_FILE_PART) and a function for query host features and supported functions (SHFL_FN_QUERY_FEATURES). [build fix] bugref:9172

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 78.9 KB
Line 
1/* $Id: vbsf.cpp 77838 2019-03-21 21:50:38Z 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, 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/** Implements SHFL_FN_COPY_FILE_PART (wrapping RTFileCopyPart). */
1371int vbsfCopyFilePart(SHFLCLIENTDATA *pClient, SHFLROOT idRootSrc, SHFLHANDLE hFileSrc, uint64_t offSrc,
1372 SHFLROOT idRootDst, SHFLHANDLE hFileDst, uint64_t offDst, uint64_t *pcbToCopy, uint32_t fFlags)
1373{
1374 /*
1375 * Validate and translates handles.
1376 */
1377 uint64_t const cbToCopy = *pcbToCopy;
1378 *pcbToCopy = 0;
1379 LogFunc(("pClient %p, idRootSrc %#RX32, hFileSrc %#RX64, offSrc %#RX64, idRootSrc %#RX32, hFileSrc %#RX64, offSrc %#RX64, cbToCopy %#RX64, fFlags %#x\n",
1380 pClient, idRootSrc, hFileSrc, offSrc, idRootDst, hFileDst, offDst, cbToCopy, fFlags));
1381
1382 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1383
1384 uint64_t cbTotal = 0;
1385
1386 SHFLFILEHANDLE *pHandleSrc = vbsfQueryFileHandle(pClient, hFileSrc);
1387 int rc = vbsfCheckHandleAccess(pClient, idRootSrc, pHandleSrc, VBSF_CHECK_ACCESS_READ);
1388 if (RT_SUCCESS(rc))
1389 {
1390 SHFLFILEHANDLE *pHandleDst = vbsfQueryFileHandle(pClient, hFileDst);
1391 rc = vbsfCheckHandleAccess(pClient, idRootDst, pHandleDst, VBSF_CHECK_ACCESS_WRITE);
1392 if (RT_SUCCESS(rc))
1393 {
1394 rc = RTFileCopyPart(pHandleSrc->file.Handle, offSrc, pHandleDst->file.Handle, offDst, cbToCopy, 0, &cbTotal);
1395 *pcbToCopy = cbTotal;
1396 }
1397 }
1398
1399 RT_NOREF(fFlags);
1400 LogFunc(("%Rrc bytes written %#zx\n", rc, cbTotal));
1401 return rc;
1402}
1403
1404#ifdef UNITTEST
1405/** Unit test the SHFL_FN_FLUSH API. Located here as a form of API
1406 * documentation. */
1407void testFlush(RTTEST hTest)
1408{
1409 /* If the number or types of parameters are wrong the API should fail. */
1410 testFlushBadParameters(hTest);
1411 /* Simple opening and flushing of a file. */
1412 testFlushFileSimple(hTest);
1413 /* Add tests as required... */
1414}
1415#endif
1416
1417int vbsfFlush(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
1418{
1419 LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64\n",
1420 pClient, root, Handle));
1421
1422 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1423
1424 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1425 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1426 if (RT_SUCCESS(rc))
1427 { /* likely */ }
1428 else
1429 return rc;
1430
1431 rc = RTFileFlush(pHandle->file.Handle);
1432
1433 LogFunc(("%Rrc\n", rc));
1434 return rc;
1435}
1436
1437#ifdef UNITTEST
1438/** Unit test the SHFL_FN_LIST API. Located here as a form of API
1439 * documentation. */
1440void testDirList(RTTEST hTest)
1441{
1442 /* If the number or types of parameters are wrong the API should fail. */
1443 testDirListBadParameters(hTest);
1444 /* Test listing an empty directory (simple edge case). */
1445 testDirListEmpty(hTest);
1446 /* Add tests as required... */
1447}
1448#endif
1449int vbsfDirList(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, SHFLSTRING *pPath, uint32_t flags,
1450 uint32_t *pcbBuffer, uint8_t *pBuffer, uint32_t *pIndex, uint32_t *pcFiles)
1451{
1452 PRTDIRENTRYEX pDirEntry = 0, pDirEntryOrg;
1453 uint32_t cbDirEntry, cbBufferOrg;
1454 PSHFLDIRINFO pSFDEntry;
1455 PRTUTF16 pwszString;
1456 RTDIR hDir;
1457 const bool fUtf8 = BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8) != 0;
1458
1459 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1460
1461 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
1462 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1463 if (RT_SUCCESS(rc))
1464 { /* likely */ }
1465 else
1466 return rc;
1467
1468 Assert(*pIndex == 0);
1469
1470 cbDirEntry = 4096;
1471 pDirEntryOrg = pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry);
1472 if (pDirEntry == 0)
1473 {
1474 AssertFailed();
1475 return VERR_NO_MEMORY;
1476 }
1477
1478 cbBufferOrg = *pcbBuffer;
1479 *pcbBuffer = 0;
1480 pSFDEntry = (PSHFLDIRINFO)pBuffer;
1481
1482 *pIndex = 1; /* not yet complete */
1483 *pcFiles = 0;
1484
1485 if (!pPath)
1486 hDir = pHandle->dir.Handle;
1487 else
1488 {
1489 if (pHandle->dir.SearchHandle == 0)
1490 {
1491 /* Build a host full path for the given path
1492 * and convert ucs2 to utf8 if necessary.
1493 */
1494 char *pszFullPath = NULL;
1495
1496 Assert(pHandle->dir.pLastValidEntry == 0);
1497
1498 rc = vbsfBuildFullPath(pClient, root, pPath, pPath->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPath, NULL, true);
1499
1500 if (RT_SUCCESS(rc))
1501 {
1502 rc = RTDirOpenFiltered(&pHandle->dir.SearchHandle, pszFullPath, RTDIRFILTER_WINNT, 0 /*fFlags*/);
1503
1504 /* free the path string */
1505 vbsfFreeFullPath(pszFullPath);
1506
1507 if (RT_FAILURE(rc))
1508 goto end;
1509 }
1510 else
1511 goto end;
1512 flags &= ~SHFL_LIST_RESTART;
1513 }
1514 Assert(pHandle->dir.SearchHandle);
1515 hDir = pHandle->dir.SearchHandle;
1516 }
1517
1518 if (flags & SHFL_LIST_RESTART)
1519 {
1520 rc = RTDirRewind(hDir);
1521 if (RT_FAILURE(rc))
1522 goto end;
1523 }
1524
1525 while (cbBufferOrg)
1526 {
1527 size_t cbDirEntrySize = cbDirEntry;
1528 uint32_t cbNeeded;
1529
1530 /* Do we still have a valid last entry for the active search? If so, then return it here */
1531 if (pHandle->dir.pLastValidEntry)
1532 {
1533 pDirEntry = pHandle->dir.pLastValidEntry;
1534 }
1535 else
1536 {
1537 pDirEntry = pDirEntryOrg;
1538
1539 rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
1540 if (rc == VERR_NO_MORE_FILES)
1541 {
1542 *pIndex = 0; /* listing completed */
1543 break;
1544 }
1545
1546 if ( rc != VINF_SUCCESS
1547 && rc != VWRN_NO_DIRENT_INFO)
1548 {
1549 //AssertFailed();
1550 if ( rc == VERR_NO_TRANSLATION
1551 || rc == VERR_INVALID_UTF8_ENCODING)
1552 continue;
1553 break;
1554 }
1555 }
1556
1557 cbNeeded = RT_OFFSETOF(SHFLDIRINFO, name.String);
1558 if (fUtf8)
1559 cbNeeded += pDirEntry->cbName + 1;
1560 else
1561 /* Overestimating, but that's ok */
1562 cbNeeded += (pDirEntry->cbName + 1) * 2;
1563
1564 if (cbBufferOrg < cbNeeded)
1565 {
1566 /* No room, so save this directory entry, or else it's lost forever */
1567 pHandle->dir.pLastValidEntry = pDirEntry;
1568
1569 if (*pcFiles == 0)
1570 {
1571 AssertFailed();
1572 return VINF_BUFFER_OVERFLOW; /* Return directly and don't free pDirEntry */
1573 }
1574 return VINF_SUCCESS; /* Return directly and don't free pDirEntry */
1575 }
1576
1577#ifdef RT_OS_WINDOWS
1578 pDirEntry->Info.Attr.fMode |= 0111;
1579#endif
1580 vbfsCopyFsObjInfoFromIprt(&pSFDEntry->Info, &pDirEntry->Info);
1581 pSFDEntry->cucShortName = 0;
1582
1583 if (fUtf8)
1584 {
1585 void *src, *dst;
1586
1587 src = &pDirEntry->szName[0];
1588 dst = &pSFDEntry->name.String.utf8[0];
1589
1590 memcpy(dst, src, pDirEntry->cbName + 1);
1591
1592 pSFDEntry->name.u16Size = pDirEntry->cbName + 1;
1593 pSFDEntry->name.u16Length = pDirEntry->cbName;
1594 }
1595 else
1596 {
1597 pSFDEntry->name.String.ucs2[0] = 0;
1598 pwszString = pSFDEntry->name.String.ucs2;
1599 int rc2 = RTStrToUtf16Ex(pDirEntry->szName, RTSTR_MAX, &pwszString, pDirEntry->cbName+1, NULL);
1600 AssertRC(rc2);
1601
1602#ifdef RT_OS_DARWIN
1603/** @todo This belongs in rtPathToNative or in the windows shared folder file system driver...
1604 * The question is simply whether the NFD normalization is actually applied on a (virtual) file
1605 * system level in darwin, or just by the user mode application libs. */
1606 {
1607 // Convert to
1608 // Normalization Form C (composed Unicode). We need this because
1609 // Mac OS X file system uses NFD (Normalization Form D :decomposed Unicode)
1610 // while most other OS', server-side programs usually expect NFC.
1611 uint16_t ucs2Length;
1612 CFRange rangeCharacters;
1613 CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, 0);
1614
1615 ::CFStringAppendCharacters(inStr, (UniChar *)pwszString, RTUtf16Len(pwszString));
1616 ::CFStringNormalize(inStr, kCFStringNormalizationFormC);
1617 ucs2Length = ::CFStringGetLength(inStr);
1618
1619 rangeCharacters.location = 0;
1620 rangeCharacters.length = ucs2Length;
1621 ::CFStringGetCharacters(inStr, rangeCharacters, pwszString);
1622 pwszString[ucs2Length] = 0x0000; // NULL terminated
1623
1624 CFRelease(inStr);
1625 }
1626#endif
1627 pSFDEntry->name.u16Length = (uint32_t)RTUtf16Len(pSFDEntry->name.String.ucs2) * 2;
1628 pSFDEntry->name.u16Size = pSFDEntry->name.u16Length + 2;
1629
1630 Log(("SHFL: File name size %d\n", pSFDEntry->name.u16Size));
1631 Log(("SHFL: File name %ls\n", &pSFDEntry->name.String.ucs2));
1632
1633 // adjust cbNeeded (it was overestimated before)
1634 cbNeeded = RT_OFFSETOF(SHFLDIRINFO, name.String) + pSFDEntry->name.u16Size;
1635 }
1636
1637 pSFDEntry = (PSHFLDIRINFO)((uintptr_t)pSFDEntry + cbNeeded);
1638 *pcbBuffer += cbNeeded;
1639 cbBufferOrg-= cbNeeded;
1640
1641 *pcFiles += 1;
1642
1643 /* Free the saved last entry, that we've just returned */
1644 if (pHandle->dir.pLastValidEntry)
1645 {
1646 RTMemFree(pHandle->dir.pLastValidEntry);
1647 pHandle->dir.pLastValidEntry = NULL;
1648
1649 /* And use the newly allocated buffer from now. */
1650 pDirEntry = pDirEntryOrg;
1651 }
1652
1653 if (flags & SHFL_LIST_RETURN_ONE)
1654 break; /* we're done */
1655 }
1656 Assert(rc != VINF_SUCCESS || *pcbBuffer > 0);
1657
1658end:
1659 if (pDirEntry)
1660 RTMemFree(pDirEntry);
1661
1662 return rc;
1663}
1664
1665#ifdef UNITTEST
1666/** Unit test the SHFL_FN_READLINK API. Located here as a form of API
1667 * documentation. */
1668void testReadLink(RTTEST hTest)
1669{
1670 /* If the number or types of parameters are wrong the API should fail. */
1671 testReadLinkBadParameters(hTest);
1672 /* Add tests as required... */
1673}
1674#endif
1675int vbsfReadLink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint8_t *pBuffer, uint32_t cbBuffer)
1676{
1677 int rc = VINF_SUCCESS;
1678
1679 if (pPath == 0 || pBuffer == 0)
1680 {
1681 AssertFailed();
1682 return VERR_INVALID_PARAMETER;
1683 }
1684
1685 /* Build a host full path for the given path, handle file name case issues
1686 * (if the guest expects case-insensitive paths but the host is
1687 * case-sensitive) and convert ucs2 to utf8 if necessary.
1688 */
1689 char *pszFullPath = NULL;
1690 uint32_t cbFullPathRoot = 0;
1691
1692 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot);
1693
1694 if (RT_SUCCESS(rc))
1695 {
1696 rc = RTSymlinkRead(pszFullPath, (char *) pBuffer, cbBuffer, 0);
1697 if (RT_SUCCESS(rc))
1698 {
1699 /* Convert the slashes in the link target to the guest path separator characters. */
1700 char *psz = (char *)pBuffer;
1701 while (*psz != '\0')
1702 {
1703 if (*psz == RTPATH_DELIMITER)
1704 *psz = pClient->PathDelimiter;
1705 psz++;
1706 }
1707 }
1708
1709 /* free the path string */
1710 vbsfFreeFullPath(pszFullPath);
1711 }
1712
1713 return rc;
1714}
1715
1716int vbsfQueryFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
1717 uint32_t *pcbBuffer, uint8_t *pBuffer)
1718{
1719 RT_NOREF1(flags);
1720 uint32_t type = vbsfQueryHandleType(pClient, Handle);
1721 int rc = VINF_SUCCESS;
1722 SHFLFSOBJINFO *pObjInfo = (SHFLFSOBJINFO *)pBuffer;
1723 RTFSOBJINFO fileinfo;
1724
1725
1726 AssertReturn(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE, VERR_INVALID_PARAMETER);
1727 AssertReturn(pcbBuffer != NULL, VERR_INVALID_PARAMETER);
1728 AssertReturn(pObjInfo != NULL, VERR_INVALID_PARAMETER);
1729 AssertReturn(*pcbBuffer >= sizeof(SHFLFSOBJINFO), VERR_INVALID_PARAMETER);
1730
1731 /** @todo other options */
1732 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_FILE));
1733
1734 *pcbBuffer = 0;
1735
1736 if (type == SHFL_HF_TYPE_DIR)
1737 {
1738 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
1739 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1740 if (RT_SUCCESS(rc))
1741 rc = RTDirQueryInfo(pHandle->dir.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1742 }
1743 else
1744 {
1745 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1746 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1747 if (RT_SUCCESS(rc))
1748 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1749#ifdef RT_OS_WINDOWS
1750 if (RT_SUCCESS(rc) && RTFS_IS_FILE(pObjInfo->Attr.fMode))
1751 pObjInfo->Attr.fMode |= 0111;
1752#endif
1753 }
1754 if (rc == VINF_SUCCESS)
1755 {
1756 vbfsCopyFsObjInfoFromIprt(pObjInfo, &fileinfo);
1757 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1758 }
1759 else
1760 AssertFailed();
1761
1762 return rc;
1763}
1764
1765static int vbsfSetFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
1766 uint32_t *pcbBuffer, uint8_t *pBuffer)
1767{
1768 RT_NOREF1(flags);
1769 uint32_t type = vbsfQueryHandleType(pClient, Handle);
1770 int rc = VINF_SUCCESS;
1771 SHFLFSOBJINFO *pSFDEntry;
1772
1773 if ( !(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE)
1774 || pcbBuffer == 0
1775 || pBuffer == 0
1776 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
1777 {
1778 AssertFailed();
1779 return VERR_INVALID_PARAMETER;
1780 }
1781
1782 *pcbBuffer = 0;
1783 pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
1784
1785 Assert(flags == (SHFL_INFO_SET | SHFL_INFO_FILE));
1786
1787 /* Change only the time values that are not zero */
1788 if (type == SHFL_HF_TYPE_DIR)
1789 {
1790 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
1791 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1792 if (RT_SUCCESS(rc))
1793 rc = RTDirSetTimes(pHandle->dir.Handle,
1794 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1795 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1796 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1797 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1798 );
1799 }
1800 else
1801 {
1802 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1803 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1804 if (RT_SUCCESS(rc))
1805 rc = RTFileSetTimes(pHandle->file.Handle,
1806 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1807 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1808 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1809 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1810 );
1811 }
1812 if (rc != VINF_SUCCESS)
1813 {
1814 Log(("RTFileSetTimes failed with %Rrc\n", rc));
1815 Log(("AccessTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->AccessTime)));
1816 Log(("ModificationTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ModificationTime)));
1817 Log(("ChangeTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ChangeTime)));
1818 Log(("BirthTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->BirthTime)));
1819 /* temporary hack */
1820 rc = VINF_SUCCESS;
1821 }
1822
1823 if (type == SHFL_HF_TYPE_FILE)
1824 {
1825 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1826 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1827 if (RT_SUCCESS(rc))
1828 {
1829 /* Change file attributes if necessary */
1830 if (pSFDEntry->Attr.fMode)
1831 {
1832 RTFMODE fMode = pSFDEntry->Attr.fMode;
1833
1834#ifndef RT_OS_WINDOWS
1835 /* Don't allow the guest to clear the own bit, otherwise the guest wouldn't be
1836 * able to access this file anymore. Only for guests, which set the UNIX mode.
1837 * Also, clear bits which we don't pass through for security reasons. */
1838 if (fMode & RTFS_UNIX_MASK)
1839 {
1840 fMode |= RTFS_UNIX_IRUSR;
1841 fMode &= ~(RTFS_UNIX_ISUID | RTFS_UNIX_ISGID | RTFS_UNIX_ISTXT);
1842 }
1843#endif
1844
1845 rc = RTFileSetMode(pHandle->file.Handle, fMode);
1846 if (rc != VINF_SUCCESS)
1847 {
1848 Log(("RTFileSetMode %x failed with %Rrc\n", fMode, rc));
1849 /* silent failure, because this tends to fail with e.g. windows guest & linux host */
1850 rc = VINF_SUCCESS;
1851 }
1852 }
1853 }
1854 }
1855 /** @todo mode for directories */
1856
1857 if (rc == VINF_SUCCESS)
1858 {
1859 uint32_t bufsize = sizeof(*pSFDEntry);
1860
1861 rc = vbsfQueryFileInfo(pClient, root, Handle, SHFL_INFO_GET|SHFL_INFO_FILE, &bufsize, (uint8_t *)pSFDEntry);
1862 if (rc == VINF_SUCCESS)
1863 {
1864 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1865 }
1866 else
1867 AssertFailed();
1868 }
1869
1870 return rc;
1871}
1872
1873
1874/**
1875 * Handles SHFL_FN_SET_FILE_SIZE.
1876 */
1877int vbsfSetFileSize(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hHandle, uint64_t cbNewSize)
1878{
1879 /*
1880 * Resolve handle and validate write access.
1881 */
1882 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, hHandle);
1883 ASSERT_GUEST_RETURN(pHandle, VERR_INVALID_HANDLE);
1884
1885 int rc = vbsfCheckHandleAccess(pClient, idRoot, pHandle, VBSF_CHECK_ACCESS_WRITE);
1886 if (RT_SUCCESS(rc))
1887 {
1888 /*
1889 * Execute the request.
1890 */
1891 rc = RTFileSetSize(pHandle->file.Handle, cbNewSize);
1892 }
1893 return rc;
1894}
1895
1896
1897static int vbsfSetEndOfFile(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
1898 uint32_t *pcbBuffer, uint8_t *pBuffer)
1899{
1900 RT_NOREF1(flags);
1901 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1902 SHFLFSOBJINFO *pSFDEntry;
1903
1904 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
1905 {
1906 AssertFailed();
1907 return VERR_INVALID_PARAMETER;
1908 }
1909
1910 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1911 if (RT_SUCCESS(rc))
1912 { /* likely */ }
1913 else
1914 return rc;
1915
1916 *pcbBuffer = 0;
1917 pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
1918
1919 if (flags & SHFL_INFO_SIZE)
1920 {
1921 rc = RTFileSetSize(pHandle->file.Handle, pSFDEntry->cbObject);
1922 if (rc != VINF_SUCCESS)
1923 AssertFailed();
1924 }
1925 else
1926 AssertFailed();
1927
1928 if (rc == VINF_SUCCESS)
1929 {
1930 RTFSOBJINFO fileinfo;
1931
1932 /* Query the new object info and return it */
1933 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1934 if (rc == VINF_SUCCESS)
1935 {
1936#ifdef RT_OS_WINDOWS
1937 fileinfo.Attr.fMode |= 0111;
1938#endif
1939 vbfsCopyFsObjInfoFromIprt(pSFDEntry, &fileinfo);
1940 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1941 }
1942 else
1943 AssertFailed();
1944 }
1945
1946 return rc;
1947}
1948
1949int vbsfQueryVolumeInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1950{
1951 RT_NOREF2(root, flags);
1952 int rc = VINF_SUCCESS;
1953 SHFLVOLINFO *pSFDEntry;
1954 char *pszFullPath = NULL;
1955 union
1956 {
1957 SHFLSTRING Dummy;
1958 uint8_t abDummy[SHFLSTRING_HEADER_SIZE + sizeof(RTUTF16)];
1959 } Buf;
1960
1961 if (pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLVOLINFO))
1962 {
1963 AssertFailed();
1964 return VERR_INVALID_PARAMETER;
1965 }
1966
1967 /** @todo other options */
1968 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_VOLUME));
1969
1970 *pcbBuffer = 0;
1971 pSFDEntry = (PSHFLVOLINFO)pBuffer;
1972
1973 ShflStringInitBuffer(&Buf.Dummy, sizeof(Buf));
1974 Buf.Dummy.String.ucs2[0] = '\0';
1975 rc = vbsfBuildFullPath(pClient, root, &Buf.Dummy, sizeof(Buf), &pszFullPath, NULL);
1976
1977 if (RT_SUCCESS(rc))
1978 {
1979 rc = RTFsQuerySizes(pszFullPath, &pSFDEntry->ullTotalAllocationBytes, &pSFDEntry->ullAvailableAllocationBytes, &pSFDEntry->ulBytesPerAllocationUnit, &pSFDEntry->ulBytesPerSector);
1980 if (rc != VINF_SUCCESS)
1981 goto exit;
1982
1983 rc = RTFsQuerySerial(pszFullPath, &pSFDEntry->ulSerial);
1984 if (rc != VINF_SUCCESS)
1985 goto exit;
1986
1987 RTFSPROPERTIES FsProperties;
1988 rc = RTFsQueryProperties(pszFullPath, &FsProperties);
1989 if (rc != VINF_SUCCESS)
1990 goto exit;
1991 vbfsCopyFsPropertiesFromIprt(&pSFDEntry->fsProperties, &FsProperties);
1992
1993 *pcbBuffer = sizeof(SHFLVOLINFO);
1994 }
1995 else AssertFailed();
1996
1997exit:
1998 AssertMsg(rc == VINF_SUCCESS, ("failure: rc = %Rrc\n", rc));
1999 /* free the path string */
2000 vbsfFreeFullPath(pszFullPath);
2001 return rc;
2002}
2003
2004int vbsfQueryFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
2005{
2006 if (pcbBuffer == 0 || pBuffer == 0)
2007 {
2008 AssertFailed();
2009 return VERR_INVALID_PARAMETER;
2010 }
2011
2012 if (flags & SHFL_INFO_FILE)
2013 return vbsfQueryFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2014
2015 if (flags & SHFL_INFO_VOLUME)
2016 return vbsfQueryVolumeInfo(pClient, root, flags, pcbBuffer, pBuffer);
2017
2018 AssertFailed();
2019 return VERR_INVALID_PARAMETER;
2020}
2021
2022#ifdef UNITTEST
2023/** Unit test the SHFL_FN_INFORMATION API. Located here as a form of API
2024 * documentation. */
2025void testFSInfo(RTTEST hTest)
2026{
2027 /* If the number or types of parameters are wrong the API should fail. */
2028 testFSInfoBadParameters(hTest);
2029 /* Basic get and set file size test. */
2030 testFSInfoQuerySetFMode(hTest);
2031 /* Basic get and set dir atime test. */
2032 testFSInfoQuerySetDirATime(hTest);
2033 /* Basic get and set file atime test. */
2034 testFSInfoQuerySetFileATime(hTest);
2035 /* Basic set end of file. */
2036 testFSInfoQuerySetEndOfFile(hTest);
2037 /* Add tests as required... */
2038}
2039#endif
2040int vbsfSetFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
2041{
2042 uint32_t type = vbsfQueryHandleType(pClient, Handle)
2043 & (SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE|SHFL_HF_TYPE_VOLUME);
2044
2045 if (type == 0 || pcbBuffer == 0 || pBuffer == 0)
2046 {
2047 AssertFailed();
2048 return VERR_INVALID_PARAMETER;
2049 }
2050
2051 if (flags & SHFL_INFO_FILE)
2052 return vbsfSetFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2053
2054 if (flags & SHFL_INFO_SIZE)
2055 return vbsfSetEndOfFile(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2056
2057// if (flags & SHFL_INFO_VOLUME)
2058// return vbsfVolumeInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2059 AssertFailed();
2060 return VERR_INVALID_PARAMETER;
2061}
2062
2063#ifdef UNITTEST
2064/** Unit test the SHFL_FN_LOCK API. Located here as a form of API
2065 * documentation. */
2066void testLock(RTTEST hTest)
2067{
2068 /* If the number or types of parameters are wrong the API should fail. */
2069 testLockBadParameters(hTest);
2070 /* Simple file locking and unlocking test. */
2071 testLockFileSimple(hTest);
2072 /* Add tests as required... */
2073}
2074#endif
2075
2076int vbsfLock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
2077{
2078 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
2079 uint32_t fRTLock = 0;
2080
2081 Assert((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL);
2082
2083 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
2084 if (RT_SUCCESS(rc))
2085 { /* likely */ }
2086 else
2087 return rc;
2088
2089 if ( ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL)
2090 || (flags & SHFL_LOCK_ENTIRE)
2091 )
2092 {
2093 AssertFailed();
2094 return VERR_INVALID_PARAMETER;
2095 }
2096
2097 /* Lock type */
2098 switch(flags & SHFL_LOCK_MODE_MASK)
2099 {
2100 case SHFL_LOCK_SHARED:
2101 fRTLock = RTFILE_LOCK_READ;
2102 break;
2103
2104 case SHFL_LOCK_EXCLUSIVE:
2105 fRTLock = RTFILE_LOCK_READ | RTFILE_LOCK_WRITE;
2106 break;
2107
2108 default:
2109 AssertFailed();
2110 return VERR_INVALID_PARAMETER;
2111 }
2112
2113 /* Lock wait type */
2114 if (flags & SHFL_LOCK_WAIT)
2115 fRTLock |= RTFILE_LOCK_WAIT;
2116 else
2117 fRTLock |= RTFILE_LOCK_IMMEDIATELY;
2118
2119#ifdef RT_OS_WINDOWS
2120 rc = RTFileLock(pHandle->file.Handle, fRTLock, offset, length);
2121 if (rc != VINF_SUCCESS)
2122 Log(("RTFileLock %RTfile %RX64 %RX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
2123#else
2124 Log(("vbsfLock: Pretend success handle=%x\n", Handle));
2125 rc = VINF_SUCCESS;
2126 RT_NOREF2(offset, length);
2127#endif
2128 return rc;
2129}
2130
2131int vbsfUnlock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
2132{
2133 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
2134
2135 Assert((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL);
2136
2137 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
2138 if (RT_SUCCESS(rc))
2139 { /* likely */ }
2140 else
2141 return rc;
2142
2143 if ( ((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL)
2144 || (flags & SHFL_LOCK_ENTIRE)
2145 )
2146 {
2147 return VERR_INVALID_PARAMETER;
2148 }
2149
2150#ifdef RT_OS_WINDOWS
2151 rc = RTFileUnlock(pHandle->file.Handle, offset, length);
2152 if (rc != VINF_SUCCESS)
2153 Log(("RTFileUnlock %RTfile %RX64 %RTX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
2154#else
2155 Log(("vbsfUnlock: Pretend success handle=%x\n", Handle));
2156 rc = VINF_SUCCESS;
2157 RT_NOREF2(offset, length);
2158#endif
2159
2160 return rc;
2161}
2162
2163
2164#ifdef UNITTEST
2165/** Unit test the SHFL_FN_REMOVE API. Located here as a form of API
2166 * documentation. */
2167void testRemove(RTTEST hTest)
2168{
2169 /* If the number or types of parameters are wrong the API should fail. */
2170 testRemoveBadParameters(hTest);
2171 /* Add tests as required... */
2172}
2173#endif
2174int vbsfRemove(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint32_t flags)
2175{
2176 int rc = VINF_SUCCESS;
2177
2178 /* Validate input */
2179 if ( flags & ~(SHFL_REMOVE_FILE|SHFL_REMOVE_DIR|SHFL_REMOVE_SYMLINK)
2180 || cbPath == 0
2181 || pPath == 0)
2182 {
2183 AssertFailed();
2184 return VERR_INVALID_PARAMETER;
2185 }
2186
2187 /* Build a host full path for the given path
2188 * and convert ucs2 to utf8 if necessary.
2189 */
2190 char *pszFullPath = NULL;
2191
2192 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, NULL);
2193 if (RT_SUCCESS(rc))
2194 {
2195 /* is the guest allowed to write to this share? */
2196 bool fWritable;
2197 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
2198 if (RT_FAILURE(rc) || !fWritable)
2199 rc = VERR_WRITE_PROTECT;
2200
2201 if (RT_SUCCESS(rc))
2202 {
2203 if (flags & SHFL_REMOVE_SYMLINK)
2204 rc = RTSymlinkDelete(pszFullPath, 0);
2205 else if (flags & SHFL_REMOVE_FILE)
2206 rc = RTFileDelete(pszFullPath);
2207 else
2208 rc = RTDirRemove(pszFullPath);
2209 }
2210
2211#ifndef DEBUG_dmik
2212 // VERR_ACCESS_DENIED for example?
2213 // Assert(rc == VINF_SUCCESS || rc == VERR_DIR_NOT_EMPTY);
2214#endif
2215 /* free the path string */
2216 vbsfFreeFullPath(pszFullPath);
2217 }
2218 return rc;
2219}
2220
2221
2222#ifdef UNITTEST
2223/** Unit test the SHFL_FN_RENAME API. Located here as a form of API
2224 * documentation. */
2225void testRename(RTTEST hTest)
2226{
2227 /* If the number or types of parameters are wrong the API should fail. */
2228 testRenameBadParameters(hTest);
2229 /* Add tests as required... */
2230}
2231#endif
2232int vbsfRename(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pSrc, SHFLSTRING *pDest, uint32_t flags)
2233{
2234 int rc = VINF_SUCCESS;
2235
2236 /* Validate input */
2237 if ( flags & ~(SHFL_RENAME_FILE|SHFL_RENAME_DIR|SHFL_RENAME_REPLACE_IF_EXISTS)
2238 || pSrc == 0
2239 || pDest == 0)
2240 {
2241 AssertFailed();
2242 return VERR_INVALID_PARAMETER;
2243 }
2244
2245 /* Build a host full path for the given path
2246 * and convert ucs2 to utf8 if necessary.
2247 */
2248 char *pszFullPathSrc = NULL;
2249 char *pszFullPathDest = NULL;
2250
2251 rc = vbsfBuildFullPath(pClient, root, pSrc, pSrc->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPathSrc, NULL);
2252 if (rc != VINF_SUCCESS)
2253 return rc;
2254
2255 rc = vbsfBuildFullPath(pClient, root, pDest, pDest->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPathDest, NULL, false, true);
2256 if (RT_SUCCESS (rc))
2257 {
2258 Log(("Rename %s to %s\n", pszFullPathSrc, pszFullPathDest));
2259
2260 /* is the guest allowed to write to this share? */
2261 bool fWritable;
2262 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
2263 if (RT_FAILURE(rc) || !fWritable)
2264 rc = VERR_WRITE_PROTECT;
2265
2266 if (RT_SUCCESS(rc))
2267 {
2268 if ((flags & (SHFL_RENAME_FILE | SHFL_RENAME_DIR)) == (SHFL_RENAME_FILE | SHFL_RENAME_DIR))
2269 {
2270 rc = RTPathRename(pszFullPathSrc, pszFullPathDest,
2271 flags & SHFL_RENAME_REPLACE_IF_EXISTS ? RTPATHRENAME_FLAGS_REPLACE : 0);
2272 }
2273 else if (flags & SHFL_RENAME_FILE)
2274 {
2275 rc = RTFileMove(pszFullPathSrc, pszFullPathDest,
2276 ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTFILEMOVE_FLAGS_REPLACE : 0));
2277 }
2278 else
2279 {
2280 /* NT ignores the REPLACE flag and simply return and already exists error. */
2281 rc = RTDirRename(pszFullPathSrc, pszFullPathDest,
2282 ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTPATHRENAME_FLAGS_REPLACE : 0));
2283 }
2284 }
2285
2286 /* free the path string */
2287 vbsfFreeFullPath(pszFullPathDest);
2288 }
2289 /* free the path string */
2290 vbsfFreeFullPath(pszFullPathSrc);
2291 return rc;
2292}
2293
2294#ifdef UNITTEST
2295/** Unit test the SHFL_FN_SYMLINK API. Located here as a form of API
2296 * documentation. */
2297void testSymlink(RTTEST hTest)
2298{
2299 /* If the number or types of parameters are wrong the API should fail. */
2300 testSymlinkBadParameters(hTest);
2301 /* Add tests as required... */
2302}
2303#endif
2304int vbsfSymlink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pNewPath, SHFLSTRING *pOldPath, SHFLFSOBJINFO *pInfo)
2305{
2306 int rc = VINF_SUCCESS;
2307
2308 char *pszFullNewPath = NULL;
2309 char *pszFullOldPath = NULL;
2310
2311 /* XXX: no support for UCS2 at the moment. */
2312 if (!BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
2313 return VERR_NOT_IMPLEMENTED;
2314
2315 bool fSymlinksCreate;
2316 rc = vbsfMappingsQuerySymlinksCreate(pClient, root, &fSymlinksCreate);
2317 AssertRCReturn(rc, rc);
2318 if (!fSymlinksCreate)
2319 return VERR_WRITE_PROTECT; /* XXX or VERR_TOO_MANY_SYMLINKS? */
2320
2321 rc = vbsfBuildFullPath(pClient, root, pNewPath, pNewPath->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullNewPath, NULL);
2322 AssertRCReturn(rc, rc);
2323
2324 /* Verify that the link target can be a valid host path, i.e. does not contain invalid characters. */
2325 uint32_t fu32PathFlags = 0;
2326 uint32_t fu32Options = 0;
2327 rc = vbsfPathGuestToHost(pClient, root, pOldPath, pOldPath->u16Size + SHFLSTRING_HEADER_SIZE,
2328 &pszFullOldPath, NULL, fu32Options, &fu32PathFlags);
2329 if (RT_FAILURE(rc))
2330 {
2331 vbsfFreeFullPath(pszFullNewPath);
2332 return rc;
2333 }
2334
2335 rc = RTSymlinkCreate(pszFullNewPath, (const char *)pOldPath->String.utf8,
2336 RTSYMLINKTYPE_UNKNOWN, 0);
2337 if (RT_SUCCESS(rc))
2338 {
2339 RTFSOBJINFO info;
2340 rc = RTPathQueryInfoEx(pszFullNewPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
2341 if (RT_SUCCESS(rc))
2342 vbfsCopyFsObjInfoFromIprt(pInfo, &info);
2343 }
2344
2345 vbsfFreeFullPath(pszFullOldPath);
2346 vbsfFreeFullPath(pszFullNewPath);
2347
2348 return rc;
2349}
2350
2351/*
2352 * Clean up our mess by freeing all handles that are still valid.
2353 *
2354 */
2355int vbsfDisconnect(SHFLCLIENTDATA *pClient)
2356{
2357 for (int i = 0; i < SHFLHANDLE_MAX; ++i)
2358 {
2359 SHFLFILEHANDLE *pHandle = NULL;
2360 SHFLHANDLE Handle = (SHFLHANDLE)i;
2361
2362 uint32_t type = vbsfQueryHandleType(pClient, Handle);
2363 switch (type & (SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE))
2364 {
2365 case SHFL_HF_TYPE_DIR:
2366 {
2367 pHandle = vbsfQueryDirHandle(pClient, Handle);
2368 break;
2369 }
2370 case SHFL_HF_TYPE_FILE:
2371 {
2372 pHandle = vbsfQueryFileHandle(pClient, Handle);
2373 break;
2374 }
2375 default:
2376 break;
2377 }
2378
2379 if (pHandle)
2380 {
2381 LogFunc(("Opened handle 0x%08x\n", i));
2382 vbsfClose(pClient, pHandle->root, Handle);
2383 }
2384 }
2385
2386 for (uint32_t i = 0; i < RT_ELEMENTS(pClient->acMappings); i++)
2387 if (pClient->acMappings[i])
2388 {
2389 uint16_t cMappings = pClient->acMappings[i];
2390 while (cMappings-- > 0)
2391 vbsfUnmapFolder(pClient, i);
2392 }
2393
2394 return VINF_SUCCESS;
2395}
Note: See TracBrowser for help on using the repository browser.

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