VirtualBox

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

Last change on this file since 86607 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

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

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