VirtualBox

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

Last change on this file since 76845 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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