VirtualBox

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

最後變更 在這個檔案從78904是 78704,由 vboxsync 提交於 6 年 前

SharedFoldersSvc: More VERR_FILE_NOT_FOUND/VERR_PATH_NOT_FOUND and some other tweaks for windows guests on non-windows hosts. FsPerf seems reasonably happy now. bugref:9172

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 88.1 KB
 
1/* $Id: vbsf.cpp 78704 2019-05-24 01:10:26Z vboxsync $ */
2/** @file
3 * Shared Folders - VBox Shared Folders.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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 * @retval pfOpen 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 @a CreateFlags Creation or open parameters, see include/VBox/shflsvc.h
562 * @param pParms @a Info When a new file is created this specifies the initial parameters.
563 * When a file is created or overwritten, it also specifies the
564 * initial size.
565 * @retval pParms @a Resulte Shared folder status code, see include/VBox/shflsvc.h
566 * @retval pParms @a Handle On success the (shared folder) handle of the file opened or
567 * created
568 * @retval pParms @a Info On success the parameters of the file opened or created
569 */
570static int vbsfOpenFile(SHFLCLIENTDATA *pClient, SHFLROOT root, char *pszPath, SHFLCREATEPARMS *pParms)
571{
572 LogFlow(("vbsfOpenFile: pszPath = %s, pParms = %p\n", pszPath, pParms));
573 Log(("SHFL create flags %08x\n", pParms->CreateFlags));
574
575 RTFILEACTION enmActionTaken = RTFILEACTION_INVALID;
576 SHFLHANDLE handle = SHFL_HANDLE_NIL;
577 SHFLFILEHANDLE *pHandle = NULL;
578
579 /* is the guest allowed to write to this share? */
580 bool fWritable;
581 int rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
582 if (RT_FAILURE(rc))
583 fWritable = false;
584
585 uint64_t fOpen = 0;
586 rc = vbsfConvertFileOpenFlags(fWritable, pParms->CreateFlags, pParms->Info.Attr.fMode, pParms->Handle, &fOpen);
587 if (RT_SUCCESS(rc))
588 {
589 rc = VERR_NO_MEMORY; /* Default error. */
590 handle = vbsfAllocFileHandle(pClient);
591 if (handle != SHFL_HANDLE_NIL)
592 {
593 pHandle = vbsfQueryFileHandle(pClient, handle);
594 if (pHandle)
595 {
596 pHandle->root = root;
597 pHandle->file.fOpenFlags = fOpen;
598 rc = RTFileOpenEx(pszPath, fOpen, &pHandle->file.Handle, &enmActionTaken);
599 }
600 }
601 }
602 bool fNoError = false;
603 if (RT_FAILURE(rc))
604 {
605 switch (rc)
606 {
607 case VERR_FILE_NOT_FOUND:
608 pParms->Result = SHFL_FILE_NOT_FOUND;
609#ifndef RT_OS_WINDOWS
610 if ( SHFL_CLIENT_NEED_WINDOWS_ERROR_STYLE_ADJUST_ON_POSIX(pClient)
611 && vbsfErrorStyleIsWindowsPathNotFound(pszPath))
612 pParms->Result = SHFL_PATH_NOT_FOUND;
613#endif
614 /* This actually isn't an error, so correct the rc before return later,
615 because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
616 fNoError = true;
617 break;
618
619 case VERR_PATH_NOT_FOUND:
620#ifndef RT_OS_WINDOWS
621 if ( SHFL_CLIENT_NEED_WINDOWS_ERROR_STYLE_ADJUST_ON_POSIX(pClient)
622 && vbsfErrorStyleIsWindowsInvalidNameForNonDir(pszPath))
623 {
624 rc = VERR_INVALID_NAME;
625 pParms->Result = SHFL_NO_RESULT;
626 break;
627 }
628#endif
629 pParms->Result = SHFL_PATH_NOT_FOUND;
630 fNoError = true; /* Not an error either (see above). */
631 break;
632
633 case VERR_ALREADY_EXISTS:
634 {
635 RTFSOBJINFO info;
636
637 /** @todo Possible race left here. */
638 if (RT_SUCCESS(RTPathQueryInfoEx(pszPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient))))
639 {
640#ifdef RT_OS_WINDOWS
641 info.Attr.fMode |= 0111;
642#endif
643 vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
644 }
645 pParms->Result = SHFL_FILE_EXISTS;
646
647 /* This actually isn't an error, so correct the rc before return later,
648 because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
649 fNoError = true;
650 break;
651 }
652
653 case VERR_TOO_MANY_OPEN_FILES:
654 {
655 static int s_cErrors;
656 if (s_cErrors < 32)
657 {
658 LogRel(("SharedFolders host service: Cannot open '%s' -- too many open files.\n", pszPath));
659#if defined RT_OS_LINUX || defined(RT_OS_SOLARIS)
660 if (s_cErrors < 1)
661 LogRel(("SharedFolders host service: Try to increase the limit for open files (ulimit -n)\n"));
662#endif
663 s_cErrors++;
664 }
665 pParms->Result = SHFL_NO_RESULT;
666 break;
667 }
668
669 default:
670 pParms->Result = SHFL_NO_RESULT;
671 }
672 }
673 else
674 {
675 switch (enmActionTaken)
676 {
677 default:
678 AssertFailed();
679 RT_FALL_THRU();
680 case RTFILEACTION_OPENED:
681 pParms->Result = SHFL_FILE_EXISTS;
682 break;
683 case RTFILEACTION_CREATED:
684 pParms->Result = SHFL_FILE_CREATED;
685 break;
686 case RTFILEACTION_REPLACED:
687 case RTFILEACTION_TRUNCATED: /* not quite right */
688 pParms->Result = SHFL_FILE_REPLACED;
689 break;
690 }
691
692 if ( (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_REPLACE_IF_EXISTS
693 || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_OVERWRITE_IF_EXISTS)
694 {
695 /* For now, we do not treat a failure here as fatal. */
696 /** @todo Also set the size for SHFL_CF_ACT_CREATE_IF_NEW if SHFL_CF_ACT_FAIL_IF_EXISTS is set. */
697 /** @todo r=bird: Exactly document cbObject usage and see what we can get
698 * away with here. I suspect it is only needed for windows and only
699 * with SHFL_FILE_CREATED and SHFL_FILE_REPLACED, and only if
700 * cbObject is non-zero. */
701 RTFileSetSize(pHandle->file.Handle, pParms->Info.cbObject);
702 }
703#if 0
704 /** @todo */
705 /* Set new attributes. */
706 if ( ( SHFL_CF_ACT_REPLACE_IF_EXISTS
707 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
708 || ( SHFL_CF_ACT_CREATE_IF_NEW
709 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW)))
710 {
711 RTFileSetTimes(pHandle->file.Handle,
712 &pParms->Info.AccessTime,
713 &pParms->Info.ModificationTime,
714 &pParms->Info.ChangeTime,
715 &pParms->Info.BirthTime
716 );
717
718 RTFileSetMode (pHandle->file.Handle, pParms->Info.Attr.fMode);
719 }
720#endif
721 RTFSOBJINFO info;
722
723 /* Get file information */
724 rc = RTFileQueryInfo(pHandle->file.Handle, &info, RTFSOBJATTRADD_NOTHING);
725 if (RT_SUCCESS(rc))
726 {
727#ifdef RT_OS_WINDOWS
728 info.Attr.fMode |= 0111;
729#endif
730 vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
731 }
732 }
733 /* Free resources if any part of the function has failed. */
734 if (RT_FAILURE(rc))
735 {
736 if ( (0 != pHandle)
737 && (NIL_RTFILE != pHandle->file.Handle)
738 && (0 != pHandle->file.Handle))
739 {
740 RTFileClose(pHandle->file.Handle);
741 pHandle->file.Handle = NIL_RTFILE;
742 }
743 if (SHFL_HANDLE_NIL != handle)
744 {
745 vbsfFreeFileHandle(pClient, handle);
746 }
747 pParms->Handle = SHFL_HANDLE_NIL;
748 }
749 else
750 {
751 pParms->Handle = handle;
752 }
753
754 /* Report the driver that all is okay, we're done here */
755 if (fNoError)
756 rc = VINF_SUCCESS;
757
758 LogFlow(("vbsfOpenFile: rc = %Rrc\n", rc));
759 return rc;
760}
761
762/**
763 * Open a folder or create and open a new one.
764 *
765 * @returns IPRT status code
766 * @param pClient Data structure describing the client accessing the shared folder
767 * @param root The index of the shared folder in the table of mappings.
768 * @param pszPath Path to the file or folder on the host.
769 * @param pParms @a CreateFlags Creation or open parameters, see include/VBox/shflsvc.h
770 * @retval pParms @a Result Shared folder status code, see include/VBox/shflsvc.h
771 * @retval pParms @a Handle On success the (shared folder) handle of the folder opened or
772 * created
773 * @retval pParms @a Info On success the parameters of the folder opened or created
774 *
775 * @note folders are created with fMode = 0777
776 */
777static int vbsfOpenDir(SHFLCLIENTDATA *pClient, SHFLROOT root, char *pszPath,
778 SHFLCREATEPARMS *pParms)
779{
780 LogFlow(("vbsfOpenDir: pszPath = %s, pParms = %p\n", pszPath, pParms));
781 Log(("SHFL create flags %08x\n", pParms->CreateFlags));
782
783 int rc = VERR_NO_MEMORY;
784 SHFLHANDLE handle = vbsfAllocDirHandle(pClient);
785 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, handle);
786 if (0 != pHandle)
787 {
788 pHandle->root = root;
789 pParms->Result = SHFL_FILE_EXISTS; /* May be overwritten with SHFL_FILE_CREATED. */
790 /** @todo Can anyone think of a sensible, race-less way to do this? Although
791 I suspect that the race is inherent, due to the API available... */
792 /* Try to create the folder first if "create if new" is specified. If this
793 fails, and "open if exists" is specified, then we ignore the failure and try
794 to open the folder anyway. */
795 if ( SHFL_CF_ACT_CREATE_IF_NEW
796 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW))
797 {
798 /** @todo render supplied attributes.
799 * bird: The guest should specify this. For windows guests RTFS_DOS_DIRECTORY should suffice. */
800 RTFMODE fMode = 0777;
801
802 pParms->Result = SHFL_FILE_CREATED;
803 rc = RTDirCreate(pszPath, fMode, 0);
804 if (RT_FAILURE(rc))
805 {
806 /** @todo we still return 'rc' as failure here, so this is mostly pointless. */
807 switch (rc)
808 {
809 case VERR_ALREADY_EXISTS:
810 pParms->Result = SHFL_FILE_EXISTS;
811 break;
812 case VERR_PATH_NOT_FOUND:
813 pParms->Result = SHFL_PATH_NOT_FOUND;
814 break;
815 case VERR_FILE_NOT_FOUND: /* may happen on posix */
816 pParms->Result = SHFL_FILE_NOT_FOUND;
817#ifndef RT_OS_WINDOWS
818 if ( SHFL_CLIENT_NEED_WINDOWS_ERROR_STYLE_ADJUST_ON_POSIX(pClient)
819 && vbsfErrorStyleIsWindowsPathNotFound(pszPath))
820 {
821 pParms->Result = SHFL_PATH_NOT_FOUND;
822 rc = VERR_PATH_NOT_FOUND;
823 }
824#endif
825 break;
826 default:
827 pParms->Result = SHFL_NO_RESULT;
828 }
829 }
830 }
831 else
832 rc = VINF_SUCCESS;
833 if ( RT_SUCCESS(rc)
834 || (SHFL_CF_ACT_OPEN_IF_EXISTS == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS)))
835 {
836 /* Open the directory now */
837 rc = RTDirOpenFiltered(&pHandle->dir.Handle, pszPath, RTDIRFILTER_NONE, 0 /*fFlags*/);
838 if (RT_SUCCESS(rc))
839 {
840 RTFSOBJINFO info;
841
842 rc = RTDirQueryInfo(pHandle->dir.Handle, &info, RTFSOBJATTRADD_NOTHING);
843 if (RT_SUCCESS(rc))
844 {
845 vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
846 }
847 }
848 else
849 {
850 /** @todo we still return 'rc' as failure here, so this is mostly pointless. */
851 switch (rc)
852 {
853 case VERR_FILE_NOT_FOUND:
854 pParms->Result = SHFL_FILE_NOT_FOUND;
855#ifndef RT_OS_WINDOWS
856 if ( SHFL_CLIENT_NEED_WINDOWS_ERROR_STYLE_ADJUST_ON_POSIX(pClient)
857 && vbsfErrorStyleIsWindowsPathNotFound(pszPath))
858 {
859 pParms->Result = SHFL_PATH_NOT_FOUND;
860 rc = VERR_PATH_NOT_FOUND;
861 }
862#endif
863 break;
864 case VERR_PATH_NOT_FOUND:
865 pParms->Result = SHFL_PATH_NOT_FOUND;
866#ifndef RT_OS_WINDOWS
867 if ( SHFL_CLIENT_NEED_WINDOWS_ERROR_STYLE_ADJUST_ON_POSIX(pClient)
868 && vbsfErrorStyleIsWindowsNotADirectory(pszPath))
869 {
870 pParms->Result = SHFL_FILE_EXISTS;
871 rc = VERR_NOT_A_DIRECTORY;
872 break;
873 }
874#endif
875 break;
876 case VERR_ACCESS_DENIED:
877 pParms->Result = SHFL_FILE_EXISTS;
878 break;
879 default:
880 pParms->Result = SHFL_NO_RESULT;
881 }
882 }
883 }
884 }
885 if (RT_FAILURE(rc))
886 {
887 if ( (0 != pHandle)
888 && (0 != pHandle->dir.Handle))
889 {
890 RTDirClose(pHandle->dir.Handle);
891 pHandle->dir.Handle = 0;
892 }
893 if (SHFL_HANDLE_NIL != handle)
894 {
895 vbsfFreeFileHandle(pClient, handle);
896 }
897 pParms->Handle = SHFL_HANDLE_NIL;
898 }
899 else
900 {
901 pParms->Handle = handle;
902 }
903 LogFlow(("vbsfOpenDir: rc = %Rrc\n", rc));
904 return rc;
905}
906
907static int vbsfCloseDir(SHFLFILEHANDLE *pHandle)
908{
909 int rc = VINF_SUCCESS;
910
911 LogFlow(("vbsfCloseDir: Handle = %08X Search Handle = %08X\n",
912 pHandle->dir.Handle, pHandle->dir.SearchHandle));
913
914 RTDirClose(pHandle->dir.Handle);
915
916 if (pHandle->dir.SearchHandle)
917 RTDirClose(pHandle->dir.SearchHandle);
918
919 if (pHandle->dir.pLastValidEntry)
920 {
921 RTMemFree(pHandle->dir.pLastValidEntry);
922 pHandle->dir.pLastValidEntry = NULL;
923 }
924
925 LogFlow(("vbsfCloseDir: rc = %d\n", rc));
926
927 return rc;
928}
929
930
931static int vbsfCloseFile(SHFLFILEHANDLE *pHandle)
932{
933 int rc = VINF_SUCCESS;
934
935 LogFlow(("vbsfCloseFile: Handle = %08X\n",
936 pHandle->file.Handle));
937
938 rc = RTFileClose(pHandle->file.Handle);
939
940 LogFlow(("vbsfCloseFile: rc = %d\n", rc));
941
942 return rc;
943}
944
945/**
946 * Look up file or folder information by host path.
947 *
948 * @returns iprt status code (currently VINF_SUCCESS)
949 * @param pClient client data
950 * @param pszPath The path of the file to be looked up
951 * @retval pParms->Result Status of the operation (success or error)
952 * @retval pParms->Info On success, information returned about the file
953 */
954static int vbsfLookupFile(SHFLCLIENTDATA *pClient, char *pszPath, SHFLCREATEPARMS *pParms)
955{
956 RTFSOBJINFO info;
957 int rc;
958
959 rc = RTPathQueryInfoEx(pszPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
960 LogFlow(("SHFL_CF_LOOKUP\n"));
961 /* Client just wants to know if the object exists. */
962 switch (rc)
963 {
964 case VINF_SUCCESS:
965 {
966#ifdef RT_OS_WINDOWS
967 info.Attr.fMode |= 0111;
968#endif
969 vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
970 pParms->Result = SHFL_FILE_EXISTS;
971 break;
972 }
973
974 case VERR_FILE_NOT_FOUND:
975 {
976 pParms->Result = SHFL_FILE_NOT_FOUND;
977 rc = VINF_SUCCESS;
978 break;
979 }
980
981 case VERR_PATH_NOT_FOUND:
982 {
983 pParms->Result = SHFL_PATH_NOT_FOUND;
984 rc = VINF_SUCCESS;
985 break;
986 }
987 }
988 pParms->Handle = SHFL_HANDLE_NIL;
989 return rc;
990}
991
992#ifdef UNITTEST
993/** Unit test the SHFL_FN_CREATE API. Located here as a form of API
994 * documentation. */
995void testCreate(RTTEST hTest)
996{
997 /* Simple opening of an existing file. */
998 testCreateFileSimple(hTest);
999 testCreateFileSimpleCaseInsensitive(hTest);
1000 /* Simple opening of an existing directory. */
1001 /** @todo How do wildcards in the path name work? */
1002 testCreateDirSimple(hTest);
1003 /* If the number or types of parameters are wrong the API should fail. */
1004 testCreateBadParameters(hTest);
1005 /* Add tests as required... */
1006}
1007#endif
1008
1009/**
1010 * Create or open a file or folder. Perform character set and case
1011 * conversion on the file name if necessary.
1012 *
1013 * @returns IPRT status code, but see note below
1014 * @param pClient Data structure describing the client accessing the shared
1015 * folder
1016 * @param root The index of the shared folder in the table of mappings.
1017 * The host path of the shared folder is found using this.
1018 * @param pPath The path of the file or folder relative to the host path
1019 * indexed by root.
1020 * @param cbPath Presumably the length of the path in pPath. Actually
1021 * ignored, as pPath contains a length parameter.
1022 * @param pParms @a Info If a new file is created or an old one overwritten, set
1023 * these attributes
1024 * @retval pParms @a Result Shared folder result code, see include/VBox/shflsvc.h
1025 * @retval pParms @a Handle Shared folder handle to the newly opened file
1026 * @retval pParms @a Info Attributes of the file or folder opened
1027 *
1028 * @note This function returns success if a "non-exceptional" error occurred,
1029 * such as "no such file". In this case, the caller should check the
1030 * pParms->Result return value and whether pParms->Handle is valid.
1031 */
1032int vbsfCreate(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, SHFLCREATEPARMS *pParms)
1033{
1034 int rc = VINF_SUCCESS;
1035
1036 LogFlow(("vbsfCreate: pClient = %p, pPath = %p, cbPath = %d, pParms = %p CreateFlags=%x\n",
1037 pClient, pPath, cbPath, pParms, pParms->CreateFlags));
1038
1039 /* Check the client access rights to the root. */
1040 /** @todo */
1041
1042 /* Build a host full path for the given path, handle file name case issues (if the guest
1043 * expects case-insensitive paths but the host is case-sensitive) and convert ucs2 to utf8 if
1044 * necessary.
1045 */
1046 char *pszFullPath = NULL;
1047 uint32_t cbFullPathRoot = 0;
1048
1049 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot);
1050 if (RT_SUCCESS(rc))
1051 {
1052 /* Reset return value in case client forgot to do so.
1053 * pParms->Handle must not be reset here, as it is used
1054 * in vbsfOpenFile to detect old additions.
1055 */
1056 pParms->Result = SHFL_NO_RESULT;
1057
1058 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_LOOKUP))
1059 {
1060 rc = vbsfLookupFile(pClient, pszFullPath, pParms);
1061 }
1062 else
1063 {
1064 /* Query path information. */
1065 RTFSOBJINFO info;
1066
1067 rc = RTPathQueryInfoEx(pszFullPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
1068 LogFlow(("RTPathQueryInfoEx returned %Rrc\n", rc));
1069
1070 if (RT_SUCCESS(rc))
1071 {
1072 /* Mark it as a directory in case the caller didn't. */
1073 /**
1074 * @todo I left this in in order not to change the behaviour of the
1075 * function too much. Is it really needed, and should it really be
1076 * here?
1077 */
1078 if (BIT_FLAG(info.Attr.fMode, RTFS_DOS_DIRECTORY))
1079 {
1080 pParms->CreateFlags |= SHFL_CF_DIRECTORY;
1081 }
1082
1083 /**
1084 * @todo This should be in the Windows Guest Additions, as no-one else
1085 * needs it.
1086 */
1087 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_OPEN_TARGET_DIRECTORY))
1088 {
1089 vbsfStripLastComponent(pszFullPath, cbFullPathRoot);
1090 pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_EXISTS;
1091 pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_NEW;
1092 pParms->CreateFlags |= SHFL_CF_DIRECTORY;
1093 pParms->CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS;
1094 pParms->CreateFlags |= SHFL_CF_ACT_FAIL_IF_NEW;
1095 }
1096 }
1097
1098 rc = VINF_SUCCESS;
1099
1100 /* Note: do not check the SHFL_CF_ACCESS_WRITE here, only check if the open operation
1101 * will cause changes.
1102 *
1103 * Actual operations (write, set attr, etc), which can write to a shared folder, have
1104 * the check and will return VERR_WRITE_PROTECT if the folder is not writable.
1105 */
1106 if ( (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_REPLACE_IF_EXISTS
1107 || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_OVERWRITE_IF_EXISTS
1108 || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_NEW) == SHFL_CF_ACT_CREATE_IF_NEW
1109 )
1110 {
1111 /* is the guest allowed to write to this share? */
1112 bool fWritable;
1113 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
1114 if (RT_FAILURE(rc) || !fWritable)
1115 rc = VERR_WRITE_PROTECT;
1116 }
1117
1118 if (RT_SUCCESS(rc))
1119 {
1120 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_DIRECTORY))
1121 {
1122 rc = vbsfOpenDir(pClient, root, pszFullPath, pParms);
1123 }
1124 else
1125 {
1126 rc = vbsfOpenFile(pClient, root, pszFullPath, pParms);
1127 }
1128 }
1129 else
1130 {
1131 pParms->Handle = SHFL_HANDLE_NIL;
1132 }
1133 }
1134
1135 /* free the path string */
1136 vbsfFreeFullPath(pszFullPath);
1137 }
1138
1139 Log(("vbsfCreate: handle = %RX64 rc = %Rrc result=%x\n", (uint64_t)pParms->Handle, rc, pParms->Result));
1140
1141 return rc;
1142}
1143
1144#ifdef UNITTEST
1145/** Unit test the SHFL_FN_CLOSE API. Located here as a form of API
1146 * documentation. */
1147void testClose(RTTEST hTest)
1148{
1149 /* If the API parameters are invalid the API should fail. */
1150 testCloseBadParameters(hTest);
1151 /* Add tests as required... */
1152}
1153#endif
1154
1155int vbsfClose(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
1156{
1157 LogFunc(("pClient = %p, root 0x%RX32, Handle = 0x%RX64\n",
1158 pClient, root, Handle));
1159
1160 int rc = VERR_INVALID_HANDLE;
1161 uint32_t type = vbsfQueryHandleType(pClient, Handle);
1162 Assert((type & ~(SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE)) == 0);
1163 switch (type & (SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE))
1164 {
1165 case SHFL_HF_TYPE_DIR:
1166 {
1167 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
1168 if (RT_LIKELY(pHandle && root == pHandle->root))
1169 {
1170 rc = vbsfCloseDir(pHandle);
1171 vbsfFreeFileHandle(pClient, Handle);
1172 }
1173 break;
1174 }
1175 case SHFL_HF_TYPE_FILE:
1176 {
1177 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1178 if (RT_LIKELY(pHandle && root == pHandle->root))
1179 {
1180 rc = vbsfCloseFile(pHandle);
1181 vbsfFreeFileHandle(pClient, Handle);
1182 }
1183 break;
1184 }
1185 default:
1186 break;
1187 }
1188
1189 LogFunc(("rc = %Rrc\n", rc));
1190 return rc;
1191}
1192
1193/**
1194 * Helper for vbsfReadPages and vbsfWritePages that creates a S/G buffer from a
1195 * pages parameter.
1196 */
1197static int vbsfPagesToSgBuf(VBOXHGCMSVCPARMPAGES const *pPages, uint32_t cbLeft, PRTSGBUF pSgBuf)
1198{
1199 PRTSGSEG paSegs = (PRTSGSEG)RTMemTmpAlloc(sizeof(paSegs[0]) * pPages->cPages);
1200 if (paSegs)
1201 {
1202 /*
1203 * Convert the pages to segments.
1204 */
1205 uint32_t iSeg = 0;
1206 uint32_t iPage = 0;
1207 for (;;)
1208 {
1209 Assert(iSeg < pPages->cPages);
1210 Assert(iPage < pPages->cPages);
1211
1212 /* Current page. */
1213 void *pvSeg;
1214 paSegs[iSeg].pvSeg = pvSeg = pPages->papvPages[iPage];
1215 uint32_t cbSeg = PAGE_SIZE - (uint32_t)((uintptr_t)pvSeg & PAGE_OFFSET_MASK);
1216 iPage++;
1217
1218 /* Adjacent to the next page? */
1219 while ( iPage < pPages->cPages
1220 && (uintptr_t)pvSeg + cbSeg == (uintptr_t)pPages->papvPages[iPage])
1221 {
1222 iPage++;
1223 cbSeg += PAGE_SIZE;
1224 }
1225
1226 /* Adjust for max size. */
1227 if (cbLeft <= cbSeg)
1228 {
1229 paSegs[iSeg++].cbSeg = cbLeft;
1230 break;
1231 }
1232 paSegs[iSeg++].cbSeg = cbSeg;
1233 cbLeft -= cbSeg;
1234 }
1235
1236 /*
1237 * Initialize the s/g buffer and execute the read.
1238 */
1239 RTSgBufInit(pSgBuf, paSegs, iSeg);
1240 return VINF_SUCCESS;
1241 }
1242 pSgBuf->paSegs = NULL;
1243 return VERR_NO_TMP_MEMORY;
1244}
1245
1246
1247#ifdef UNITTEST
1248/** Unit test the SHFL_FN_READ API. Located here as a form of API
1249 * documentation. */
1250void testRead(RTTEST hTest)
1251{
1252 /* If the number or types of parameters are wrong the API should fail. */
1253 testReadBadParameters(hTest);
1254 /* Basic reading from a file. */
1255 testReadFileSimple(hTest);
1256 /* Add tests as required... */
1257}
1258#endif
1259int vbsfRead(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer)
1260{
1261 LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64, offset 0x%RX64, bytes 0x%RX32\n",
1262 pClient, root, Handle, offset, pcbBuffer? *pcbBuffer: 0));
1263
1264 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1265
1266 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1267 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1268 if (RT_SUCCESS(rc))
1269 {
1270 size_t const cbToRead = *pcbBuffer;
1271 if (RT_LIKELY(cbToRead > 0))
1272 {
1273 size_t cbActual = 0;
1274 rc = RTFileReadAt(pHandle->file.Handle, offset, pBuffer, cbToRead, &cbActual);
1275 *pcbBuffer = (uint32_t)cbActual;
1276 }
1277 else
1278 {
1279 /* Reading zero bytes always succeeds. */
1280 rc = VINF_SUCCESS;
1281 }
1282 }
1283 else
1284 *pcbBuffer = 0;
1285
1286 LogFunc(("%Rrc bytes read 0x%RX32\n", rc, *pcbBuffer));
1287 return rc;
1288}
1289
1290/**
1291 * SHFL_FN_READ w/o bounce buffering.
1292 */
1293int vbsfReadPages(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hFile, uint64_t offFile,
1294 uint32_t *pcbRead, PVBOXHGCMSVCPARMPAGES pPages)
1295{
1296 LogFunc(("pClient %p, idRoot %#RX32, hFile %#RX64, offFile %#RX64, cbRead %#RX32, cPages %#x\n",
1297 pClient, idRoot, hFile, offFile, *pcbRead, pPages->cPages));
1298
1299 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1300
1301 size_t cbTotal = 0;
1302 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, hFile);
1303 int rc = vbsfCheckHandleAccess(pClient, idRoot, pHandle, VBSF_CHECK_ACCESS_READ);
1304 if (RT_SUCCESS(rc))
1305 {
1306 uint32_t const cbToRead = *pcbRead;
1307 if (cbToRead > 0)
1308 {
1309 ASSERT_GUEST_RETURN(pPages->cPages > 0, VERR_INTERNAL_ERROR_3);
1310
1311 /*
1312 * Convert to a scatter-gather buffer.
1313 *
1314 * We need not do any platform specific code here as the RTSGBUF
1315 * segment array maps directly onto the posix iovec structure.
1316 * Windows does currently benefit much from this conversion, but
1317 * so be it.
1318 */
1319 RTSGBUF SgBuf;
1320 rc = vbsfPagesToSgBuf(pPages, cbToRead, &SgBuf);
1321 if (RT_SUCCESS(rc))
1322 {
1323 rc = RTFileSgReadAt(pHandle->file.Handle, offFile, &SgBuf, cbToRead, &cbTotal);
1324 while (rc == VERR_INTERRUPTED)
1325 {
1326 RTSgBufReset(&SgBuf);
1327 rc = RTFileSgReadAt(pHandle->file.Handle, offFile, &SgBuf, cbToRead, &cbTotal);
1328 }
1329
1330 RTMemTmpFree((void *)SgBuf.paSegs);
1331 }
1332 else
1333 rc = VERR_NO_TMP_MEMORY;
1334
1335 *pcbRead = (uint32_t)cbTotal;
1336 }
1337 else
1338 {
1339 /* Reading zero bytes always succeeds. */
1340 rc = VINF_SUCCESS;
1341 }
1342 }
1343 else
1344 *pcbRead = 0;
1345
1346 LogFunc(("%Rrc bytes read %#zx\n", rc, cbTotal));
1347 return rc;
1348}
1349
1350/**
1351 * Helps with writes to RTFILE_O_APPEND files.
1352 */
1353static uint64_t vbsfWriteCalcPostAppendFilePosition(RTFILE hFile, uint64_t offGuessed)
1354{
1355 RTFSOBJINFO ObjInfo;
1356 int rc2 = RTFileQueryInfo(hFile, &ObjInfo, RTFSOBJATTRADD_NOTHING);
1357 if (RT_SUCCESS(rc2) && (uint64_t)ObjInfo.cbObject >= offGuessed)
1358 return ObjInfo.cbObject;
1359 return offGuessed;
1360}
1361
1362#ifdef UNITTEST
1363/** Unit test the SHFL_FN_WRITE API. Located here as a form of API
1364 * documentation. */
1365void testWrite(RTTEST hTest)
1366{
1367 /* If the number or types of parameters are wrong the API should fail. */
1368 testWriteBadParameters(hTest);
1369 /* Simple test of writing to a file. */
1370 testWriteFileSimple(hTest);
1371 /* Add tests as required... */
1372}
1373#endif
1374int vbsfWrite(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hFile, uint64_t *poffFile,
1375 uint32_t *pcbBuffer, uint8_t *pBuffer)
1376{
1377 uint64_t offFile = *poffFile;
1378 LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64, offFile 0x%RX64, bytes 0x%RX32\n",
1379 pClient, idRoot, hFile, offFile, *pcbBuffer));
1380
1381 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1382
1383 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, hFile);
1384 int rc = vbsfCheckHandleAccess(pClient, idRoot, pHandle, VBSF_CHECK_ACCESS_WRITE);
1385 if (RT_SUCCESS(rc))
1386 {
1387 size_t const cbToWrite = *pcbBuffer;
1388 if (RT_LIKELY(cbToWrite != 0))
1389 {
1390 size_t cbWritten = 0;
1391 if (!(pHandle->file.fOpenFlags & RTFILE_O_APPEND))
1392 rc = RTFileWriteAt(pHandle->file.Handle, offFile, pBuffer, cbToWrite, &cbWritten);
1393 else
1394 {
1395 rc = RTFileSeek(pHandle->file.Handle, offFile, RTFILE_SEEK_BEGIN, NULL);
1396 AssertRC(rc);
1397 if (RT_SUCCESS(rc))
1398 {
1399 rc = RTFileWrite(pHandle->file.Handle, pBuffer, cbToWrite, &cbWritten);
1400 *pcbBuffer = (uint32_t)cbWritten;
1401 }
1402 }
1403
1404 /* Update the file offset (mainly for RTFILE_O_APPEND), */
1405 if (RT_SUCCESS(rc))
1406 {
1407 offFile += cbWritten;
1408 if (!(pHandle->file.fOpenFlags & RTFILE_O_APPEND))
1409 *poffFile = offFile;
1410 else
1411 *poffFile = vbsfWriteCalcPostAppendFilePosition(pHandle->file.Handle, offFile);
1412 }
1413 }
1414 else
1415 {
1416 /** @todo What writing zero bytes should do? */
1417 rc = VINF_SUCCESS;
1418 }
1419 }
1420 else
1421 *pcbBuffer = 0;
1422 LogFunc(("%Rrc bytes written 0x%RX32\n", rc, *pcbBuffer));
1423 return rc;
1424}
1425
1426/**
1427 * SHFL_FN_WRITE w/o bounce buffering.
1428 */
1429int vbsfWritePages(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hFile, uint64_t *poffFile,
1430 uint32_t *pcbWrite, PVBOXHGCMSVCPARMPAGES pPages)
1431{
1432 uint64_t offFile = *poffFile;
1433 LogFunc(("pClient %p, idRoot %#RX32, hFile %#RX64, offFile %#RX64, cbWrite %#RX32, cPages %#x\n",
1434 pClient, idRoot, hFile, offFile, *pcbWrite, pPages->cPages));
1435
1436 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1437
1438 size_t cbTotal = 0;
1439 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, hFile);
1440 int rc = vbsfCheckHandleAccess(pClient, idRoot, pHandle, VBSF_CHECK_ACCESS_WRITE);
1441 if (RT_SUCCESS(rc))
1442 {
1443 uint32_t const cbToWrite = *pcbWrite;
1444 if (cbToWrite > 0)
1445 {
1446 ASSERT_GUEST_RETURN(pPages->cPages > 0, VERR_INTERNAL_ERROR_3);
1447
1448 /*
1449 * Convert to a scatter-gather buffer.
1450 *
1451 * We need not do any platform specific code here as the RTSGBUF
1452 * segment array maps directly onto the posix iovec structure.
1453 * Windows does currently benefit much from this conversion, but
1454 * so be it.
1455 */
1456 RTSGBUF SgBuf;
1457 rc = vbsfPagesToSgBuf(pPages, cbToWrite, &SgBuf);
1458 if (RT_SUCCESS(rc))
1459 {
1460#ifndef RT_OS_LINUX
1461 /* Cannot use RTFileSgWriteAt or RTFileWriteAt when opened with
1462 RTFILE_O_APPEND, except for on linux where the offset is
1463 then ignored by the low level kernel API. */
1464 if (pHandle->file.fOpenFlags & RTFILE_O_APPEND)
1465 {
1466 /* paranoia */
1467 RTFileSeek(pHandle->file.Handle, 0, RTFILE_SEEK_END, NULL);
1468
1469 for (size_t iSeg = 0; iSeg < SgBuf.cSegs; iSeg++)
1470 {
1471 size_t cbWrittenNow = 0;
1472 do
1473 rc = RTFileWrite(pHandle->file.Handle, SgBuf.paSegs[iSeg].pvSeg,
1474 SgBuf.paSegs[iSeg].cbSeg, &cbWrittenNow);
1475 while (rc == VERR_INTERRUPTED);
1476 if (RT_SUCCESS(rc))
1477 {
1478 cbTotal += cbWrittenNow;
1479 if (cbWrittenNow < SgBuf.paSegs[iSeg].cbSeg)
1480 break;
1481 }
1482 else
1483 {
1484 if (cbTotal > 0)
1485 rc = VINF_SUCCESS;
1486 break;
1487 }
1488 }
1489 }
1490 else
1491#endif
1492 {
1493 rc = RTFileSgWriteAt(pHandle->file.Handle, offFile, &SgBuf, cbToWrite, &cbTotal);
1494 while (rc == VERR_INTERRUPTED)
1495 {
1496 RTSgBufReset(&SgBuf);
1497 rc = RTFileSgWriteAt(pHandle->file.Handle, offFile, &SgBuf, cbToWrite, &cbTotal);
1498 }
1499 }
1500
1501 RTMemTmpFree((void *)SgBuf.paSegs);
1502
1503 /* Update the file offset (mainly for RTFILE_O_APPEND), */
1504 if (RT_SUCCESS(rc))
1505 {
1506 offFile += cbTotal;
1507 if (!(pHandle->file.fOpenFlags & RTFILE_O_APPEND))
1508 *poffFile = offFile;
1509 else
1510 *poffFile = vbsfWriteCalcPostAppendFilePosition(pHandle->file.Handle, offFile);
1511 }
1512 }
1513 else
1514 rc = VERR_NO_TMP_MEMORY;
1515
1516 *pcbWrite = (uint32_t)cbTotal;
1517 }
1518 else
1519 {
1520 /* Writing zero bytes always succeeds. */
1521 rc = VINF_SUCCESS;
1522 }
1523 }
1524 else
1525 *pcbWrite = 0;
1526
1527 LogFunc(("%Rrc bytes written %#zx\n", rc, cbTotal));
1528 return rc;
1529}
1530
1531/**
1532 * Implements SHFL_FN_COPY_FILE_PART (wrapping RTFileCopyPart).
1533 */
1534int vbsfCopyFilePart(SHFLCLIENTDATA *pClient, SHFLROOT idRootSrc, SHFLHANDLE hFileSrc, uint64_t offSrc,
1535 SHFLROOT idRootDst, SHFLHANDLE hFileDst, uint64_t offDst, uint64_t *pcbToCopy, uint32_t fFlags)
1536{
1537 /*
1538 * Validate and translates handles.
1539 */
1540 uint64_t const cbToCopy = *pcbToCopy;
1541 *pcbToCopy = 0;
1542 LogFunc(("pClient %p, idRootSrc %#RX32, hFileSrc %#RX64, offSrc %#RX64, idRootSrc %#RX32, hFileSrc %#RX64, offSrc %#RX64, cbToCopy %#RX64, fFlags %#x\n",
1543 pClient, idRootSrc, hFileSrc, offSrc, idRootDst, hFileDst, offDst, cbToCopy, fFlags));
1544
1545 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1546
1547 uint64_t cbTotal = 0;
1548
1549 SHFLFILEHANDLE *pHandleSrc = vbsfQueryFileHandle(pClient, hFileSrc);
1550 int rc = vbsfCheckHandleAccess(pClient, idRootSrc, pHandleSrc, VBSF_CHECK_ACCESS_READ);
1551 if (RT_SUCCESS(rc))
1552 {
1553 SHFLFILEHANDLE *pHandleDst = vbsfQueryFileHandle(pClient, hFileDst);
1554 rc = vbsfCheckHandleAccess(pClient, idRootDst, pHandleDst, VBSF_CHECK_ACCESS_WRITE);
1555 if (RT_SUCCESS(rc))
1556 {
1557 /*
1558 * Do the job.
1559 */
1560 rc = RTFileCopyPart(pHandleSrc->file.Handle, offSrc, pHandleDst->file.Handle, offDst, cbToCopy, 0, &cbTotal);
1561 *pcbToCopy = cbTotal;
1562 }
1563 }
1564
1565 RT_NOREF(fFlags);
1566 LogFunc(("%Rrc bytes written %#zx\n", rc, cbTotal));
1567 return rc;
1568}
1569
1570#ifdef UNITTEST
1571/** Unit test the SHFL_FN_FLUSH API. Located here as a form of API
1572 * documentation. */
1573void testFlush(RTTEST hTest)
1574{
1575 /* If the number or types of parameters are wrong the API should fail. */
1576 testFlushBadParameters(hTest);
1577 /* Simple opening and flushing of a file. */
1578 testFlushFileSimple(hTest);
1579 /* Add tests as required... */
1580}
1581#endif
1582
1583int vbsfFlush(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
1584{
1585 LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64\n",
1586 pClient, root, Handle));
1587
1588 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1589
1590 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1591 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1592 if (RT_SUCCESS(rc))
1593 { /* likely */ }
1594 else
1595 return rc;
1596
1597 rc = RTFileFlush(pHandle->file.Handle);
1598
1599 LogFunc(("%Rrc\n", rc));
1600 return rc;
1601}
1602
1603#ifdef UNITTEST
1604/** Unit test the SHFL_FN_LIST API. Located here as a form of API
1605 * documentation. */
1606void testDirList(RTTEST hTest)
1607{
1608 /* If the number or types of parameters are wrong the API should fail. */
1609 testDirListBadParameters(hTest);
1610 /* Test listing an empty directory (simple edge case). */
1611 testDirListEmpty(hTest);
1612 /* Add tests as required... */
1613}
1614#endif
1615int vbsfDirList(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, SHFLSTRING *pPath, uint32_t flags,
1616 uint32_t *pcbBuffer, uint8_t *pBuffer, uint32_t *pIndex, uint32_t *pcFiles)
1617{
1618 PRTDIRENTRYEX pDirEntry = 0, pDirEntryOrg;
1619 uint32_t cbDirEntry, cbBufferOrg;
1620 PSHFLDIRINFO pSFDEntry;
1621 PRTUTF16 pwszString;
1622 RTDIR hDir;
1623 const bool fUtf8 = BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8) != 0;
1624
1625 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1626
1627 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
1628 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1629 if (RT_SUCCESS(rc))
1630 { /* likely */ }
1631 else
1632 return rc;
1633
1634 Assert(*pIndex == 0);
1635
1636 cbDirEntry = 4096;
1637 pDirEntryOrg = pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry);
1638 if (pDirEntry == 0)
1639 {
1640 AssertFailed();
1641 return VERR_NO_MEMORY;
1642 }
1643
1644 cbBufferOrg = *pcbBuffer;
1645 *pcbBuffer = 0;
1646 pSFDEntry = (PSHFLDIRINFO)pBuffer;
1647
1648 *pIndex = 1; /* not yet complete */
1649 *pcFiles = 0;
1650
1651 if (!pPath)
1652 hDir = pHandle->dir.Handle;
1653 else
1654 {
1655 if (pHandle->dir.SearchHandle == 0)
1656 {
1657 /* Build a host full path for the given path
1658 * and convert ucs2 to utf8 if necessary.
1659 */
1660 char *pszFullPath = NULL;
1661
1662 Assert(pHandle->dir.pLastValidEntry == 0);
1663
1664 rc = vbsfBuildFullPath(pClient, root, pPath, pPath->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPath, NULL, true);
1665
1666 if (RT_SUCCESS(rc))
1667 {
1668 rc = RTDirOpenFiltered(&pHandle->dir.SearchHandle, pszFullPath, RTDIRFILTER_WINNT, 0 /*fFlags*/);
1669
1670 /* free the path string */
1671 vbsfFreeFullPath(pszFullPath);
1672
1673 if (RT_FAILURE(rc))
1674 goto end;
1675 }
1676 else
1677 goto end;
1678 flags &= ~SHFL_LIST_RESTART;
1679 }
1680 Assert(pHandle->dir.SearchHandle);
1681 hDir = pHandle->dir.SearchHandle;
1682 }
1683
1684 if (flags & SHFL_LIST_RESTART)
1685 {
1686 rc = RTDirRewind(hDir);
1687 if (RT_FAILURE(rc))
1688 goto end;
1689 }
1690
1691 while (cbBufferOrg)
1692 {
1693 size_t cbDirEntrySize = cbDirEntry;
1694 uint32_t cbNeeded;
1695
1696 /* Do we still have a valid last entry for the active search? If so, then return it here */
1697 if (pHandle->dir.pLastValidEntry)
1698 {
1699 pDirEntry = pHandle->dir.pLastValidEntry;
1700 }
1701 else
1702 {
1703 pDirEntry = pDirEntryOrg;
1704
1705 rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
1706 if (rc == VERR_NO_MORE_FILES)
1707 {
1708 *pIndex = 0; /* listing completed */
1709 break;
1710 }
1711
1712 if ( rc != VINF_SUCCESS
1713 && rc != VWRN_NO_DIRENT_INFO)
1714 {
1715 //AssertFailed();
1716 if ( rc == VERR_NO_TRANSLATION
1717 || rc == VERR_INVALID_UTF8_ENCODING)
1718 continue;
1719 break;
1720 }
1721 }
1722
1723 cbNeeded = RT_OFFSETOF(SHFLDIRINFO, name.String);
1724 if (fUtf8)
1725 cbNeeded += pDirEntry->cbName + 1;
1726 else
1727 /* Overestimating, but that's ok */
1728 cbNeeded += (pDirEntry->cbName + 1) * 2;
1729
1730 if (cbBufferOrg < cbNeeded)
1731 {
1732 /* No room, so save this directory entry, or else it's lost forever */
1733 pHandle->dir.pLastValidEntry = pDirEntry;
1734
1735 if (*pcFiles == 0)
1736 {
1737 AssertFailed();
1738 return VINF_BUFFER_OVERFLOW; /* Return directly and don't free pDirEntry */
1739 }
1740 return VINF_SUCCESS; /* Return directly and don't free pDirEntry */
1741 }
1742
1743#ifdef RT_OS_WINDOWS
1744 pDirEntry->Info.Attr.fMode |= 0111;
1745#endif
1746 vbfsCopyFsObjInfoFromIprt(&pSFDEntry->Info, &pDirEntry->Info);
1747 pSFDEntry->cucShortName = 0;
1748
1749 if (fUtf8)
1750 {
1751 void *src, *dst;
1752
1753 src = &pDirEntry->szName[0];
1754 dst = &pSFDEntry->name.String.utf8[0];
1755
1756 memcpy(dst, src, pDirEntry->cbName + 1);
1757
1758 pSFDEntry->name.u16Size = pDirEntry->cbName + 1;
1759 pSFDEntry->name.u16Length = pDirEntry->cbName;
1760 }
1761 else
1762 {
1763 pSFDEntry->name.String.ucs2[0] = 0;
1764 pwszString = pSFDEntry->name.String.ucs2;
1765 int rc2 = RTStrToUtf16Ex(pDirEntry->szName, RTSTR_MAX, &pwszString, pDirEntry->cbName+1, NULL);
1766 AssertRC(rc2);
1767
1768#ifdef RT_OS_DARWIN
1769/** @todo This belongs in rtPathToNative or in the windows shared folder file system driver...
1770 * The question is simply whether the NFD normalization is actually applied on a (virtual) file
1771 * system level in darwin, or just by the user mode application libs. */
1772 {
1773 // Convert to
1774 // Normalization Form C (composed Unicode). We need this because
1775 // Mac OS X file system uses NFD (Normalization Form D :decomposed Unicode)
1776 // while most other OS', server-side programs usually expect NFC.
1777 uint16_t ucs2Length;
1778 CFRange rangeCharacters;
1779 CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, 0);
1780
1781 ::CFStringAppendCharacters(inStr, (UniChar *)pwszString, RTUtf16Len(pwszString));
1782 ::CFStringNormalize(inStr, kCFStringNormalizationFormC);
1783 ucs2Length = ::CFStringGetLength(inStr);
1784
1785 rangeCharacters.location = 0;
1786 rangeCharacters.length = ucs2Length;
1787 ::CFStringGetCharacters(inStr, rangeCharacters, pwszString);
1788 pwszString[ucs2Length] = 0x0000; // NULL terminated
1789
1790 CFRelease(inStr);
1791 }
1792#endif
1793 pSFDEntry->name.u16Length = (uint32_t)RTUtf16Len(pSFDEntry->name.String.ucs2) * 2;
1794 pSFDEntry->name.u16Size = pSFDEntry->name.u16Length + 2;
1795
1796 Log(("SHFL: File name size %d\n", pSFDEntry->name.u16Size));
1797 Log(("SHFL: File name %ls\n", &pSFDEntry->name.String.ucs2));
1798
1799 // adjust cbNeeded (it was overestimated before)
1800 cbNeeded = RT_OFFSETOF(SHFLDIRINFO, name.String) + pSFDEntry->name.u16Size;
1801 }
1802
1803 pSFDEntry = (PSHFLDIRINFO)((uintptr_t)pSFDEntry + cbNeeded);
1804 *pcbBuffer += cbNeeded;
1805 cbBufferOrg-= cbNeeded;
1806
1807 *pcFiles += 1;
1808
1809 /* Free the saved last entry, that we've just returned */
1810 if (pHandle->dir.pLastValidEntry)
1811 {
1812 RTMemFree(pHandle->dir.pLastValidEntry);
1813 pHandle->dir.pLastValidEntry = NULL;
1814
1815 /* And use the newly allocated buffer from now. */
1816 pDirEntry = pDirEntryOrg;
1817 }
1818
1819 if (flags & SHFL_LIST_RETURN_ONE)
1820 break; /* we're done */
1821 }
1822 Assert(rc != VINF_SUCCESS || *pcbBuffer > 0);
1823
1824end:
1825 if (pDirEntry)
1826 RTMemFree(pDirEntry);
1827
1828 return rc;
1829}
1830
1831#ifdef UNITTEST
1832/** Unit test the SHFL_FN_READLINK API. Located here as a form of API
1833 * documentation. */
1834void testReadLink(RTTEST hTest)
1835{
1836 /* If the number or types of parameters are wrong the API should fail. */
1837 testReadLinkBadParameters(hTest);
1838 /* Add tests as required... */
1839}
1840#endif
1841int vbsfReadLink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint8_t *pBuffer, uint32_t cbBuffer)
1842{
1843 int rc = VINF_SUCCESS;
1844
1845 if (pPath == 0 || pBuffer == 0)
1846 {
1847 AssertFailed();
1848 return VERR_INVALID_PARAMETER;
1849 }
1850
1851 /* Build a host full path for the given path, handle file name case issues
1852 * (if the guest expects case-insensitive paths but the host is
1853 * case-sensitive) and convert ucs2 to utf8 if necessary.
1854 */
1855 char *pszFullPath = NULL;
1856 uint32_t cbFullPathRoot = 0;
1857
1858 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot);
1859
1860 if (RT_SUCCESS(rc))
1861 {
1862 rc = RTSymlinkRead(pszFullPath, (char *) pBuffer, cbBuffer, 0);
1863 if (RT_SUCCESS(rc))
1864 {
1865 /* Convert the slashes in the link target to the guest path separator characters. */
1866 /** @todo r=bird: for some messed up reason, we return UTF-8 here rather than
1867 * the character set selected by the client. We also don't return the
1868 * length, so the clients are paranoid about the zero termination behavior. */
1869 char ch;
1870 char *psz = (char *)pBuffer;
1871 while ((ch = *psz) != '\0')
1872 {
1873 if (RTPATH_IS_SLASH(ch))
1874 *psz = pClient->PathDelimiter;
1875 psz++;
1876 }
1877 }
1878
1879 /* free the path string */
1880 vbsfFreeFullPath(pszFullPath);
1881 }
1882
1883 return rc;
1884}
1885
1886int vbsfQueryFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
1887 uint32_t *pcbBuffer, uint8_t *pBuffer)
1888{
1889 RT_NOREF1(flags);
1890 uint32_t type = vbsfQueryHandleType(pClient, Handle);
1891 int rc = VINF_SUCCESS;
1892 SHFLFSOBJINFO *pObjInfo = (SHFLFSOBJINFO *)pBuffer;
1893 RTFSOBJINFO fileinfo;
1894
1895
1896 AssertReturn(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE, VERR_INVALID_PARAMETER);
1897 AssertReturn(pcbBuffer != NULL, VERR_INVALID_PARAMETER);
1898 AssertReturn(pObjInfo != NULL, VERR_INVALID_PARAMETER);
1899 AssertReturn(*pcbBuffer >= sizeof(SHFLFSOBJINFO), VERR_INVALID_PARAMETER);
1900
1901 /** @todo other options */
1902 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_FILE));
1903
1904 *pcbBuffer = 0;
1905
1906 if (type == SHFL_HF_TYPE_DIR)
1907 {
1908 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
1909 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1910 if (RT_SUCCESS(rc))
1911 rc = RTDirQueryInfo(pHandle->dir.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1912 }
1913 else
1914 {
1915 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1916 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1917 if (RT_SUCCESS(rc))
1918 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1919#ifdef RT_OS_WINDOWS
1920 if (RT_SUCCESS(rc) && RTFS_IS_FILE(pObjInfo->Attr.fMode))
1921 pObjInfo->Attr.fMode |= 0111;
1922#endif
1923 }
1924 if (rc == VINF_SUCCESS)
1925 {
1926 vbfsCopyFsObjInfoFromIprt(pObjInfo, &fileinfo);
1927 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1928 }
1929 else
1930 AssertFailed();
1931
1932 return rc;
1933}
1934
1935static int vbsfSetFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
1936 uint32_t *pcbBuffer, uint8_t *pBuffer)
1937{
1938 RT_NOREF1(flags);
1939 uint32_t type = vbsfQueryHandleType(pClient, Handle);
1940 int rc = VINF_SUCCESS;
1941 SHFLFSOBJINFO *pSFDEntry;
1942
1943 if ( !(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE)
1944 || pcbBuffer == 0
1945 || pBuffer == 0
1946 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
1947 {
1948 AssertFailed();
1949 return VERR_INVALID_PARAMETER;
1950 }
1951
1952 *pcbBuffer = 0;
1953 pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
1954
1955 Assert(flags == (SHFL_INFO_SET | SHFL_INFO_FILE));
1956
1957 /* Change only the time values that are not zero */
1958 if (type == SHFL_HF_TYPE_DIR)
1959 {
1960 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
1961 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1962 if (RT_SUCCESS(rc))
1963 rc = RTDirSetTimes(pHandle->dir.Handle,
1964 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1965 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1966 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1967 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1968 );
1969 }
1970 else
1971 {
1972 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1973 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1974 if (RT_SUCCESS(rc))
1975 rc = RTFileSetTimes(pHandle->file.Handle,
1976 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1977 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1978 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1979 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1980 );
1981 }
1982 if (rc != VINF_SUCCESS)
1983 {
1984 Log(("RTFileSetTimes failed with %Rrc\n", rc));
1985 Log(("AccessTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->AccessTime)));
1986 Log(("ModificationTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ModificationTime)));
1987 Log(("ChangeTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ChangeTime)));
1988 Log(("BirthTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->BirthTime)));
1989 /* temporary hack */
1990 rc = VINF_SUCCESS;
1991 }
1992
1993 if (type == SHFL_HF_TYPE_FILE)
1994 {
1995 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1996 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1997 if (RT_SUCCESS(rc))
1998 {
1999 /* Change file attributes if necessary */
2000 if (pSFDEntry->Attr.fMode)
2001 {
2002 RTFMODE fMode = pSFDEntry->Attr.fMode;
2003
2004#ifndef RT_OS_WINDOWS
2005 /* Don't allow the guest to clear the own bit, otherwise the guest wouldn't be
2006 * able to access this file anymore. Only for guests, which set the UNIX mode.
2007 * Also, clear bits which we don't pass through for security reasons. */
2008 if (fMode & RTFS_UNIX_MASK)
2009 {
2010 fMode |= RTFS_UNIX_IRUSR;
2011 fMode &= ~(RTFS_UNIX_ISUID | RTFS_UNIX_ISGID | RTFS_UNIX_ISTXT);
2012 }
2013#endif
2014
2015 rc = RTFileSetMode(pHandle->file.Handle, fMode);
2016 if (rc != VINF_SUCCESS)
2017 {
2018 Log(("RTFileSetMode %x failed with %Rrc\n", fMode, rc));
2019 /* silent failure, because this tends to fail with e.g. windows guest & linux host */
2020 rc = VINF_SUCCESS;
2021 }
2022 }
2023 }
2024 }
2025 /** @todo mode for directories */
2026
2027 if (rc == VINF_SUCCESS)
2028 {
2029 uint32_t bufsize = sizeof(*pSFDEntry);
2030
2031 rc = vbsfQueryFileInfo(pClient, root, Handle, SHFL_INFO_GET|SHFL_INFO_FILE, &bufsize, (uint8_t *)pSFDEntry);
2032 if (rc == VINF_SUCCESS)
2033 {
2034 *pcbBuffer = sizeof(SHFLFSOBJINFO);
2035 }
2036 else
2037 AssertFailed();
2038 }
2039
2040 return rc;
2041}
2042
2043
2044/**
2045 * Handles SHFL_FN_SET_FILE_SIZE.
2046 */
2047int vbsfSetFileSize(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hHandle, uint64_t cbNewSize)
2048{
2049 /*
2050 * Resolve handle and validate write access.
2051 */
2052 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, hHandle);
2053 ASSERT_GUEST_RETURN(pHandle, VERR_INVALID_HANDLE);
2054
2055 int rc = vbsfCheckHandleAccess(pClient, idRoot, pHandle, VBSF_CHECK_ACCESS_WRITE);
2056 if (RT_SUCCESS(rc))
2057 {
2058 /*
2059 * Execute the request.
2060 */
2061 rc = RTFileSetSize(pHandle->file.Handle, cbNewSize);
2062 }
2063 return rc;
2064}
2065
2066
2067static int vbsfSetEndOfFile(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
2068 uint32_t *pcbBuffer, uint8_t *pBuffer)
2069{
2070 RT_NOREF1(flags);
2071 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
2072 SHFLFSOBJINFO *pSFDEntry;
2073
2074 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
2075 {
2076 AssertFailed();
2077 return VERR_INVALID_PARAMETER;
2078 }
2079
2080 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
2081 if (RT_SUCCESS(rc))
2082 { /* likely */ }
2083 else
2084 return rc;
2085
2086 *pcbBuffer = 0;
2087 pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
2088
2089 if (flags & SHFL_INFO_SIZE)
2090 {
2091 rc = RTFileSetSize(pHandle->file.Handle, pSFDEntry->cbObject);
2092 if (rc != VINF_SUCCESS)
2093 AssertFailed();
2094 }
2095 else
2096 AssertFailed();
2097
2098 if (rc == VINF_SUCCESS)
2099 {
2100 RTFSOBJINFO fileinfo;
2101
2102 /* Query the new object info and return it */
2103 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
2104 if (rc == VINF_SUCCESS)
2105 {
2106#ifdef RT_OS_WINDOWS
2107 fileinfo.Attr.fMode |= 0111;
2108#endif
2109 vbfsCopyFsObjInfoFromIprt(pSFDEntry, &fileinfo);
2110 *pcbBuffer = sizeof(SHFLFSOBJINFO);
2111 }
2112 else
2113 AssertFailed();
2114 }
2115
2116 return rc;
2117}
2118
2119int vbsfQueryVolumeInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
2120{
2121 RT_NOREF2(root, flags);
2122 int rc = VINF_SUCCESS;
2123 SHFLVOLINFO *pSFDEntry;
2124 char *pszFullPath = NULL;
2125 union
2126 {
2127 SHFLSTRING Dummy;
2128 uint8_t abDummy[SHFLSTRING_HEADER_SIZE + sizeof(RTUTF16)];
2129 } Buf;
2130
2131 if (pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLVOLINFO))
2132 {
2133 AssertFailed();
2134 return VERR_INVALID_PARAMETER;
2135 }
2136
2137 /** @todo other options */
2138 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_VOLUME));
2139
2140 *pcbBuffer = 0;
2141 pSFDEntry = (PSHFLVOLINFO)pBuffer;
2142
2143 ShflStringInitBuffer(&Buf.Dummy, sizeof(Buf));
2144 Buf.Dummy.String.ucs2[0] = '\0';
2145 rc = vbsfBuildFullPath(pClient, root, &Buf.Dummy, sizeof(Buf), &pszFullPath, NULL);
2146
2147 if (RT_SUCCESS(rc))
2148 {
2149 rc = RTFsQuerySizes(pszFullPath, &pSFDEntry->ullTotalAllocationBytes, &pSFDEntry->ullAvailableAllocationBytes, &pSFDEntry->ulBytesPerAllocationUnit, &pSFDEntry->ulBytesPerSector);
2150 if (rc != VINF_SUCCESS)
2151 goto exit;
2152
2153 rc = RTFsQuerySerial(pszFullPath, &pSFDEntry->ulSerial);
2154 if (rc != VINF_SUCCESS)
2155 goto exit;
2156
2157 RTFSPROPERTIES FsProperties;
2158 rc = RTFsQueryProperties(pszFullPath, &FsProperties);
2159 if (rc != VINF_SUCCESS)
2160 goto exit;
2161 vbfsCopyFsPropertiesFromIprt(&pSFDEntry->fsProperties, &FsProperties);
2162
2163 *pcbBuffer = sizeof(SHFLVOLINFO);
2164 }
2165 else AssertFailed();
2166
2167exit:
2168 AssertMsg(rc == VINF_SUCCESS, ("failure: rc = %Rrc\n", rc));
2169 /* free the path string */
2170 vbsfFreeFullPath(pszFullPath);
2171 return rc;
2172}
2173
2174int vbsfQueryFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
2175{
2176 if (pcbBuffer == 0 || pBuffer == 0)
2177 {
2178 AssertFailed();
2179 return VERR_INVALID_PARAMETER;
2180 }
2181
2182 if (flags & SHFL_INFO_FILE)
2183 return vbsfQueryFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2184
2185 if (flags & SHFL_INFO_VOLUME)
2186 return vbsfQueryVolumeInfo(pClient, root, flags, pcbBuffer, pBuffer);
2187
2188 AssertFailed();
2189 return VERR_INVALID_PARAMETER;
2190}
2191
2192#ifdef UNITTEST
2193/** Unit test the SHFL_FN_INFORMATION API. Located here as a form of API
2194 * documentation. */
2195void testFSInfo(RTTEST hTest)
2196{
2197 /* If the number or types of parameters are wrong the API should fail. */
2198 testFSInfoBadParameters(hTest);
2199 /* Basic get and set file size test. */
2200 testFSInfoQuerySetFMode(hTest);
2201 /* Basic get and set dir atime test. */
2202 testFSInfoQuerySetDirATime(hTest);
2203 /* Basic get and set file atime test. */
2204 testFSInfoQuerySetFileATime(hTest);
2205 /* Basic set end of file. */
2206 testFSInfoQuerySetEndOfFile(hTest);
2207 /* Add tests as required... */
2208}
2209#endif
2210int vbsfSetFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
2211{
2212 uint32_t type = vbsfQueryHandleType(pClient, Handle)
2213 & (SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE|SHFL_HF_TYPE_VOLUME);
2214
2215 if (type == 0 || pcbBuffer == 0 || pBuffer == 0)
2216 {
2217 AssertFailed();
2218 return VERR_INVALID_PARAMETER;
2219 }
2220
2221 if (flags & SHFL_INFO_FILE)
2222 return vbsfSetFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2223
2224 if (flags & SHFL_INFO_SIZE)
2225 return vbsfSetEndOfFile(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2226
2227// if (flags & SHFL_INFO_VOLUME)
2228// return vbsfVolumeInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2229 AssertFailed();
2230 return VERR_INVALID_PARAMETER;
2231}
2232
2233#ifdef UNITTEST
2234/** Unit test the SHFL_FN_LOCK API. Located here as a form of API
2235 * documentation. */
2236void testLock(RTTEST hTest)
2237{
2238 /* If the number or types of parameters are wrong the API should fail. */
2239 testLockBadParameters(hTest);
2240 /* Simple file locking and unlocking test. */
2241 testLockFileSimple(hTest);
2242 /* Add tests as required... */
2243}
2244#endif
2245
2246int vbsfLock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
2247{
2248 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
2249 uint32_t fRTLock = 0;
2250
2251 Assert((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL);
2252
2253 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
2254 if (RT_SUCCESS(rc))
2255 { /* likely */ }
2256 else
2257 return rc;
2258
2259 if ( ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL)
2260 || (flags & SHFL_LOCK_ENTIRE)
2261 )
2262 {
2263 AssertFailed();
2264 return VERR_INVALID_PARAMETER;
2265 }
2266
2267 /* Lock type */
2268 switch(flags & SHFL_LOCK_MODE_MASK)
2269 {
2270 case SHFL_LOCK_SHARED:
2271 fRTLock = RTFILE_LOCK_READ;
2272 break;
2273
2274 case SHFL_LOCK_EXCLUSIVE:
2275 fRTLock = RTFILE_LOCK_READ | RTFILE_LOCK_WRITE;
2276 break;
2277
2278 default:
2279 AssertFailed();
2280 return VERR_INVALID_PARAMETER;
2281 }
2282
2283 /* Lock wait type */
2284 if (flags & SHFL_LOCK_WAIT)
2285 fRTLock |= RTFILE_LOCK_WAIT;
2286 else
2287 fRTLock |= RTFILE_LOCK_IMMEDIATELY;
2288
2289#ifdef RT_OS_WINDOWS
2290 rc = RTFileLock(pHandle->file.Handle, fRTLock, offset, length);
2291 if (rc != VINF_SUCCESS)
2292 Log(("RTFileLock %RTfile %RX64 %RX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
2293#else
2294 Log(("vbsfLock: Pretend success handle=%x\n", Handle));
2295 rc = VINF_SUCCESS;
2296 RT_NOREF2(offset, length);
2297#endif
2298 return rc;
2299}
2300
2301int vbsfUnlock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
2302{
2303 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
2304
2305 Assert((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL);
2306
2307 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
2308 if (RT_SUCCESS(rc))
2309 { /* likely */ }
2310 else
2311 return rc;
2312
2313 if ( ((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL)
2314 || (flags & SHFL_LOCK_ENTIRE)
2315 )
2316 {
2317 return VERR_INVALID_PARAMETER;
2318 }
2319
2320#ifdef RT_OS_WINDOWS
2321 rc = RTFileUnlock(pHandle->file.Handle, offset, length);
2322 if (rc != VINF_SUCCESS)
2323 Log(("RTFileUnlock %RTfile %RX64 %RTX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
2324#else
2325 Log(("vbsfUnlock: Pretend success handle=%x\n", Handle));
2326 rc = VINF_SUCCESS;
2327 RT_NOREF2(offset, length);
2328#endif
2329
2330 return rc;
2331}
2332
2333
2334#ifdef UNITTEST
2335/** Unit test the SHFL_FN_REMOVE API. Located here as a form of API
2336 * documentation. */
2337void testRemove(RTTEST hTest)
2338{
2339 /* If the number or types of parameters are wrong the API should fail. */
2340 testRemoveBadParameters(hTest);
2341 /* Add tests as required... */
2342}
2343#endif
2344int vbsfRemove(SHFLCLIENTDATA *pClient, SHFLROOT root, PCSHFLSTRING pPath, uint32_t cbPath, uint32_t flags, SHFLHANDLE hToClose)
2345{
2346
2347 /* Validate input */
2348 Assert(pPath);
2349 AssertReturn(pPath->u16Size > 0, VERR_INVALID_PARAMETER);
2350
2351 /*
2352 * Close the handle if specified.
2353 */
2354 int rc = VINF_SUCCESS;
2355 if (hToClose != SHFL_HANDLE_NIL)
2356 rc = vbsfClose(pClient, root, hToClose);
2357 if (RT_SUCCESS(rc))
2358 {
2359 /*
2360 * Build a host full path for the given path and convert ucs2 to utf8 if necessary.
2361 */
2362 char *pszFullPath = NULL;
2363 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, NULL);
2364 if (RT_SUCCESS(rc))
2365 {
2366 /*
2367 * Is the guest allowed to write to this share?
2368 */
2369 bool fWritable;
2370 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
2371 if (RT_SUCCESS(rc) && fWritable)
2372 {
2373 /*
2374 * Do the removal/deletion according to the type flags.
2375 */
2376 if (flags & SHFL_REMOVE_SYMLINK)
2377 rc = RTSymlinkDelete(pszFullPath, 0);
2378 else if (flags & SHFL_REMOVE_FILE)
2379 rc = RTFileDelete(pszFullPath);
2380 else
2381 rc = RTDirRemove(pszFullPath);
2382
2383#if 0 //ndef RT_OS_WINDOWS
2384 /* There are a few adjustments to be made here: */
2385 if ( rc == VERR_FILE_NOT_FOUND
2386 && SHFL_CLIENT_NEED_WINDOWS_ERROR_STYLE_ADJUST_ON_POSIX(pClient)
2387 && vbsfErrorStyleIsWindowsPathNotFound(pszFullPath))
2388 rc = VERR_PATH_NOT_FOUND;
2389 else if ( rc == VERR_PATH_NOT_FOUND
2390 && SHFL_CLIENT_NEED_WINDOWS_ERROR_STYLE_ADJUST_ON_POSIX(pClient))
2391 {
2392 if (flags & (SHFL_REMOVE_FILE | SHFL_REMOVE_SYMLINK))
2393 {
2394 size_t cchFullPath = strlen(pszFullPath);
2395 if (cchFullPath > 0 && RTPATH_IS_SLASH(pszFullPath[cchFullPath - 1]))
2396 rc = VERR_INVALID_NAME;
2397 }
2398 else if (vbsfErrorStyleIsWindowsNotADirectory(pszFullPath))
2399 rc = VERR_NOT_A_DIRECTORY;
2400 }
2401#endif
2402 }
2403 else
2404 rc = VERR_WRITE_PROTECT;
2405
2406 /* free the path string */
2407 vbsfFreeFullPath(pszFullPath);
2408 }
2409 }
2410 return rc;
2411}
2412
2413
2414#ifdef UNITTEST
2415/** Unit test the SHFL_FN_RENAME API. Located here as a form of API
2416 * documentation. */
2417void testRename(RTTEST hTest)
2418{
2419 /* If the number or types of parameters are wrong the API should fail. */
2420 testRenameBadParameters(hTest);
2421 /* Add tests as required... */
2422}
2423#endif
2424int vbsfRename(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pSrc, SHFLSTRING *pDest, uint32_t flags)
2425{
2426 int rc = VINF_SUCCESS;
2427
2428 /* Validate input */
2429 if ( flags & ~(SHFL_RENAME_FILE|SHFL_RENAME_DIR|SHFL_RENAME_REPLACE_IF_EXISTS)
2430 || pSrc == 0
2431 || pDest == 0)
2432 {
2433 AssertFailed();
2434 return VERR_INVALID_PARAMETER;
2435 }
2436
2437 /* Build a host full path for the given path
2438 * and convert ucs2 to utf8 if necessary.
2439 */
2440 char *pszFullPathSrc = NULL;
2441 char *pszFullPathDest = NULL;
2442
2443 rc = vbsfBuildFullPath(pClient, root, pSrc, pSrc->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPathSrc, NULL);
2444 if (rc != VINF_SUCCESS)
2445 return rc;
2446
2447 rc = vbsfBuildFullPath(pClient, root, pDest, pDest->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPathDest, NULL, false, true);
2448 if (RT_SUCCESS (rc))
2449 {
2450 Log(("Rename %s to %s\n", pszFullPathSrc, pszFullPathDest));
2451
2452 /* is the guest allowed to write to this share? */
2453 bool fWritable;
2454 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
2455 if (RT_FAILURE(rc) || !fWritable)
2456 rc = VERR_WRITE_PROTECT;
2457
2458 if (RT_SUCCESS(rc))
2459 {
2460 if ((flags & (SHFL_RENAME_FILE | SHFL_RENAME_DIR)) == (SHFL_RENAME_FILE | SHFL_RENAME_DIR))
2461 {
2462 rc = RTPathRename(pszFullPathSrc, pszFullPathDest,
2463 flags & SHFL_RENAME_REPLACE_IF_EXISTS ? RTPATHRENAME_FLAGS_REPLACE : 0);
2464 }
2465 else if (flags & SHFL_RENAME_FILE)
2466 {
2467 rc = RTFileMove(pszFullPathSrc, pszFullPathDest,
2468 ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTFILEMOVE_FLAGS_REPLACE : 0));
2469 }
2470 else
2471 {
2472 /* NT ignores the REPLACE flag and simply return and already exists error. */
2473 rc = RTDirRename(pszFullPathSrc, pszFullPathDest,
2474 ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTPATHRENAME_FLAGS_REPLACE : 0));
2475 }
2476#ifndef RT_OS_WINDOWS
2477 if ( rc == VERR_FILE_NOT_FOUND
2478 && SHFL_CLIENT_NEED_WINDOWS_ERROR_STYLE_ADJUST_ON_POSIX(pClient)
2479 && vbsfErrorStyleIsWindowsPathNotFound2(pszFullPathSrc, pszFullPathDest))
2480 rc = VERR_PATH_NOT_FOUND;
2481#endif
2482 }
2483
2484 /* free the path string */
2485 vbsfFreeFullPath(pszFullPathDest);
2486 }
2487 /* free the path string */
2488 vbsfFreeFullPath(pszFullPathSrc);
2489 return rc;
2490}
2491
2492/**
2493 * Implements SHFL_FN_COPY_FILE (wrapping RTFileCopy).
2494 */
2495int vbsfCopyFile(SHFLCLIENTDATA *pClient, SHFLROOT idRootSrc, PCSHFLSTRING pStrPathSrc,
2496 SHFLROOT idRootDst, PCSHFLSTRING pStrPathDst, uint32_t fFlags)
2497{
2498 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
2499 if (pClient->fu32Flags & SHFL_CF_UTF8)
2500 LogFunc(("pClient %p, idRootSrc %#RX32, '%.*s', idRootSrc %#RX32, '%.*s', fFlags %#x\n", pClient, idRootSrc,
2501 pStrPathSrc->u16Length, pStrPathSrc->String.ach, idRootDst, pStrPathDst->u16Length, pStrPathDst->String.ach, fFlags));
2502 else
2503 LogFunc(("pClient %p, idRootSrc %#RX32, '%.*ls', idRootSrc %#RX32, '%.*ls', fFlags %#x\n", pClient,
2504 idRootSrc, pStrPathSrc->u16Length / sizeof(RTUTF16), pStrPathSrc->String.ach,
2505 idRootDst, pStrPathDst->u16Length / sizeof(RTUTF16), pStrPathDst->String.ach, fFlags));
2506
2507 /*
2508 * Build host paths.
2509 */
2510 char *pszPathSrc = NULL;
2511 int rc = vbsfBuildFullPath(pClient, idRootSrc, pStrPathSrc, pStrPathSrc->u16Size + SHFLSTRING_HEADER_SIZE, &pszPathSrc, NULL);
2512 if (RT_SUCCESS(rc))
2513 {
2514 char *pszPathDst = NULL;
2515 rc = vbsfBuildFullPath(pClient, idRootDst, pStrPathDst, pStrPathDst->u16Size + SHFLSTRING_HEADER_SIZE, &pszPathDst, NULL);
2516 if (RT_SUCCESS(rc))
2517 {
2518 /*
2519 * Do the job.
2520 */
2521 rc = RTFileCopy(pszPathSrc, pszPathDst);
2522
2523 vbsfFreeFullPath(pszPathDst);
2524 }
2525 vbsfFreeFullPath(pszPathSrc);
2526 }
2527
2528 RT_NOREF(fFlags);
2529 LogFunc(("returns %Rrc\n", rc));
2530 return rc;
2531}
2532
2533#ifdef UNITTEST
2534/** Unit test the SHFL_FN_SYMLINK API. Located here as a form of API
2535 * documentation. */
2536void testSymlink(RTTEST hTest)
2537{
2538 /* If the number or types of parameters are wrong the API should fail. */
2539 testSymlinkBadParameters(hTest);
2540 /* Add tests as required... */
2541}
2542#endif
2543int vbsfSymlink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pNewPath, SHFLSTRING *pOldPath, SHFLFSOBJINFO *pInfo)
2544{
2545 int rc = VINF_SUCCESS;
2546
2547 char *pszFullNewPath = NULL;
2548 char *pszFullOldPath = NULL;
2549
2550 /* XXX: no support for UCS2 at the moment. */
2551 if (!BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
2552 return VERR_NOT_IMPLEMENTED;
2553
2554 bool fSymlinksCreate;
2555 rc = vbsfMappingsQuerySymlinksCreate(pClient, root, &fSymlinksCreate);
2556 AssertRCReturn(rc, rc);
2557 if (!fSymlinksCreate)
2558 return VERR_WRITE_PROTECT; /* XXX or VERR_TOO_MANY_SYMLINKS? */
2559
2560 rc = vbsfBuildFullPath(pClient, root, pNewPath, pNewPath->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullNewPath, NULL);
2561 AssertRCReturn(rc, rc);
2562
2563 /* Verify that the link target can be a valid host path, i.e. does not contain invalid characters. */
2564 uint32_t fu32PathFlags = 0;
2565 uint32_t fu32Options = 0;
2566 rc = vbsfPathGuestToHost(pClient, root, pOldPath, pOldPath->u16Size + SHFLSTRING_HEADER_SIZE,
2567 &pszFullOldPath, NULL, fu32Options, &fu32PathFlags);
2568 if (RT_FAILURE(rc))
2569 {
2570 vbsfFreeFullPath(pszFullNewPath);
2571 return rc;
2572 }
2573
2574 /** @todo r=bird: We _must_ perform slash conversion on the target (what this
2575 * code calls 'pOldPath' for some peculiar reason)! */
2576
2577 rc = RTSymlinkCreate(pszFullNewPath, (const char *)pOldPath->String.utf8,
2578 RTSYMLINKTYPE_UNKNOWN, 0);
2579 if (RT_SUCCESS(rc))
2580 {
2581 RTFSOBJINFO info;
2582 rc = RTPathQueryInfoEx(pszFullNewPath, &info, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
2583 if (RT_SUCCESS(rc))
2584 vbfsCopyFsObjInfoFromIprt(pInfo, &info);
2585 }
2586
2587 vbsfFreeFullPath(pszFullOldPath);
2588 vbsfFreeFullPath(pszFullNewPath);
2589
2590 return rc;
2591}
2592
2593/*
2594 * Clean up our mess by freeing all handles that are still valid.
2595 *
2596 */
2597int vbsfDisconnect(SHFLCLIENTDATA *pClient)
2598{
2599 for (int i = 0; i < SHFLHANDLE_MAX; ++i)
2600 {
2601 SHFLFILEHANDLE *pHandle = NULL;
2602 SHFLHANDLE Handle = (SHFLHANDLE)i;
2603
2604 uint32_t type = vbsfQueryHandleType(pClient, Handle);
2605 switch (type & (SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE))
2606 {
2607 case SHFL_HF_TYPE_DIR:
2608 {
2609 pHandle = vbsfQueryDirHandle(pClient, Handle);
2610 break;
2611 }
2612 case SHFL_HF_TYPE_FILE:
2613 {
2614 pHandle = vbsfQueryFileHandle(pClient, Handle);
2615 break;
2616 }
2617 default:
2618 break;
2619 }
2620
2621 if (pHandle)
2622 {
2623 LogFunc(("Opened handle 0x%08x\n", i));
2624 vbsfClose(pClient, pHandle->root, Handle);
2625 }
2626 }
2627
2628 for (uint32_t i = 0; i < RT_ELEMENTS(pClient->acMappings); i++)
2629 if (pClient->acMappings[i])
2630 {
2631 uint16_t cMappings = pClient->acMappings[i];
2632 while (cMappings-- > 0)
2633 vbsfUnmapFolder(pClient, i);
2634 }
2635
2636 return VINF_SUCCESS;
2637}
2638
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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