VirtualBox

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

Last change on this file since 99802 was 99802, checked in by vboxsync, 12 months ago

*: Adjustments necessary for dropping -Zc:wchar_t- on windows and instead let the C++ compiler use the native wchar_t type.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use