VirtualBox

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

Last change on this file was 100220, checked in by vboxsync, 10 months ago

HostServices/SharedFolders/vbsf.cpp: Don't use the host page size when dealing with guest memory but use the VMMDEV_PAGE_* macros, fixes data corruption during reads and writes, bugref:10456

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

© 2023 Oracle
ContactPrivacy policyTerms of Use