VirtualBox

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

Last change on this file since 93075 was 93075, checked in by vboxsync, 3 years ago

SharedFoldersSvc: Copy out the short name to help run DOS and WinOS2 programs on Windows hosts. Seems we never returned these, always setting cucShortName to zero. ticketref:19453

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 89.9 KB
Line 
1/* $Id: vbsf.cpp 93075 2021-12-24 16:11:55Z 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
1757 /* The shortname (only used by OS/2 atm): */
1758 Assert(pDirEntry->cwcShortName < RT_ELEMENTS(pSFDEntry->uszShortName));
1759 Assert(pDirEntry->wszShortName[pDirEntry->cwcShortName] == '\0');
1760 pSFDEntry->cucShortName = pDirEntry->cwcShortName;
1761 if (pDirEntry->cwcShortName)
1762 memcpy(pSFDEntry->uszShortName, pDirEntry->wszShortName, sizeof(pSFDEntry->uszShortName));
1763
1764 /* The name: */
1765 if (fUtf8)
1766 {
1767 void *src, *dst;
1768
1769 src = &pDirEntry->szName[0];
1770 dst = &pSFDEntry->name.String.utf8[0];
1771
1772 memcpy(dst, src, pDirEntry->cbName + 1);
1773
1774 pSFDEntry->name.u16Size = pDirEntry->cbName + 1;
1775 pSFDEntry->name.u16Length = pDirEntry->cbName;
1776 }
1777 else
1778 {
1779 pSFDEntry->name.String.ucs2[0] = 0;
1780 pwszString = pSFDEntry->name.String.ucs2;
1781 int rc2 = RTStrToUtf16Ex(pDirEntry->szName, RTSTR_MAX, &pwszString, pDirEntry->cbName+1, NULL);
1782 AssertRC(rc2);
1783
1784#ifdef RT_OS_DARWIN
1785/** @todo This belongs in rtPathToNative or in the windows shared folder file system driver...
1786 * The question is simply whether the NFD normalization is actually applied on a (virtual) file
1787 * system level in darwin, or just by the user mode application libs. */
1788 {
1789 // Convert to
1790 // Normalization Form C (composed Unicode). We need this because
1791 // Mac OS X file system uses NFD (Normalization Form D :decomposed Unicode)
1792 // while most other OS', server-side programs usually expect NFC.
1793 uint16_t ucs2Length;
1794 CFRange rangeCharacters;
1795 CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, 0);
1796
1797 ::CFStringAppendCharacters(inStr, (UniChar *)pwszString, RTUtf16Len(pwszString));
1798 ::CFStringNormalize(inStr, kCFStringNormalizationFormC);
1799 ucs2Length = ::CFStringGetLength(inStr);
1800
1801 rangeCharacters.location = 0;
1802 rangeCharacters.length = ucs2Length;
1803 ::CFStringGetCharacters(inStr, rangeCharacters, pwszString);
1804 pwszString[ucs2Length] = 0x0000; // NULL terminated
1805
1806 CFRelease(inStr);
1807 }
1808#endif
1809 pSFDEntry->name.u16Length = (uint32_t)RTUtf16Len(pSFDEntry->name.String.ucs2) * 2;
1810 pSFDEntry->name.u16Size = pSFDEntry->name.u16Length + 2;
1811
1812 Log(("SHFL: File name size %d\n", pSFDEntry->name.u16Size));
1813 Log(("SHFL: File name %ls\n", &pSFDEntry->name.String.ucs2));
1814
1815 // adjust cbNeeded (it was overestimated before)
1816 cbNeeded = RT_OFFSETOF(SHFLDIRINFO, name.String) + pSFDEntry->name.u16Size;
1817 }
1818
1819 /* Advance */
1820 pSFDEntry = (PSHFLDIRINFO)((uintptr_t)pSFDEntry + cbNeeded);
1821 *pcbBuffer += cbNeeded;
1822 cbBufferOrg-= cbNeeded;
1823
1824 *pcFiles += 1;
1825
1826 /* Free the saved last entry, that we've just returned */
1827 if (pHandle->dir.pLastValidEntry)
1828 {
1829 RTMemFree(pHandle->dir.pLastValidEntry);
1830 pHandle->dir.pLastValidEntry = NULL;
1831
1832 /* And use the newly allocated buffer from now. */
1833 pDirEntry = pDirEntryOrg;
1834 }
1835
1836 if (flags & SHFL_LIST_RETURN_ONE)
1837 break; /* we're done */
1838 }
1839 Assert(rc != VINF_SUCCESS || *pcbBuffer > 0);
1840
1841end:
1842 if (pDirEntry)
1843 RTMemFree(pDirEntry);
1844
1845 return rc;
1846}
1847
1848#ifdef UNITTEST
1849/** Unit test the SHFL_FN_READLINK API. Located here as a form of API
1850 * documentation. */
1851void testReadLink(RTTEST hTest)
1852{
1853 /* If the number or types of parameters are wrong the API should fail. */
1854 testReadLinkBadParameters(hTest);
1855 /* Add tests as required... */
1856}
1857#endif
1858int vbsfReadLink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint8_t *pBuffer, uint32_t cbBuffer)
1859{
1860 int rc = VINF_SUCCESS;
1861
1862 if (pPath == 0 || pBuffer == 0)
1863 {
1864 AssertFailed();
1865 return VERR_INVALID_PARAMETER;
1866 }
1867
1868 /* Build a host full path for the given path, handle file name case issues
1869 * (if the guest expects case-insensitive paths but the host is
1870 * case-sensitive) and convert ucs2 to utf8 if necessary.
1871 */
1872 char *pszFullPath = NULL;
1873 uint32_t cbFullPathRoot = 0;
1874
1875 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot);
1876
1877 if (RT_SUCCESS(rc))
1878 {
1879 rc = RTSymlinkRead(pszFullPath, (char *) pBuffer, cbBuffer, 0);
1880 if (RT_SUCCESS(rc))
1881 {
1882 /* Convert the slashes in the link target to the guest path separator characters. */
1883 /** @todo r=bird: for some messed up reason, we return UTF-8 here rather than
1884 * the character set selected by the client. We also don't return the
1885 * length, so the clients are paranoid about the zero termination behavior. */
1886 char ch;
1887 char *psz = (char *)pBuffer;
1888 while ((ch = *psz) != '\0')
1889 {
1890 if (RTPATH_IS_SLASH(ch))
1891 *psz = pClient->PathDelimiter;
1892 psz++;
1893 }
1894 }
1895
1896 /* free the path string */
1897 vbsfFreeFullPath(pszFullPath);
1898 }
1899
1900 return rc;
1901}
1902
1903int vbsfQueryFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
1904 uint32_t *pcbBuffer, uint8_t *pBuffer)
1905{
1906 RT_NOREF1(flags);
1907 uint32_t type = vbsfQueryHandleType(pClient, Handle);
1908 int rc = VINF_SUCCESS;
1909 SHFLFSOBJINFO *pObjInfo = (SHFLFSOBJINFO *)pBuffer;
1910 RTFSOBJINFO fileinfo;
1911
1912
1913 AssertReturn(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE, VERR_INVALID_PARAMETER);
1914 AssertReturn(pcbBuffer != NULL, VERR_INVALID_PARAMETER);
1915 AssertReturn(pObjInfo != NULL, VERR_INVALID_PARAMETER);
1916 AssertReturn(*pcbBuffer >= sizeof(SHFLFSOBJINFO), VERR_INVALID_PARAMETER);
1917
1918 /** @todo other options */
1919 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_FILE));
1920
1921 *pcbBuffer = 0;
1922
1923 if (type == SHFL_HF_TYPE_DIR)
1924 {
1925 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
1926 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1927 if (RT_SUCCESS(rc))
1928 rc = RTDirQueryInfo(pHandle->dir.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1929 }
1930 else
1931 {
1932 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1933 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1934 if (RT_SUCCESS(rc))
1935 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1936#ifdef RT_OS_WINDOWS
1937 if (RT_SUCCESS(rc) && RTFS_IS_FILE(pObjInfo->Attr.fMode))
1938 pObjInfo->Attr.fMode |= 0111;
1939#endif
1940 }
1941 if (rc == VINF_SUCCESS)
1942 {
1943 vbfsCopyFsObjInfoFromIprt(pObjInfo, &fileinfo);
1944 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1945 }
1946 else
1947 AssertFailed();
1948
1949 return rc;
1950}
1951
1952static int vbsfSetFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
1953 uint32_t *pcbBuffer, uint8_t *pBuffer)
1954{
1955 RT_NOREF1(flags);
1956 uint32_t type = vbsfQueryHandleType(pClient, Handle);
1957 int rc = VINF_SUCCESS;
1958 SHFLFSOBJINFO *pSFDEntry;
1959
1960 if ( !(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE)
1961 || pcbBuffer == 0
1962 || pBuffer == 0
1963 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
1964 {
1965 AssertFailed();
1966 return VERR_INVALID_PARAMETER;
1967 }
1968
1969 *pcbBuffer = 0;
1970 pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
1971
1972 Assert(flags == (SHFL_INFO_SET | SHFL_INFO_FILE));
1973
1974 /*
1975 * Get the handle.
1976 */
1977 SHFLFILEHANDLE *pHandle;
1978 if (type == SHFL_HF_TYPE_FILE)
1979 {
1980 pHandle = vbsfQueryFileHandle(pClient, Handle);
1981 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1982 }
1983 else
1984 {
1985 Assert(type == SHFL_HF_TYPE_DIR);
1986 pHandle = vbsfQueryDirHandle(pClient, Handle);
1987 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1988 }
1989 if (RT_SUCCESS(rc))
1990 {
1991 /*
1992 * Any times to set?
1993 */
1994 if ( RTTimeSpecGetNano(&pSFDEntry->AccessTime)
1995 || RTTimeSpecGetNano(&pSFDEntry->ModificationTime)
1996 || RTTimeSpecGetNano(&pSFDEntry->ChangeTime)
1997 || RTTimeSpecGetNano(&pSFDEntry->BirthTime))
1998 {
1999
2000 /* Change only the time values that are not zero */
2001 if (type == SHFL_HF_TYPE_FILE)
2002 rc = RTFileSetTimes(pHandle->file.Handle,
2003 RTTimeSpecGetNano(&pSFDEntry->AccessTime) ? &pSFDEntry->AccessTime : NULL,
2004 RTTimeSpecGetNano(&pSFDEntry->ModificationTime) ? &pSFDEntry->ModificationTime : NULL,
2005 RTTimeSpecGetNano(&pSFDEntry->ChangeTime) ? &pSFDEntry->ChangeTime : NULL,
2006 RTTimeSpecGetNano(&pSFDEntry->BirthTime) ? &pSFDEntry->BirthTime : NULL);
2007 else
2008 rc = RTDirSetTimes( pHandle->dir.Handle,
2009 RTTimeSpecGetNano(&pSFDEntry->AccessTime) ? &pSFDEntry->AccessTime : NULL,
2010 RTTimeSpecGetNano(&pSFDEntry->ModificationTime) ? &pSFDEntry->ModificationTime : NULL,
2011 RTTimeSpecGetNano(&pSFDEntry->ChangeTime) ? &pSFDEntry->ChangeTime : NULL,
2012 RTTimeSpecGetNano(&pSFDEntry->BirthTime) ? &pSFDEntry->BirthTime : NULL);
2013 if (RT_FAILURE(rc))
2014 {
2015 Log(("RT%sSetTimes failed with %Rrc\n", type == SHFL_HF_TYPE_FILE ? "File" : "Dir", rc));
2016 Log(("AccessTime %#RX64\n", RTTimeSpecGetNano(&pSFDEntry->AccessTime)));
2017 Log(("ModificationTime %#RX64\n", RTTimeSpecGetNano(&pSFDEntry->ModificationTime)));
2018 Log(("ChangeTime %#RX64\n", RTTimeSpecGetNano(&pSFDEntry->ChangeTime)));
2019 Log(("BirthTime %#RX64\n", RTTimeSpecGetNano(&pSFDEntry->BirthTime)));
2020 /* "temporary" hack */
2021 rc = VINF_SUCCESS;
2022 }
2023 }
2024
2025 /*
2026 * Any mode changes?
2027 */
2028 if (pSFDEntry->Attr.fMode)
2029 {
2030 RTFMODE fMode = pSFDEntry->Attr.fMode;
2031
2032 if (type == SHFL_HF_TYPE_FILE)
2033 {
2034#ifndef RT_OS_WINDOWS
2035 /* Don't allow the guest to clear the read own bit, otherwise the guest wouldn't
2036 * be able to access this file anymore. Only for guests, which set the UNIX mode.
2037 * Also, clear bits which we don't pass through for security reasons. */
2038 if (fMode & RTFS_UNIX_MASK)
2039 {
2040 fMode |= RTFS_UNIX_IRUSR;
2041 fMode &= ~(RTFS_UNIX_ISUID | RTFS_UNIX_ISGID | RTFS_UNIX_ISTXT);
2042 }
2043#endif
2044 rc = RTFileSetMode(pHandle->file.Handle, fMode);
2045 }
2046 else
2047 {
2048#ifndef RT_OS_WINDOWS
2049 /* Don't allow the guest to clear the read+execute own bits, otherwise the guest
2050 * wouldn't be able to access this directory anymore. Only for guests, which set
2051 * the UNIX mode. Also, clear bits which we don't pass through for security reasons. */
2052 if (fMode & RTFS_UNIX_MASK)
2053 {
2054 fMode |= RTFS_UNIX_IRUSR | RTFS_UNIX_IXUSR;
2055 fMode &= ~(RTFS_UNIX_ISUID | RTFS_UNIX_ISGID | RTFS_UNIX_ISTXT /*?*/);
2056 }
2057#endif
2058 rc = RTDirSetMode(pHandle->dir.Handle, fMode);
2059 }
2060 if (RT_FAILURE(rc))
2061 {
2062 Log(("RT%sSetMode %#x (%#x) failed with %Rrc\n", type == SHFL_HF_TYPE_FILE ? "File" : "Dir",
2063 fMode, pSFDEntry->Attr.fMode, rc));
2064 /* silent failure, because this tends to fail with e.g. windows guest & linux host */
2065 rc = VINF_SUCCESS;
2066 }
2067 }
2068
2069 /*
2070 * Return the current file info on success.
2071 */
2072 if (RT_SUCCESS(rc))
2073 {
2074 uint32_t bufsize = sizeof(*pSFDEntry);
2075 rc = vbsfQueryFileInfo(pClient, root, Handle, SHFL_INFO_GET | SHFL_INFO_FILE, &bufsize, (uint8_t *)pSFDEntry);
2076 if (RT_SUCCESS(rc))
2077 *pcbBuffer = sizeof(SHFLFSOBJINFO);
2078 else
2079 AssertFailed();
2080 }
2081 }
2082 return rc;
2083}
2084
2085
2086/**
2087 * Handles SHFL_FN_SET_FILE_SIZE.
2088 */
2089int vbsfSetFileSize(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hHandle, uint64_t cbNewSize)
2090{
2091 /*
2092 * Resolve handle and validate write access.
2093 */
2094 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, hHandle);
2095 ASSERT_GUEST_RETURN(pHandle, VERR_INVALID_HANDLE);
2096
2097 int rc = vbsfCheckHandleAccess(pClient, idRoot, pHandle, VBSF_CHECK_ACCESS_WRITE);
2098 if (RT_SUCCESS(rc))
2099 {
2100 /*
2101 * Execute the request.
2102 */
2103 rc = RTFileSetSize(pHandle->file.Handle, cbNewSize);
2104 }
2105 return rc;
2106}
2107
2108
2109static int vbsfSetEndOfFile(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
2110 uint32_t *pcbBuffer, uint8_t *pBuffer)
2111{
2112 RT_NOREF1(flags);
2113 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
2114 SHFLFSOBJINFO *pSFDEntry;
2115
2116 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
2117 {
2118 AssertFailed();
2119 return VERR_INVALID_PARAMETER;
2120 }
2121
2122 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
2123 if (RT_SUCCESS(rc))
2124 { /* likely */ }
2125 else
2126 return rc;
2127
2128 *pcbBuffer = 0;
2129 pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
2130
2131 if (flags & SHFL_INFO_SIZE)
2132 {
2133 rc = RTFileSetSize(pHandle->file.Handle, pSFDEntry->cbObject);
2134 if (rc != VINF_SUCCESS)
2135 AssertFailed();
2136 }
2137 else
2138 AssertFailed();
2139
2140 if (rc == VINF_SUCCESS)
2141 {
2142 RTFSOBJINFO fileinfo;
2143
2144 /* Query the new object info and return it */
2145 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
2146 if (rc == VINF_SUCCESS)
2147 {
2148#ifdef RT_OS_WINDOWS
2149 fileinfo.Attr.fMode |= 0111;
2150#endif
2151 vbfsCopyFsObjInfoFromIprt(pSFDEntry, &fileinfo);
2152 *pcbBuffer = sizeof(SHFLFSOBJINFO);
2153 }
2154 else
2155 AssertFailed();
2156 }
2157
2158 return rc;
2159}
2160
2161int vbsfQueryVolumeInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
2162{
2163 RT_NOREF2(root, flags);
2164 int rc = VINF_SUCCESS;
2165 SHFLVOLINFO *pSFDEntry;
2166 char *pszFullPath = NULL;
2167 union
2168 {
2169 SHFLSTRING Dummy;
2170 uint8_t abDummy[SHFLSTRING_HEADER_SIZE + sizeof(RTUTF16)];
2171 } Buf;
2172
2173 if (pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLVOLINFO))
2174 {
2175 AssertFailed();
2176 return VERR_INVALID_PARAMETER;
2177 }
2178
2179 /** @todo other options */
2180 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_VOLUME));
2181
2182 *pcbBuffer = 0;
2183 pSFDEntry = (PSHFLVOLINFO)pBuffer;
2184
2185 ShflStringInitBuffer(&Buf.Dummy, sizeof(Buf));
2186 Buf.Dummy.String.ucs2[0] = '\0';
2187 rc = vbsfBuildFullPath(pClient, root, &Buf.Dummy, sizeof(Buf), &pszFullPath, NULL);
2188
2189 if (RT_SUCCESS(rc))
2190 {
2191 rc = RTFsQuerySizes(pszFullPath, &pSFDEntry->ullTotalAllocationBytes, &pSFDEntry->ullAvailableAllocationBytes, &pSFDEntry->ulBytesPerAllocationUnit, &pSFDEntry->ulBytesPerSector);
2192 if (rc != VINF_SUCCESS)
2193 goto exit;
2194
2195 rc = RTFsQuerySerial(pszFullPath, &pSFDEntry->ulSerial);
2196 if (rc != VINF_SUCCESS)
2197 goto exit;
2198
2199 RTFSPROPERTIES FsProperties;
2200 rc = RTFsQueryProperties(pszFullPath, &FsProperties);
2201 if (rc != VINF_SUCCESS)
2202 goto exit;
2203 vbfsCopyFsPropertiesFromIprt(&pSFDEntry->fsProperties, &FsProperties);
2204
2205 *pcbBuffer = sizeof(SHFLVOLINFO);
2206 }
2207 else AssertFailed();
2208
2209exit:
2210 AssertMsg(rc == VINF_SUCCESS, ("failure: rc = %Rrc\n", rc));
2211 /* free the path string */
2212 vbsfFreeFullPath(pszFullPath);
2213 return rc;
2214}
2215
2216int vbsfQueryFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
2217{
2218 if (pcbBuffer == 0 || pBuffer == 0)
2219 {
2220 AssertFailed();
2221 return VERR_INVALID_PARAMETER;
2222 }
2223
2224 if (flags & SHFL_INFO_FILE)
2225 return vbsfQueryFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2226
2227 if (flags & SHFL_INFO_VOLUME)
2228 return vbsfQueryVolumeInfo(pClient, root, flags, pcbBuffer, pBuffer);
2229
2230 AssertFailed();
2231 return VERR_INVALID_PARAMETER;
2232}
2233
2234#ifdef UNITTEST
2235/** Unit test the SHFL_FN_INFORMATION API. Located here as a form of API
2236 * documentation. */
2237void testFSInfo(RTTEST hTest)
2238{
2239 /* If the number or types of parameters are wrong the API should fail. */
2240 testFSInfoBadParameters(hTest);
2241 /* Basic get and set file size test. */
2242 testFSInfoQuerySetFMode(hTest);
2243 /* Basic get and set dir atime test. */
2244 testFSInfoQuerySetDirATime(hTest);
2245 /* Basic get and set file atime test. */
2246 testFSInfoQuerySetFileATime(hTest);
2247 /* Basic set end of file. */
2248 testFSInfoQuerySetEndOfFile(hTest);
2249 /* Add tests as required... */
2250}
2251#endif
2252int vbsfSetFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
2253{
2254 uint32_t type = vbsfQueryHandleType(pClient, Handle)
2255 & (SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE|SHFL_HF_TYPE_VOLUME);
2256
2257 if (type == 0 || pcbBuffer == 0 || pBuffer == 0)
2258 {
2259 AssertFailed();
2260 return VERR_INVALID_PARAMETER;
2261 }
2262
2263 if (flags & SHFL_INFO_FILE)
2264 return vbsfSetFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2265
2266 if (flags & SHFL_INFO_SIZE)
2267 return vbsfSetEndOfFile(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2268
2269// if (flags & SHFL_INFO_VOLUME)
2270// return vbsfVolumeInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2271 AssertFailed();
2272 return VERR_INVALID_PARAMETER;
2273}
2274
2275#ifdef UNITTEST
2276/** Unit test the SHFL_FN_LOCK API. Located here as a form of API
2277 * documentation. */
2278void testLock(RTTEST hTest)
2279{
2280 /* If the number or types of parameters are wrong the API should fail. */
2281 testLockBadParameters(hTest);
2282 /* Simple file locking and unlocking test. */
2283 testLockFileSimple(hTest);
2284 /* Add tests as required... */
2285}
2286#endif
2287
2288int vbsfLock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
2289{
2290 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
2291 uint32_t fRTLock = 0;
2292
2293 Assert((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL);
2294
2295 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
2296 if (RT_SUCCESS(rc))
2297 { /* likely */ }
2298 else
2299 return rc;
2300
2301 if ( ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL)
2302 || (flags & SHFL_LOCK_ENTIRE)
2303 )
2304 {
2305 AssertFailed();
2306 return VERR_INVALID_PARAMETER;
2307 }
2308
2309 /* Lock type */
2310 switch(flags & SHFL_LOCK_MODE_MASK)
2311 {
2312 case SHFL_LOCK_SHARED:
2313 fRTLock = RTFILE_LOCK_READ;
2314 break;
2315
2316 case SHFL_LOCK_EXCLUSIVE:
2317 fRTLock = RTFILE_LOCK_READ | RTFILE_LOCK_WRITE;
2318 break;
2319
2320 default:
2321 AssertFailed();
2322 return VERR_INVALID_PARAMETER;
2323 }
2324
2325 /* Lock wait type */
2326 if (flags & SHFL_LOCK_WAIT)
2327 fRTLock |= RTFILE_LOCK_WAIT;
2328 else
2329 fRTLock |= RTFILE_LOCK_IMMEDIATELY;
2330
2331#ifdef RT_OS_WINDOWS
2332 rc = RTFileLock(pHandle->file.Handle, fRTLock, offset, length);
2333 if (rc != VINF_SUCCESS)
2334 Log(("RTFileLock %RTfile %RX64 %RX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
2335#else
2336 Log(("vbsfLock: Pretend success handle=%x\n", Handle));
2337 rc = VINF_SUCCESS;
2338 RT_NOREF2(offset, length);
2339#endif
2340 return rc;
2341}
2342
2343int vbsfUnlock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
2344{
2345 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
2346
2347 Assert((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL);
2348
2349 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
2350 if (RT_SUCCESS(rc))
2351 { /* likely */ }
2352 else
2353 return rc;
2354
2355 if ( ((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL)
2356 || (flags & SHFL_LOCK_ENTIRE)
2357 )
2358 {
2359 return VERR_INVALID_PARAMETER;
2360 }
2361
2362#ifdef RT_OS_WINDOWS
2363 rc = RTFileUnlock(pHandle->file.Handle, offset, length);
2364 if (rc != VINF_SUCCESS)
2365 Log(("RTFileUnlock %RTfile %RX64 %RTX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
2366#else
2367 Log(("vbsfUnlock: Pretend success handle=%x\n", Handle));
2368 rc = VINF_SUCCESS;
2369 RT_NOREF2(offset, length);
2370#endif
2371
2372 return rc;
2373}
2374
2375
2376#ifdef UNITTEST
2377/** Unit test the SHFL_FN_REMOVE API. Located here as a form of API
2378 * documentation. */
2379void testRemove(RTTEST hTest)
2380{
2381 /* If the number or types of parameters are wrong the API should fail. */
2382 testRemoveBadParameters(hTest);
2383 /* Add tests as required... */
2384}
2385#endif
2386int vbsfRemove(SHFLCLIENTDATA *pClient, SHFLROOT root, PCSHFLSTRING pPath, uint32_t cbPath, uint32_t flags, SHFLHANDLE hToClose)
2387{
2388
2389 /* Validate input */
2390 Assert(pPath);
2391 AssertReturn(pPath->u16Size > 0, VERR_INVALID_PARAMETER);
2392
2393 /*
2394 * Close the handle if specified.
2395 */
2396 int rc = VINF_SUCCESS;
2397 if (hToClose != SHFL_HANDLE_NIL)
2398 rc = vbsfClose(pClient, root, hToClose);
2399 if (RT_SUCCESS(rc))
2400 {
2401 /*
2402 * Build a host full path for the given path and convert ucs2 to utf8 if necessary.
2403 */
2404 char *pszFullPath = NULL;
2405 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, NULL);
2406 if (RT_SUCCESS(rc))
2407 {
2408 /*
2409 * Is the guest allowed to write to this share?
2410 */
2411 bool fWritable;
2412 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
2413 if (RT_SUCCESS(rc) && fWritable)
2414 {
2415 /*
2416 * Do the removal/deletion according to the type flags.
2417 */
2418 if (flags & SHFL_REMOVE_SYMLINK)
2419 rc = RTSymlinkDelete(pszFullPath, 0);
2420 else if (flags & SHFL_REMOVE_FILE)
2421 rc = RTFileDelete(pszFullPath);
2422 else
2423 rc = RTDirRemove(pszFullPath);
2424
2425#if 0 //ndef RT_OS_WINDOWS
2426 /* There are a few adjustments to be made here: */
2427 if ( rc == VERR_FILE_NOT_FOUND
2428 && SHFL_CLIENT_NEED_WINDOWS_ERROR_STYLE_ADJUST_ON_POSIX(pClient)
2429 && vbsfErrorStyleIsWindowsPathNotFound(pszFullPath))
2430 rc = VERR_PATH_NOT_FOUND;
2431 else if ( rc == VERR_PATH_NOT_FOUND
2432 && SHFL_CLIENT_NEED_WINDOWS_ERROR_STYLE_ADJUST_ON_POSIX(pClient))
2433 {
2434 if (flags & (SHFL_REMOVE_FILE | SHFL_REMOVE_SYMLINK))
2435 {
2436 size_t cchFullPath = strlen(pszFullPath);
2437 if (cchFullPath > 0 && RTPATH_IS_SLASH(pszFullPath[cchFullPath - 1]))
2438 rc = VERR_INVALID_NAME;
2439 }
2440 else if (vbsfErrorStyleIsWindowsNotADirectory(pszFullPath))
2441 rc = VERR_NOT_A_DIRECTORY;
2442 }
2443#endif
2444 }
2445 else
2446 rc = VERR_WRITE_PROTECT;
2447
2448 /* free the path string */
2449 vbsfFreeFullPath(pszFullPath);
2450 }
2451 }
2452 return rc;
2453}
2454
2455
2456#ifdef UNITTEST
2457/** Unit test the SHFL_FN_RENAME API. Located here as a form of API
2458 * documentation. */
2459void testRename(RTTEST hTest)
2460{
2461 /* If the number or types of parameters are wrong the API should fail. */
2462 testRenameBadParameters(hTest);
2463 /* Add tests as required... */
2464}
2465#endif
2466int vbsfRename(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pSrc, SHFLSTRING *pDest, uint32_t flags)
2467{
2468 int rc = VINF_SUCCESS;
2469
2470 /* Validate input */
2471 if ( flags & ~(SHFL_RENAME_FILE|SHFL_RENAME_DIR|SHFL_RENAME_REPLACE_IF_EXISTS)
2472 || pSrc == 0
2473 || pDest == 0)
2474 {
2475 AssertFailed();
2476 return VERR_INVALID_PARAMETER;
2477 }
2478
2479 /* Build a host full path for the given path
2480 * and convert ucs2 to utf8 if necessary.
2481 */
2482 char *pszFullPathSrc = NULL;
2483 char *pszFullPathDest = NULL;
2484
2485 rc = vbsfBuildFullPath(pClient, root, pSrc, pSrc->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPathSrc, NULL);
2486 if (rc != VINF_SUCCESS)
2487 return rc;
2488
2489 rc = vbsfBuildFullPath(pClient, root, pDest, pDest->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPathDest, NULL, false, true);
2490 if (RT_SUCCESS (rc))
2491 {
2492 Log(("Rename %s to %s\n", pszFullPathSrc, pszFullPathDest));
2493
2494 /* is the guest allowed to write to this share? */
2495 bool fWritable;
2496 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
2497 if (RT_FAILURE(rc) || !fWritable)
2498 rc = VERR_WRITE_PROTECT;
2499
2500 if (RT_SUCCESS(rc))
2501 {
2502 if ((flags & (SHFL_RENAME_FILE | SHFL_RENAME_DIR)) == (SHFL_RENAME_FILE | SHFL_RENAME_DIR))
2503 {
2504 rc = RTPathRename(pszFullPathSrc, pszFullPathDest,
2505 flags & SHFL_RENAME_REPLACE_IF_EXISTS ? RTPATHRENAME_FLAGS_REPLACE : 0);
2506 }
2507 else if (flags & SHFL_RENAME_FILE)
2508 {
2509 rc = RTFileMove(pszFullPathSrc, pszFullPathDest,
2510 ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTFILEMOVE_FLAGS_REPLACE : 0));
2511 }
2512 else
2513 {
2514 /* NT ignores the REPLACE flag and simply return and already exists error. */
2515 rc = RTDirRename(pszFullPathSrc, pszFullPathDest,
2516 ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTPATHRENAME_FLAGS_REPLACE : 0));
2517 }
2518#ifndef RT_OS_WINDOWS
2519 if ( rc == VERR_FILE_NOT_FOUND
2520 && SHFL_CLIENT_NEED_WINDOWS_ERROR_STYLE_ADJUST_ON_POSIX(pClient)
2521 && vbsfErrorStyleIsWindowsPathNotFound2(pszFullPathSrc, pszFullPathDest))
2522 rc = VERR_PATH_NOT_FOUND;
2523#endif
2524 }
2525
2526 /* free the path string */
2527 vbsfFreeFullPath(pszFullPathDest);
2528 }
2529 /* free the path string */
2530 vbsfFreeFullPath(pszFullPathSrc);
2531 return rc;
2532}
2533
2534/**
2535 * Implements SHFL_FN_COPY_FILE (wrapping RTFileCopy).
2536 */
2537int vbsfCopyFile(SHFLCLIENTDATA *pClient, SHFLROOT idRootSrc, PCSHFLSTRING pStrPathSrc,
2538 SHFLROOT idRootDst, PCSHFLSTRING pStrPathDst, uint32_t fFlags)
2539{
2540 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
2541 if (pClient->fu32Flags & SHFL_CF_UTF8)
2542 LogFunc(("pClient %p, idRootSrc %#RX32, '%.*s', idRootSrc %#RX32, '%.*s', fFlags %#x\n", pClient, idRootSrc,
2543 pStrPathSrc->u16Length, pStrPathSrc->String.ach, idRootDst, pStrPathDst->u16Length, pStrPathDst->String.ach, fFlags));
2544 else
2545 LogFunc(("pClient %p, idRootSrc %#RX32, '%.*ls', idRootSrc %#RX32, '%.*ls', fFlags %#x\n", pClient,
2546 idRootSrc, pStrPathSrc->u16Length / sizeof(RTUTF16), pStrPathSrc->String.ach,
2547 idRootDst, pStrPathDst->u16Length / sizeof(RTUTF16), pStrPathDst->String.ach, fFlags));
2548
2549 /*
2550 * Build host paths.
2551 */
2552 char *pszPathSrc = NULL;
2553 int rc = vbsfBuildFullPath(pClient, idRootSrc, pStrPathSrc, pStrPathSrc->u16Size + SHFLSTRING_HEADER_SIZE, &pszPathSrc, NULL);
2554 if (RT_SUCCESS(rc))
2555 {
2556 char *pszPathDst = NULL;
2557 rc = vbsfBuildFullPath(pClient, idRootDst, pStrPathDst, pStrPathDst->u16Size + SHFLSTRING_HEADER_SIZE, &pszPathDst, NULL);
2558 if (RT_SUCCESS(rc))
2559 {
2560 /*
2561 * Do the job.
2562 */
2563 rc = RTFileCopy(pszPathSrc, pszPathDst);
2564
2565 vbsfFreeFullPath(pszPathDst);
2566 }
2567 vbsfFreeFullPath(pszPathSrc);
2568 }
2569
2570 RT_NOREF(fFlags);
2571 LogFunc(("returns %Rrc\n", rc));
2572 return rc;
2573}
2574
2575#ifdef UNITTEST
2576/** Unit test the SHFL_FN_SYMLINK API. Located here as a form of API
2577 * documentation. */
2578void testSymlink(RTTEST hTest)
2579{
2580 /* If the number or types of parameters are wrong the API should fail. */
2581 testSymlinkBadParameters(hTest);
2582 /* Add tests as required... */
2583}
2584#endif
2585int vbsfSymlink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pNewPath, SHFLSTRING *pOldPath, SHFLFSOBJINFO *pInfo)
2586{
2587 int rc = VINF_SUCCESS;
2588
2589 char *pszFullNewPath = NULL;
2590 char *pszFullOldPath = NULL;
2591
2592 /* XXX: no support for UCS2 at the moment. */
2593 if (!BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
2594 return VERR_NOT_IMPLEMENTED;
2595
2596 bool fSymlinksCreate;
2597 rc = vbsfMappingsQuerySymlinksCreate(pClient, root, &fSymlinksCreate);
2598 AssertRCReturn(rc, rc);
2599 if (!fSymlinksCreate)
2600 return VERR_WRITE_PROTECT; /* XXX or VERR_TOO_MANY_SYMLINKS? */
2601
2602 rc = vbsfBuildFullPath(pClient, root, pNewPath, pNewPath->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullNewPath, NULL);
2603 AssertRCReturn(rc, rc);
2604
2605 /* Verify that the link target can be a valid host path, i.e. does not contain invalid characters. */
2606 uint32_t fu32PathFlags = 0;
2607 uint32_t fu32Options = 0;
2608 rc = vbsfPathGuestToHost(pClient, root, pOldPath, pOldPath->u16Size + SHFLSTRING_HEADER_SIZE,
2609 &pszFullOldPath, NULL, fu32Options, &fu32PathFlags);
2610 if (RT_FAILURE(rc))
2611 {
2612 vbsfFreeFullPath(pszFullNewPath);
2613 return rc;
2614 }
2615
2616 /** @todo r=bird: We _must_ perform slash conversion on the target (what this
2617 * code calls 'pOldPath' for some peculiar reason)! */
2618
2619 rc = RTSymlinkCreate(pszFullNewPath, (const char *)pOldPath->String.utf8,
2620 RTSYMLINKTYPE_UNKNOWN, 0);
2621 if (RT_SUCCESS(rc))
2622 {
2623 RTFSOBJINFO info;
2624 rc = RTPathQueryInfoEx(pszFullNewPath, &info, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
2625 if (RT_SUCCESS(rc))
2626 vbfsCopyFsObjInfoFromIprt(pInfo, &info);
2627 }
2628
2629 vbsfFreeFullPath(pszFullOldPath);
2630 vbsfFreeFullPath(pszFullNewPath);
2631
2632 return rc;
2633}
2634
2635/*
2636 * Clean up our mess by freeing all handles that are still valid.
2637 *
2638 */
2639int vbsfDisconnect(SHFLCLIENTDATA *pClient)
2640{
2641 for (int i = 0; i < SHFLHANDLE_MAX; ++i)
2642 {
2643 SHFLFILEHANDLE *pHandle = NULL;
2644 SHFLHANDLE Handle = (SHFLHANDLE)i;
2645
2646 uint32_t type = vbsfQueryHandleType(pClient, Handle);
2647 switch (type & (SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE))
2648 {
2649 case SHFL_HF_TYPE_DIR:
2650 {
2651 pHandle = vbsfQueryDirHandle(pClient, Handle);
2652 break;
2653 }
2654 case SHFL_HF_TYPE_FILE:
2655 {
2656 pHandle = vbsfQueryFileHandle(pClient, Handle);
2657 break;
2658 }
2659 default:
2660 break;
2661 }
2662
2663 if (pHandle)
2664 {
2665 LogFunc(("Opened handle 0x%08x\n", i));
2666 vbsfClose(pClient, pHandle->root, Handle);
2667 }
2668 }
2669
2670 for (uint32_t i = 0; i < RT_ELEMENTS(pClient->acMappings); i++)
2671 if (pClient->acMappings[i])
2672 {
2673 uint16_t cMappings = pClient->acMappings[i];
2674 while (cMappings-- > 0)
2675 vbsfUnmapFolder(pClient, i);
2676 }
2677
2678 return VINF_SUCCESS;
2679}
2680
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