VirtualBox

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

Last change on this file since 79862 was 78947, checked in by vboxsync, 6 years ago

SharedFolderSvc,IPRT: Implemented changing attributes/mode via a directory handle. bugref:9172 ticketref:17626 ticketref:17859

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 89.2 KB
Line 
1/* $Id: vbsf.cpp 78947 2019-06-04 00:39:03Z 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.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 * @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 /*
1958 * Get the handle.
1959 */
1960 SHFLFILEHANDLE *pHandle;
1961 if (type == SHFL_HF_TYPE_FILE)
1962 {
1963 pHandle = vbsfQueryFileHandle(pClient, Handle);
1964 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1965 }
1966 else
1967 {
1968 Assert(type == SHFL_HF_TYPE_DIR);
1969 pHandle = vbsfQueryDirHandle(pClient, Handle);
1970 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1971 }
1972 if (RT_SUCCESS(rc))
1973 {
1974 /*
1975 * Any times to set?
1976 */
1977 if ( RTTimeSpecGetNano(&pSFDEntry->AccessTime)
1978 || RTTimeSpecGetNano(&pSFDEntry->ModificationTime)
1979 || RTTimeSpecGetNano(&pSFDEntry->ChangeTime)
1980 || RTTimeSpecGetNano(&pSFDEntry->BirthTime))
1981 {
1982
1983 /* Change only the time values that are not zero */
1984 if (type == SHFL_HF_TYPE_FILE)
1985 rc = RTFileSetTimes(pHandle->file.Handle,
1986 RTTimeSpecGetNano(&pSFDEntry->AccessTime) ? &pSFDEntry->AccessTime : NULL,
1987 RTTimeSpecGetNano(&pSFDEntry->ModificationTime) ? &pSFDEntry->ModificationTime : NULL,
1988 RTTimeSpecGetNano(&pSFDEntry->ChangeTime) ? &pSFDEntry->ChangeTime : NULL,
1989 RTTimeSpecGetNano(&pSFDEntry->BirthTime) ? &pSFDEntry->BirthTime : NULL);
1990 else
1991 rc = RTDirSetTimes( pHandle->dir.Handle,
1992 RTTimeSpecGetNano(&pSFDEntry->AccessTime) ? &pSFDEntry->AccessTime : NULL,
1993 RTTimeSpecGetNano(&pSFDEntry->ModificationTime) ? &pSFDEntry->ModificationTime : NULL,
1994 RTTimeSpecGetNano(&pSFDEntry->ChangeTime) ? &pSFDEntry->ChangeTime : NULL,
1995 RTTimeSpecGetNano(&pSFDEntry->BirthTime) ? &pSFDEntry->BirthTime : NULL);
1996 if (RT_FAILURE(rc))
1997 {
1998 Log(("RT%sSetTimes failed with %Rrc\n", type == SHFL_HF_TYPE_FILE ? "File" : "Dir", rc));
1999 Log(("AccessTime %#RX64\n", RTTimeSpecGetNano(&pSFDEntry->AccessTime)));
2000 Log(("ModificationTime %#RX64\n", RTTimeSpecGetNano(&pSFDEntry->ModificationTime)));
2001 Log(("ChangeTime %#RX64\n", RTTimeSpecGetNano(&pSFDEntry->ChangeTime)));
2002 Log(("BirthTime %#RX64\n", RTTimeSpecGetNano(&pSFDEntry->BirthTime)));
2003 /* "temporary" hack */
2004 rc = VINF_SUCCESS;
2005 }
2006 }
2007
2008 /*
2009 * Any mode changes?
2010 */
2011 if (pSFDEntry->Attr.fMode)
2012 {
2013 RTFMODE fMode = pSFDEntry->Attr.fMode;
2014
2015 if (type == SHFL_HF_TYPE_FILE)
2016 {
2017#ifndef RT_OS_WINDOWS
2018 /* Don't allow the guest to clear the read own bit, otherwise the guest wouldn't
2019 * be able to access this file anymore. Only for guests, which set the UNIX mode.
2020 * Also, clear bits which we don't pass through for security reasons. */
2021 if (fMode & RTFS_UNIX_MASK)
2022 {
2023 fMode |= RTFS_UNIX_IRUSR;
2024 fMode &= ~(RTFS_UNIX_ISUID | RTFS_UNIX_ISGID | RTFS_UNIX_ISTXT);
2025 }
2026#endif
2027 rc = RTFileSetMode(pHandle->file.Handle, fMode);
2028 }
2029 else
2030 {
2031#ifndef RT_OS_WINDOWS
2032 /* Don't allow the guest to clear the read+execute own bits, otherwise the guest
2033 * wouldn't be able to access this directory anymore. Only for guests, which set
2034 * the UNIX mode. Also, clear bits which we don't pass through for security reasons. */
2035 if (fMode & RTFS_UNIX_MASK)
2036 {
2037 fMode |= RTFS_UNIX_IRUSR | RTFS_UNIX_IXUSR;
2038 fMode &= ~(RTFS_UNIX_ISUID | RTFS_UNIX_ISGID | RTFS_UNIX_ISTXT /*?*/);
2039 }
2040#endif
2041 rc = RTDirSetMode(pHandle->dir.Handle, fMode);
2042 }
2043 if (RT_FAILURE(rc))
2044 {
2045 Log(("RT%sSetMode %#x (%#x) failed with %Rrc\n", type == SHFL_HF_TYPE_FILE ? "File" : "Dir",
2046 fMode, pSFDEntry->Attr.fMode, rc));
2047 /* silent failure, because this tends to fail with e.g. windows guest & linux host */
2048 rc = VINF_SUCCESS;
2049 }
2050 }
2051
2052 /*
2053 * Return the current file info on success.
2054 */
2055 if (RT_SUCCESS(rc))
2056 {
2057 uint32_t bufsize = sizeof(*pSFDEntry);
2058 rc = vbsfQueryFileInfo(pClient, root, Handle, SHFL_INFO_GET | SHFL_INFO_FILE, &bufsize, (uint8_t *)pSFDEntry);
2059 if (RT_SUCCESS(rc))
2060 *pcbBuffer = sizeof(SHFLFSOBJINFO);
2061 else
2062 AssertFailed();
2063 }
2064 }
2065 return rc;
2066}
2067
2068
2069/**
2070 * Handles SHFL_FN_SET_FILE_SIZE.
2071 */
2072int vbsfSetFileSize(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hHandle, uint64_t cbNewSize)
2073{
2074 /*
2075 * Resolve handle and validate write access.
2076 */
2077 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, hHandle);
2078 ASSERT_GUEST_RETURN(pHandle, VERR_INVALID_HANDLE);
2079
2080 int rc = vbsfCheckHandleAccess(pClient, idRoot, pHandle, VBSF_CHECK_ACCESS_WRITE);
2081 if (RT_SUCCESS(rc))
2082 {
2083 /*
2084 * Execute the request.
2085 */
2086 rc = RTFileSetSize(pHandle->file.Handle, cbNewSize);
2087 }
2088 return rc;
2089}
2090
2091
2092static int vbsfSetEndOfFile(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
2093 uint32_t *pcbBuffer, uint8_t *pBuffer)
2094{
2095 RT_NOREF1(flags);
2096 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
2097 SHFLFSOBJINFO *pSFDEntry;
2098
2099 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
2100 {
2101 AssertFailed();
2102 return VERR_INVALID_PARAMETER;
2103 }
2104
2105 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
2106 if (RT_SUCCESS(rc))
2107 { /* likely */ }
2108 else
2109 return rc;
2110
2111 *pcbBuffer = 0;
2112 pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
2113
2114 if (flags & SHFL_INFO_SIZE)
2115 {
2116 rc = RTFileSetSize(pHandle->file.Handle, pSFDEntry->cbObject);
2117 if (rc != VINF_SUCCESS)
2118 AssertFailed();
2119 }
2120 else
2121 AssertFailed();
2122
2123 if (rc == VINF_SUCCESS)
2124 {
2125 RTFSOBJINFO fileinfo;
2126
2127 /* Query the new object info and return it */
2128 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
2129 if (rc == VINF_SUCCESS)
2130 {
2131#ifdef RT_OS_WINDOWS
2132 fileinfo.Attr.fMode |= 0111;
2133#endif
2134 vbfsCopyFsObjInfoFromIprt(pSFDEntry, &fileinfo);
2135 *pcbBuffer = sizeof(SHFLFSOBJINFO);
2136 }
2137 else
2138 AssertFailed();
2139 }
2140
2141 return rc;
2142}
2143
2144int vbsfQueryVolumeInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
2145{
2146 RT_NOREF2(root, flags);
2147 int rc = VINF_SUCCESS;
2148 SHFLVOLINFO *pSFDEntry;
2149 char *pszFullPath = NULL;
2150 union
2151 {
2152 SHFLSTRING Dummy;
2153 uint8_t abDummy[SHFLSTRING_HEADER_SIZE + sizeof(RTUTF16)];
2154 } Buf;
2155
2156 if (pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLVOLINFO))
2157 {
2158 AssertFailed();
2159 return VERR_INVALID_PARAMETER;
2160 }
2161
2162 /** @todo other options */
2163 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_VOLUME));
2164
2165 *pcbBuffer = 0;
2166 pSFDEntry = (PSHFLVOLINFO)pBuffer;
2167
2168 ShflStringInitBuffer(&Buf.Dummy, sizeof(Buf));
2169 Buf.Dummy.String.ucs2[0] = '\0';
2170 rc = vbsfBuildFullPath(pClient, root, &Buf.Dummy, sizeof(Buf), &pszFullPath, NULL);
2171
2172 if (RT_SUCCESS(rc))
2173 {
2174 rc = RTFsQuerySizes(pszFullPath, &pSFDEntry->ullTotalAllocationBytes, &pSFDEntry->ullAvailableAllocationBytes, &pSFDEntry->ulBytesPerAllocationUnit, &pSFDEntry->ulBytesPerSector);
2175 if (rc != VINF_SUCCESS)
2176 goto exit;
2177
2178 rc = RTFsQuerySerial(pszFullPath, &pSFDEntry->ulSerial);
2179 if (rc != VINF_SUCCESS)
2180 goto exit;
2181
2182 RTFSPROPERTIES FsProperties;
2183 rc = RTFsQueryProperties(pszFullPath, &FsProperties);
2184 if (rc != VINF_SUCCESS)
2185 goto exit;
2186 vbfsCopyFsPropertiesFromIprt(&pSFDEntry->fsProperties, &FsProperties);
2187
2188 *pcbBuffer = sizeof(SHFLVOLINFO);
2189 }
2190 else AssertFailed();
2191
2192exit:
2193 AssertMsg(rc == VINF_SUCCESS, ("failure: rc = %Rrc\n", rc));
2194 /* free the path string */
2195 vbsfFreeFullPath(pszFullPath);
2196 return rc;
2197}
2198
2199int vbsfQueryFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
2200{
2201 if (pcbBuffer == 0 || pBuffer == 0)
2202 {
2203 AssertFailed();
2204 return VERR_INVALID_PARAMETER;
2205 }
2206
2207 if (flags & SHFL_INFO_FILE)
2208 return vbsfQueryFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2209
2210 if (flags & SHFL_INFO_VOLUME)
2211 return vbsfQueryVolumeInfo(pClient, root, flags, pcbBuffer, pBuffer);
2212
2213 AssertFailed();
2214 return VERR_INVALID_PARAMETER;
2215}
2216
2217#ifdef UNITTEST
2218/** Unit test the SHFL_FN_INFORMATION API. Located here as a form of API
2219 * documentation. */
2220void testFSInfo(RTTEST hTest)
2221{
2222 /* If the number or types of parameters are wrong the API should fail. */
2223 testFSInfoBadParameters(hTest);
2224 /* Basic get and set file size test. */
2225 testFSInfoQuerySetFMode(hTest);
2226 /* Basic get and set dir atime test. */
2227 testFSInfoQuerySetDirATime(hTest);
2228 /* Basic get and set file atime test. */
2229 testFSInfoQuerySetFileATime(hTest);
2230 /* Basic set end of file. */
2231 testFSInfoQuerySetEndOfFile(hTest);
2232 /* Add tests as required... */
2233}
2234#endif
2235int vbsfSetFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
2236{
2237 uint32_t type = vbsfQueryHandleType(pClient, Handle)
2238 & (SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE|SHFL_HF_TYPE_VOLUME);
2239
2240 if (type == 0 || pcbBuffer == 0 || pBuffer == 0)
2241 {
2242 AssertFailed();
2243 return VERR_INVALID_PARAMETER;
2244 }
2245
2246 if (flags & SHFL_INFO_FILE)
2247 return vbsfSetFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2248
2249 if (flags & SHFL_INFO_SIZE)
2250 return vbsfSetEndOfFile(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2251
2252// if (flags & SHFL_INFO_VOLUME)
2253// return vbsfVolumeInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2254 AssertFailed();
2255 return VERR_INVALID_PARAMETER;
2256}
2257
2258#ifdef UNITTEST
2259/** Unit test the SHFL_FN_LOCK API. Located here as a form of API
2260 * documentation. */
2261void testLock(RTTEST hTest)
2262{
2263 /* If the number or types of parameters are wrong the API should fail. */
2264 testLockBadParameters(hTest);
2265 /* Simple file locking and unlocking test. */
2266 testLockFileSimple(hTest);
2267 /* Add tests as required... */
2268}
2269#endif
2270
2271int vbsfLock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
2272{
2273 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
2274 uint32_t fRTLock = 0;
2275
2276 Assert((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL);
2277
2278 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
2279 if (RT_SUCCESS(rc))
2280 { /* likely */ }
2281 else
2282 return rc;
2283
2284 if ( ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL)
2285 || (flags & SHFL_LOCK_ENTIRE)
2286 )
2287 {
2288 AssertFailed();
2289 return VERR_INVALID_PARAMETER;
2290 }
2291
2292 /* Lock type */
2293 switch(flags & SHFL_LOCK_MODE_MASK)
2294 {
2295 case SHFL_LOCK_SHARED:
2296 fRTLock = RTFILE_LOCK_READ;
2297 break;
2298
2299 case SHFL_LOCK_EXCLUSIVE:
2300 fRTLock = RTFILE_LOCK_READ | RTFILE_LOCK_WRITE;
2301 break;
2302
2303 default:
2304 AssertFailed();
2305 return VERR_INVALID_PARAMETER;
2306 }
2307
2308 /* Lock wait type */
2309 if (flags & SHFL_LOCK_WAIT)
2310 fRTLock |= RTFILE_LOCK_WAIT;
2311 else
2312 fRTLock |= RTFILE_LOCK_IMMEDIATELY;
2313
2314#ifdef RT_OS_WINDOWS
2315 rc = RTFileLock(pHandle->file.Handle, fRTLock, offset, length);
2316 if (rc != VINF_SUCCESS)
2317 Log(("RTFileLock %RTfile %RX64 %RX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
2318#else
2319 Log(("vbsfLock: Pretend success handle=%x\n", Handle));
2320 rc = VINF_SUCCESS;
2321 RT_NOREF2(offset, length);
2322#endif
2323 return rc;
2324}
2325
2326int vbsfUnlock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
2327{
2328 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
2329
2330 Assert((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL);
2331
2332 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
2333 if (RT_SUCCESS(rc))
2334 { /* likely */ }
2335 else
2336 return rc;
2337
2338 if ( ((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL)
2339 || (flags & SHFL_LOCK_ENTIRE)
2340 )
2341 {
2342 return VERR_INVALID_PARAMETER;
2343 }
2344
2345#ifdef RT_OS_WINDOWS
2346 rc = RTFileUnlock(pHandle->file.Handle, offset, length);
2347 if (rc != VINF_SUCCESS)
2348 Log(("RTFileUnlock %RTfile %RX64 %RTX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
2349#else
2350 Log(("vbsfUnlock: Pretend success handle=%x\n", Handle));
2351 rc = VINF_SUCCESS;
2352 RT_NOREF2(offset, length);
2353#endif
2354
2355 return rc;
2356}
2357
2358
2359#ifdef UNITTEST
2360/** Unit test the SHFL_FN_REMOVE API. Located here as a form of API
2361 * documentation. */
2362void testRemove(RTTEST hTest)
2363{
2364 /* If the number or types of parameters are wrong the API should fail. */
2365 testRemoveBadParameters(hTest);
2366 /* Add tests as required... */
2367}
2368#endif
2369int vbsfRemove(SHFLCLIENTDATA *pClient, SHFLROOT root, PCSHFLSTRING pPath, uint32_t cbPath, uint32_t flags, SHFLHANDLE hToClose)
2370{
2371
2372 /* Validate input */
2373 Assert(pPath);
2374 AssertReturn(pPath->u16Size > 0, VERR_INVALID_PARAMETER);
2375
2376 /*
2377 * Close the handle if specified.
2378 */
2379 int rc = VINF_SUCCESS;
2380 if (hToClose != SHFL_HANDLE_NIL)
2381 rc = vbsfClose(pClient, root, hToClose);
2382 if (RT_SUCCESS(rc))
2383 {
2384 /*
2385 * Build a host full path for the given path and convert ucs2 to utf8 if necessary.
2386 */
2387 char *pszFullPath = NULL;
2388 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, NULL);
2389 if (RT_SUCCESS(rc))
2390 {
2391 /*
2392 * Is the guest allowed to write to this share?
2393 */
2394 bool fWritable;
2395 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
2396 if (RT_SUCCESS(rc) && fWritable)
2397 {
2398 /*
2399 * Do the removal/deletion according to the type flags.
2400 */
2401 if (flags & SHFL_REMOVE_SYMLINK)
2402 rc = RTSymlinkDelete(pszFullPath, 0);
2403 else if (flags & SHFL_REMOVE_FILE)
2404 rc = RTFileDelete(pszFullPath);
2405 else
2406 rc = RTDirRemove(pszFullPath);
2407
2408#if 0 //ndef RT_OS_WINDOWS
2409 /* There are a few adjustments to be made here: */
2410 if ( rc == VERR_FILE_NOT_FOUND
2411 && SHFL_CLIENT_NEED_WINDOWS_ERROR_STYLE_ADJUST_ON_POSIX(pClient)
2412 && vbsfErrorStyleIsWindowsPathNotFound(pszFullPath))
2413 rc = VERR_PATH_NOT_FOUND;
2414 else if ( rc == VERR_PATH_NOT_FOUND
2415 && SHFL_CLIENT_NEED_WINDOWS_ERROR_STYLE_ADJUST_ON_POSIX(pClient))
2416 {
2417 if (flags & (SHFL_REMOVE_FILE | SHFL_REMOVE_SYMLINK))
2418 {
2419 size_t cchFullPath = strlen(pszFullPath);
2420 if (cchFullPath > 0 && RTPATH_IS_SLASH(pszFullPath[cchFullPath - 1]))
2421 rc = VERR_INVALID_NAME;
2422 }
2423 else if (vbsfErrorStyleIsWindowsNotADirectory(pszFullPath))
2424 rc = VERR_NOT_A_DIRECTORY;
2425 }
2426#endif
2427 }
2428 else
2429 rc = VERR_WRITE_PROTECT;
2430
2431 /* free the path string */
2432 vbsfFreeFullPath(pszFullPath);
2433 }
2434 }
2435 return rc;
2436}
2437
2438
2439#ifdef UNITTEST
2440/** Unit test the SHFL_FN_RENAME API. Located here as a form of API
2441 * documentation. */
2442void testRename(RTTEST hTest)
2443{
2444 /* If the number or types of parameters are wrong the API should fail. */
2445 testRenameBadParameters(hTest);
2446 /* Add tests as required... */
2447}
2448#endif
2449int vbsfRename(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pSrc, SHFLSTRING *pDest, uint32_t flags)
2450{
2451 int rc = VINF_SUCCESS;
2452
2453 /* Validate input */
2454 if ( flags & ~(SHFL_RENAME_FILE|SHFL_RENAME_DIR|SHFL_RENAME_REPLACE_IF_EXISTS)
2455 || pSrc == 0
2456 || pDest == 0)
2457 {
2458 AssertFailed();
2459 return VERR_INVALID_PARAMETER;
2460 }
2461
2462 /* Build a host full path for the given path
2463 * and convert ucs2 to utf8 if necessary.
2464 */
2465 char *pszFullPathSrc = NULL;
2466 char *pszFullPathDest = NULL;
2467
2468 rc = vbsfBuildFullPath(pClient, root, pSrc, pSrc->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPathSrc, NULL);
2469 if (rc != VINF_SUCCESS)
2470 return rc;
2471
2472 rc = vbsfBuildFullPath(pClient, root, pDest, pDest->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPathDest, NULL, false, true);
2473 if (RT_SUCCESS (rc))
2474 {
2475 Log(("Rename %s to %s\n", pszFullPathSrc, pszFullPathDest));
2476
2477 /* is the guest allowed to write to this share? */
2478 bool fWritable;
2479 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
2480 if (RT_FAILURE(rc) || !fWritable)
2481 rc = VERR_WRITE_PROTECT;
2482
2483 if (RT_SUCCESS(rc))
2484 {
2485 if ((flags & (SHFL_RENAME_FILE | SHFL_RENAME_DIR)) == (SHFL_RENAME_FILE | SHFL_RENAME_DIR))
2486 {
2487 rc = RTPathRename(pszFullPathSrc, pszFullPathDest,
2488 flags & SHFL_RENAME_REPLACE_IF_EXISTS ? RTPATHRENAME_FLAGS_REPLACE : 0);
2489 }
2490 else if (flags & SHFL_RENAME_FILE)
2491 {
2492 rc = RTFileMove(pszFullPathSrc, pszFullPathDest,
2493 ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTFILEMOVE_FLAGS_REPLACE : 0));
2494 }
2495 else
2496 {
2497 /* NT ignores the REPLACE flag and simply return and already exists error. */
2498 rc = RTDirRename(pszFullPathSrc, pszFullPathDest,
2499 ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTPATHRENAME_FLAGS_REPLACE : 0));
2500 }
2501#ifndef RT_OS_WINDOWS
2502 if ( rc == VERR_FILE_NOT_FOUND
2503 && SHFL_CLIENT_NEED_WINDOWS_ERROR_STYLE_ADJUST_ON_POSIX(pClient)
2504 && vbsfErrorStyleIsWindowsPathNotFound2(pszFullPathSrc, pszFullPathDest))
2505 rc = VERR_PATH_NOT_FOUND;
2506#endif
2507 }
2508
2509 /* free the path string */
2510 vbsfFreeFullPath(pszFullPathDest);
2511 }
2512 /* free the path string */
2513 vbsfFreeFullPath(pszFullPathSrc);
2514 return rc;
2515}
2516
2517/**
2518 * Implements SHFL_FN_COPY_FILE (wrapping RTFileCopy).
2519 */
2520int vbsfCopyFile(SHFLCLIENTDATA *pClient, SHFLROOT idRootSrc, PCSHFLSTRING pStrPathSrc,
2521 SHFLROOT idRootDst, PCSHFLSTRING pStrPathDst, uint32_t fFlags)
2522{
2523 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
2524 if (pClient->fu32Flags & SHFL_CF_UTF8)
2525 LogFunc(("pClient %p, idRootSrc %#RX32, '%.*s', idRootSrc %#RX32, '%.*s', fFlags %#x\n", pClient, idRootSrc,
2526 pStrPathSrc->u16Length, pStrPathSrc->String.ach, idRootDst, pStrPathDst->u16Length, pStrPathDst->String.ach, fFlags));
2527 else
2528 LogFunc(("pClient %p, idRootSrc %#RX32, '%.*ls', idRootSrc %#RX32, '%.*ls', fFlags %#x\n", pClient,
2529 idRootSrc, pStrPathSrc->u16Length / sizeof(RTUTF16), pStrPathSrc->String.ach,
2530 idRootDst, pStrPathDst->u16Length / sizeof(RTUTF16), pStrPathDst->String.ach, fFlags));
2531
2532 /*
2533 * Build host paths.
2534 */
2535 char *pszPathSrc = NULL;
2536 int rc = vbsfBuildFullPath(pClient, idRootSrc, pStrPathSrc, pStrPathSrc->u16Size + SHFLSTRING_HEADER_SIZE, &pszPathSrc, NULL);
2537 if (RT_SUCCESS(rc))
2538 {
2539 char *pszPathDst = NULL;
2540 rc = vbsfBuildFullPath(pClient, idRootDst, pStrPathDst, pStrPathDst->u16Size + SHFLSTRING_HEADER_SIZE, &pszPathDst, NULL);
2541 if (RT_SUCCESS(rc))
2542 {
2543 /*
2544 * Do the job.
2545 */
2546 rc = RTFileCopy(pszPathSrc, pszPathDst);
2547
2548 vbsfFreeFullPath(pszPathDst);
2549 }
2550 vbsfFreeFullPath(pszPathSrc);
2551 }
2552
2553 RT_NOREF(fFlags);
2554 LogFunc(("returns %Rrc\n", rc));
2555 return rc;
2556}
2557
2558#ifdef UNITTEST
2559/** Unit test the SHFL_FN_SYMLINK API. Located here as a form of API
2560 * documentation. */
2561void testSymlink(RTTEST hTest)
2562{
2563 /* If the number or types of parameters are wrong the API should fail. */
2564 testSymlinkBadParameters(hTest);
2565 /* Add tests as required... */
2566}
2567#endif
2568int vbsfSymlink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pNewPath, SHFLSTRING *pOldPath, SHFLFSOBJINFO *pInfo)
2569{
2570 int rc = VINF_SUCCESS;
2571
2572 char *pszFullNewPath = NULL;
2573 char *pszFullOldPath = NULL;
2574
2575 /* XXX: no support for UCS2 at the moment. */
2576 if (!BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
2577 return VERR_NOT_IMPLEMENTED;
2578
2579 bool fSymlinksCreate;
2580 rc = vbsfMappingsQuerySymlinksCreate(pClient, root, &fSymlinksCreate);
2581 AssertRCReturn(rc, rc);
2582 if (!fSymlinksCreate)
2583 return VERR_WRITE_PROTECT; /* XXX or VERR_TOO_MANY_SYMLINKS? */
2584
2585 rc = vbsfBuildFullPath(pClient, root, pNewPath, pNewPath->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullNewPath, NULL);
2586 AssertRCReturn(rc, rc);
2587
2588 /* Verify that the link target can be a valid host path, i.e. does not contain invalid characters. */
2589 uint32_t fu32PathFlags = 0;
2590 uint32_t fu32Options = 0;
2591 rc = vbsfPathGuestToHost(pClient, root, pOldPath, pOldPath->u16Size + SHFLSTRING_HEADER_SIZE,
2592 &pszFullOldPath, NULL, fu32Options, &fu32PathFlags);
2593 if (RT_FAILURE(rc))
2594 {
2595 vbsfFreeFullPath(pszFullNewPath);
2596 return rc;
2597 }
2598
2599 /** @todo r=bird: We _must_ perform slash conversion on the target (what this
2600 * code calls 'pOldPath' for some peculiar reason)! */
2601
2602 rc = RTSymlinkCreate(pszFullNewPath, (const char *)pOldPath->String.utf8,
2603 RTSYMLINKTYPE_UNKNOWN, 0);
2604 if (RT_SUCCESS(rc))
2605 {
2606 RTFSOBJINFO info;
2607 rc = RTPathQueryInfoEx(pszFullNewPath, &info, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
2608 if (RT_SUCCESS(rc))
2609 vbfsCopyFsObjInfoFromIprt(pInfo, &info);
2610 }
2611
2612 vbsfFreeFullPath(pszFullOldPath);
2613 vbsfFreeFullPath(pszFullNewPath);
2614
2615 return rc;
2616}
2617
2618/*
2619 * Clean up our mess by freeing all handles that are still valid.
2620 *
2621 */
2622int vbsfDisconnect(SHFLCLIENTDATA *pClient)
2623{
2624 for (int i = 0; i < SHFLHANDLE_MAX; ++i)
2625 {
2626 SHFLFILEHANDLE *pHandle = NULL;
2627 SHFLHANDLE Handle = (SHFLHANDLE)i;
2628
2629 uint32_t type = vbsfQueryHandleType(pClient, Handle);
2630 switch (type & (SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE))
2631 {
2632 case SHFL_HF_TYPE_DIR:
2633 {
2634 pHandle = vbsfQueryDirHandle(pClient, Handle);
2635 break;
2636 }
2637 case SHFL_HF_TYPE_FILE:
2638 {
2639 pHandle = vbsfQueryFileHandle(pClient, Handle);
2640 break;
2641 }
2642 default:
2643 break;
2644 }
2645
2646 if (pHandle)
2647 {
2648 LogFunc(("Opened handle 0x%08x\n", i));
2649 vbsfClose(pClient, pHandle->root, Handle);
2650 }
2651 }
2652
2653 for (uint32_t i = 0; i < RT_ELEMENTS(pClient->acMappings); i++)
2654 if (pClient->acMappings[i])
2655 {
2656 uint16_t cMappings = pClient->acMappings[i];
2657 while (cMappings-- > 0)
2658 vbsfUnmapFolder(pClient, i);
2659 }
2660
2661 return VINF_SUCCESS;
2662}
2663
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