VirtualBox

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

Last change on this file since 75902 was 75653, checked in by vboxsync, 6 years ago

SharedFolders: Added SHFL_LIST_RESTART flag for implementing RestartScan on NT.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 67.7 KB
Line 
1/* $Id: vbsf.cpp 75653 2018-11-21 21:01:28Z vboxsync $ */
2/** @file
3 * Shared Folders - VBox Shared Folders.
4 */
5
6/*
7 * Copyright (C) 2006-2017 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 <iprt/alloc.h>
33#include <iprt/assert.h>
34#include <iprt/asm.h>
35#include <iprt/fs.h>
36#include <iprt/dir.h>
37#include <iprt/file.h>
38#include <iprt/path.h>
39#include <iprt/string.h>
40#include <iprt/symlink.h>
41#include <iprt/uni.h>
42#include <iprt/stream.h>
43#ifdef RT_OS_DARWIN
44# include <Carbon/Carbon.h>
45#endif
46
47#ifdef UNITTEST
48# include "teststubs.h"
49#endif
50
51
52/*********************************************************************************************************************************
53* Defined Constants And Macros *
54*********************************************************************************************************************************/
55#define SHFL_RT_LINK(pClient) ((pClient)->fu32Flags & SHFL_CF_SYMLINKS ? RTPATH_F_ON_LINK : RTPATH_F_FOLLOW_LINK)
56
57
58/**
59 * @todo find a better solution for supporting the execute bit for non-windows
60 * guests on windows host. Search for "0111" to find all the relevant places.
61 */
62
63void vbsfStripLastComponent(char *pszFullPath, uint32_t cbFullPathRoot)
64{
65 RTUNICP cp;
66
67 /* Do not strip root. */
68 char *s = pszFullPath + cbFullPathRoot;
69 char *delimSecondLast = NULL;
70 char *delimLast = NULL;
71
72 LogFlowFunc(("%s -> %s\n", pszFullPath, s));
73
74 for (;;)
75 {
76 cp = RTStrGetCp(s);
77
78 if (cp == RTUNICP_INVALID || cp == 0)
79 {
80 break;
81 }
82
83 if (cp == RTPATH_DELIMITER)
84 {
85 if (delimLast != NULL)
86 {
87 delimSecondLast = delimLast;
88 }
89
90 delimLast = s;
91 }
92
93 s = RTStrNextCp(s);
94 }
95
96 if (cp == 0)
97 {
98 if (delimLast + 1 == s)
99 {
100 if (delimSecondLast)
101 {
102 *delimSecondLast = 0;
103 }
104 else if (delimLast)
105 {
106 *delimLast = 0;
107 }
108 }
109 else
110 {
111 if (delimLast)
112 {
113 *delimLast = 0;
114 }
115 }
116 }
117
118 LogFlowFunc(("%s, %s, %s\n", pszFullPath, delimLast, delimSecondLast));
119}
120
121static int vbsfBuildFullPath(SHFLCLIENTDATA *pClient, SHFLROOT root, PSHFLSTRING pPath,
122 uint32_t cbPath, char **ppszFullPath, uint32_t *pcbFullPathRoot,
123 bool fWildCard = false, bool fPreserveLastComponent = false)
124{
125 char *pszHostPath = NULL;
126 uint32_t fu32PathFlags = 0;
127 uint32_t fu32Options = VBSF_O_PATH_CHECK_ROOT_ESCAPE
128 | (fWildCard? VBSF_O_PATH_WILDCARD: 0)
129 | (fPreserveLastComponent? VBSF_O_PATH_PRESERVE_LAST_COMPONENT: 0);
130
131 int rc = vbsfPathGuestToHost(pClient, root, pPath, cbPath,
132 &pszHostPath, pcbFullPathRoot, fu32Options, &fu32PathFlags);
133 if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
134 {
135 LogRel2(("SharedFolders: GuestToHost 0x%RX32 [%.*s]->[%s] %Rrc\n", fu32PathFlags, pPath->u16Length, &pPath->String.utf8[0], pszHostPath, rc));
136 }
137 else
138 {
139 LogRel2(("SharedFolders: GuestToHost 0x%RX32 [%.*ls]->[%s] %Rrc\n", fu32PathFlags, pPath->u16Length / 2, &pPath->String.ucs2[0], pszHostPath, rc));
140 }
141
142 if (RT_SUCCESS(rc))
143 {
144 if (ppszFullPath)
145 *ppszFullPath = pszHostPath;
146 }
147 return rc;
148}
149
150static void vbsfFreeFullPath(char *pszFullPath)
151{
152 vbsfFreeHostPath(pszFullPath);
153}
154
155typedef enum VBSFCHECKACCESS
156{
157 VBSF_CHECK_ACCESS_READ = 0,
158 VBSF_CHECK_ACCESS_WRITE = 1
159} VBSFCHECKACCESS;
160
161/**
162 * Check if the handle data is valid and the operation is allowed on the shared folder.
163 *
164 * @returns IPRT status code
165 * @param pClient Data structure describing the client accessing the shared folder
166 * @param root The index of the shared folder in the table of mappings.
167 * @param pHandle Information about the file or directory object.
168 * @param enmCheckAccess Whether the operation needs read only or write access.
169 */
170static int vbsfCheckHandleAccess(SHFLCLIENTDATA *pClient, SHFLROOT root,
171 SHFLFILEHANDLE *pHandle, VBSFCHECKACCESS enmCheckAccess)
172{
173 /* Handle from the same 'root' index? */
174 if (RT_LIKELY(RT_VALID_PTR(pHandle) && root == pHandle->root))
175 { /* likely */ }
176 else
177 return VERR_INVALID_HANDLE;
178
179 /* Check if the guest is still allowed to access this share.
180 * vbsfMappingsQueryWritable returns error if the shared folder has been removed from the VM settings.
181 */
182 bool fWritable;
183 int rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
184 if (RT_SUCCESS(rc))
185 { /* likely */ }
186 else
187 return VERR_ACCESS_DENIED;
188
189 if (enmCheckAccess == VBSF_CHECK_ACCESS_WRITE)
190 {
191 /* Operation requires write access. Check if the shared folder is writable too. */
192 if (RT_LIKELY(fWritable))
193 { /* likely */ }
194 else
195 return VERR_WRITE_PROTECT;
196 }
197
198 return VINF_SUCCESS;
199}
200
201/**
202 * Convert shared folder create flags (see include/iprt/shflsvc.h) into iprt create flags.
203 *
204 * @returns iprt status code
205 * @param fWritable whether the shared folder is writable
206 * @param fShflFlags shared folder create flags
207 * @param fMode file attributes
208 * @param handleInitial initial handle
209 * @retval pfOpen iprt create flags
210 */
211static int vbsfConvertFileOpenFlags(bool fWritable, unsigned fShflFlags, RTFMODE fMode, SHFLHANDLE handleInitial, uint32_t *pfOpen)
212{
213 uint32_t fOpen = 0;
214 int rc = VINF_SUCCESS;
215
216 if ( (fMode & RTFS_DOS_MASK) != 0
217 && (fMode & RTFS_UNIX_MASK) == 0)
218 {
219 /* A DOS/Windows guest, make RTFS_UNIX_* from RTFS_DOS_*.
220 * @todo this is based on rtFsModeNormalize/rtFsModeFromDos.
221 * May be better to use RTFsModeNormalize here.
222 */
223 fMode |= RTFS_UNIX_IRUSR | RTFS_UNIX_IRGRP | RTFS_UNIX_IROTH;
224 /* x for directories. */
225 if (fMode & RTFS_DOS_DIRECTORY)
226 fMode |= RTFS_TYPE_DIRECTORY | RTFS_UNIX_IXUSR | RTFS_UNIX_IXGRP | RTFS_UNIX_IXOTH;
227 /* writable? */
228 if (!(fMode & RTFS_DOS_READONLY))
229 fMode |= RTFS_UNIX_IWUSR | RTFS_UNIX_IWGRP | RTFS_UNIX_IWOTH;
230
231 /* Set the requested mode using only allowed bits. */
232 fOpen |= ((fMode & RTFS_UNIX_MASK) << RTFILE_O_CREATE_MODE_SHIFT) & RTFILE_O_CREATE_MODE_MASK;
233 }
234 else
235 {
236 /* Old linux and solaris additions did not initialize the Info.Attr.fMode field
237 * and it contained random bits from stack. Detect this using the handle field value
238 * passed from the guest: old additions set it (incorrectly) to 0, new additions
239 * set it to SHFL_HANDLE_NIL(~0).
240 */
241 if (handleInitial == 0)
242 {
243 /* Old additions. Do nothing, use default mode. */
244 }
245 else
246 {
247 /* New additions or Windows additions. Set the requested mode using only allowed bits.
248 * Note: Windows guest set RTFS_UNIX_MASK bits to 0, which means a default mode
249 * will be set in fOpen.
250 */
251 fOpen |= ((fMode & RTFS_UNIX_MASK) << RTFILE_O_CREATE_MODE_SHIFT) & RTFILE_O_CREATE_MODE_MASK;
252 }
253 }
254
255 switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_RW))
256 {
257 default:
258 case SHFL_CF_ACCESS_NONE:
259 {
260#ifdef RT_OS_WINDOWS
261 if (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_ATTR) != SHFL_CF_ACCESS_ATTR_NONE)
262 fOpen |= RTFILE_O_ATTR_ONLY;
263 else
264#endif
265 fOpen |= RTFILE_O_READ;
266 Log(("FLAG: SHFL_CF_ACCESS_NONE\n"));
267 break;
268 }
269
270 case SHFL_CF_ACCESS_READ:
271 {
272 fOpen |= RTFILE_O_READ;
273 Log(("FLAG: SHFL_CF_ACCESS_READ\n"));
274 break;
275 }
276
277 case SHFL_CF_ACCESS_WRITE:
278 {
279 fOpen |= RTFILE_O_WRITE;
280 Log(("FLAG: SHFL_CF_ACCESS_WRITE\n"));
281 break;
282 }
283
284 case SHFL_CF_ACCESS_READWRITE:
285 {
286 fOpen |= RTFILE_O_READWRITE;
287 Log(("FLAG: SHFL_CF_ACCESS_READWRITE\n"));
288 break;
289 }
290 }
291
292 if (fShflFlags & SHFL_CF_ACCESS_APPEND)
293 {
294 fOpen |= RTFILE_O_APPEND;
295 }
296
297 switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_ATTR))
298 {
299 default:
300 case SHFL_CF_ACCESS_ATTR_NONE:
301 {
302 fOpen |= RTFILE_O_ACCESS_ATTR_DEFAULT;
303 Log(("FLAG: SHFL_CF_ACCESS_ATTR_NONE\n"));
304 break;
305 }
306
307 case SHFL_CF_ACCESS_ATTR_READ:
308 {
309 fOpen |= RTFILE_O_ACCESS_ATTR_READ;
310 Log(("FLAG: SHFL_CF_ACCESS_ATTR_READ\n"));
311 break;
312 }
313
314 case SHFL_CF_ACCESS_ATTR_WRITE:
315 {
316 fOpen |= RTFILE_O_ACCESS_ATTR_WRITE;
317 Log(("FLAG: SHFL_CF_ACCESS_ATTR_WRITE\n"));
318 break;
319 }
320
321 case SHFL_CF_ACCESS_ATTR_READWRITE:
322 {
323 fOpen |= RTFILE_O_ACCESS_ATTR_READWRITE;
324 Log(("FLAG: SHFL_CF_ACCESS_ATTR_READWRITE\n"));
325 break;
326 }
327 }
328
329 /* Sharing mask */
330 switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_DENY))
331 {
332 default:
333 case SHFL_CF_ACCESS_DENYNONE:
334 fOpen |= RTFILE_O_DENY_NONE;
335 Log(("FLAG: SHFL_CF_ACCESS_DENYNONE\n"));
336 break;
337
338 case SHFL_CF_ACCESS_DENYREAD:
339 fOpen |= RTFILE_O_DENY_READ;
340 Log(("FLAG: SHFL_CF_ACCESS_DENYREAD\n"));
341 break;
342
343 case SHFL_CF_ACCESS_DENYWRITE:
344 fOpen |= RTFILE_O_DENY_WRITE;
345 Log(("FLAG: SHFL_CF_ACCESS_DENYWRITE\n"));
346 break;
347
348 case SHFL_CF_ACCESS_DENYALL:
349 fOpen |= RTFILE_O_DENY_ALL;
350 Log(("FLAG: SHFL_CF_ACCESS_DENYALL\n"));
351 break;
352 }
353
354 /* Open/Create action mask */
355 switch (BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
356 {
357 case SHFL_CF_ACT_OPEN_IF_EXISTS:
358 if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
359 {
360 fOpen |= RTFILE_O_OPEN_CREATE;
361 Log(("FLAGS: SHFL_CF_ACT_OPEN_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
362 }
363 else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
364 {
365 fOpen |= RTFILE_O_OPEN;
366 Log(("FLAGS: SHFL_CF_ACT_OPEN_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n"));
367 }
368 else
369 {
370 Log(("FLAGS: invalid open/create action combination\n"));
371 rc = VERR_INVALID_PARAMETER;
372 }
373 break;
374 case SHFL_CF_ACT_FAIL_IF_EXISTS:
375 if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
376 {
377 fOpen |= RTFILE_O_CREATE;
378 Log(("FLAGS: SHFL_CF_ACT_FAIL_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
379 }
380 else
381 {
382 Log(("FLAGS: invalid open/create action combination\n"));
383 rc = VERR_INVALID_PARAMETER;
384 }
385 break;
386 case SHFL_CF_ACT_REPLACE_IF_EXISTS:
387 if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
388 {
389 fOpen |= RTFILE_O_CREATE_REPLACE;
390 Log(("FLAGS: SHFL_CF_ACT_REPLACE_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
391 }
392 else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
393 {
394 fOpen |= RTFILE_O_OPEN | RTFILE_O_TRUNCATE;
395 Log(("FLAGS: SHFL_CF_ACT_REPLACE_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n"));
396 }
397 else
398 {
399 Log(("FLAGS: invalid open/create action combination\n"));
400 rc = VERR_INVALID_PARAMETER;
401 }
402 break;
403 case SHFL_CF_ACT_OVERWRITE_IF_EXISTS:
404 if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
405 {
406 fOpen |= RTFILE_O_CREATE_REPLACE;
407 Log(("FLAGS: SHFL_CF_ACT_OVERWRITE_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
408 }
409 else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
410 {
411 fOpen |= RTFILE_O_OPEN | RTFILE_O_TRUNCATE;
412 Log(("FLAGS: SHFL_CF_ACT_OVERWRITE_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n"));
413 }
414 else
415 {
416 Log(("FLAGS: invalid open/create action combination\n"));
417 rc = VERR_INVALID_PARAMETER;
418 }
419 break;
420 default:
421 rc = VERR_INVALID_PARAMETER;
422 Log(("FLAG: SHFL_CF_ACT_MASK_IF_EXISTS - invalid parameter\n"));
423 }
424
425 if (RT_SUCCESS(rc))
426 {
427 if (!fWritable)
428 fOpen &= ~RTFILE_O_WRITE;
429
430 *pfOpen = fOpen;
431 }
432 return rc;
433}
434
435/**
436 * Open a file or create and open a new one.
437 *
438 * @returns IPRT status code
439 * @param pClient Data structure describing the client accessing the shared folder
440 * @param root The index of the shared folder in the table of mappings.
441 * @param pszPath Path to the file or folder on the host.
442 * @param pParms @a CreateFlags Creation or open parameters, see include/VBox/shflsvc.h
443 * @param pParms @a Info When a new file is created this specifies the initial parameters.
444 * When a file is created or overwritten, it also specifies the
445 * initial size.
446 * @retval pParms @a Resulte Shared folder status code, see include/VBox/shflsvc.h
447 * @retval pParms @a Handle On success the (shared folder) handle of the file opened or
448 * created
449 * @retval pParms @a Info On success the parameters of the file opened or created
450 */
451static int vbsfOpenFile(SHFLCLIENTDATA *pClient, SHFLROOT root, const char *pszPath, SHFLCREATEPARMS *pParms)
452{
453 LogFlow(("vbsfOpenFile: pszPath = %s, pParms = %p\n", pszPath, pParms));
454 Log(("SHFL create flags %08x\n", pParms->CreateFlags));
455
456 SHFLHANDLE handle = SHFL_HANDLE_NIL;
457 SHFLFILEHANDLE *pHandle = 0;
458 /* Open or create a file. */
459 uint32_t fOpen = 0;
460 bool fNoError = false;
461 static int cErrors;
462
463 /* is the guest allowed to write to this share? */
464 bool fWritable;
465 int rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
466 if (RT_FAILURE(rc))
467 fWritable = false;
468
469 rc = vbsfConvertFileOpenFlags(fWritable, pParms->CreateFlags, pParms->Info.Attr.fMode, pParms->Handle, &fOpen);
470 if (RT_SUCCESS(rc))
471 {
472 rc = VERR_NO_MEMORY; /* Default error. */
473 handle = vbsfAllocFileHandle(pClient);
474 if (handle != SHFL_HANDLE_NIL)
475 {
476 pHandle = vbsfQueryFileHandle(pClient, handle);
477 if (pHandle)
478 {
479 pHandle->root = root;
480 rc = RTFileOpen(&pHandle->file.Handle, pszPath, fOpen);
481 }
482 }
483 }
484 if (RT_FAILURE(rc))
485 {
486 switch (rc)
487 {
488 case VERR_FILE_NOT_FOUND:
489 pParms->Result = SHFL_FILE_NOT_FOUND;
490
491 /* This actually isn't an error, so correct the rc before return later,
492 because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
493 fNoError = true;
494 break;
495 case VERR_PATH_NOT_FOUND:
496 pParms->Result = SHFL_PATH_NOT_FOUND;
497
498 /* This actually isn't an error, so correct the rc before return later,
499 because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
500 fNoError = true;
501 break;
502 case VERR_ALREADY_EXISTS:
503 RTFSOBJINFO info;
504
505 /** @todo Possible race left here. */
506 if (RT_SUCCESS(RTPathQueryInfoEx(pszPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient))))
507 {
508#ifdef RT_OS_WINDOWS
509 info.Attr.fMode |= 0111;
510#endif
511 vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
512 }
513 pParms->Result = SHFL_FILE_EXISTS;
514
515 /* This actually isn't an error, so correct the rc before return later,
516 because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
517 fNoError = true;
518 break;
519 case VERR_TOO_MANY_OPEN_FILES:
520 if (cErrors < 32)
521 {
522 LogRel(("SharedFolders host service: Cannot open '%s' -- too many open files.\n", pszPath));
523#if defined RT_OS_LINUX || defined(RT_OS_SOLARIS)
524 if (cErrors < 1)
525 LogRel(("SharedFolders host service: Try to increase the limit for open files (ulimit -n)\n"));
526#endif
527 cErrors++;
528 }
529 pParms->Result = SHFL_NO_RESULT;
530 break;
531 default:
532 pParms->Result = SHFL_NO_RESULT;
533 }
534 }
535 else
536 {
537 /** @note The shared folder status code is very approximate, as the runtime
538 * does not really provide this information. */
539 pParms->Result = SHFL_FILE_EXISTS; /* We lost the information as to whether it was
540 created when we eliminated the race. */
541 if ( ( SHFL_CF_ACT_REPLACE_IF_EXISTS
542 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
543 || ( SHFL_CF_ACT_OVERWRITE_IF_EXISTS
544 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS)))
545 {
546 /* For now, we do not treat a failure here as fatal. */
547 /** @todo Also set the size for SHFL_CF_ACT_CREATE_IF_NEW if
548 SHFL_CF_ACT_FAIL_IF_EXISTS is set. */
549 RTFileSetSize(pHandle->file.Handle, pParms->Info.cbObject);
550 pParms->Result = SHFL_FILE_REPLACED;
551 }
552 if ( ( SHFL_CF_ACT_FAIL_IF_EXISTS
553 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
554 || ( SHFL_CF_ACT_CREATE_IF_NEW
555 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW)))
556 {
557 pParms->Result = SHFL_FILE_CREATED;
558 }
559#if 0
560 /** @todo */
561 /* Set new attributes. */
562 if ( ( SHFL_CF_ACT_REPLACE_IF_EXISTS
563 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
564 || ( SHFL_CF_ACT_CREATE_IF_NEW
565 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW)))
566 {
567 RTFileSetTimes(pHandle->file.Handle,
568 &pParms->Info.AccessTime,
569 &pParms->Info.ModificationTime,
570 &pParms->Info.ChangeTime,
571 &pParms->Info.BirthTime
572 );
573
574 RTFileSetMode (pHandle->file.Handle, pParms->Info.Attr.fMode);
575 }
576#endif
577 RTFSOBJINFO info;
578
579 /* Get file information */
580 rc = RTFileQueryInfo(pHandle->file.Handle, &info, RTFSOBJATTRADD_NOTHING);
581 if (RT_SUCCESS(rc))
582 {
583#ifdef RT_OS_WINDOWS
584 info.Attr.fMode |= 0111;
585#endif
586 vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
587 }
588 }
589 /* Free resources if any part of the function has failed. */
590 if (RT_FAILURE(rc))
591 {
592 if ( (0 != pHandle)
593 && (NIL_RTFILE != pHandle->file.Handle)
594 && (0 != pHandle->file.Handle))
595 {
596 RTFileClose(pHandle->file.Handle);
597 pHandle->file.Handle = NIL_RTFILE;
598 }
599 if (SHFL_HANDLE_NIL != handle)
600 {
601 vbsfFreeFileHandle(pClient, handle);
602 }
603 pParms->Handle = SHFL_HANDLE_NIL;
604 }
605 else
606 {
607 pParms->Handle = handle;
608 }
609
610 /* Report the driver that all is okay, we're done here */
611 if (fNoError)
612 rc = VINF_SUCCESS;
613
614 LogFlow(("vbsfOpenFile: rc = %Rrc\n", rc));
615 return rc;
616}
617
618/**
619 * Open a folder or create and open a new one.
620 *
621 * @returns IPRT status code
622 * @param pClient Data structure describing the client accessing the shared folder
623 * @param root The index of the shared folder in the table of mappings.
624 * @param pszPath Path to the file or folder on the host.
625 * @param pParms @a CreateFlags Creation or open parameters, see include/VBox/shflsvc.h
626 * @retval pParms @a Result Shared folder status code, see include/VBox/shflsvc.h
627 * @retval pParms @a Handle On success the (shared folder) handle of the folder opened or
628 * created
629 * @retval pParms @a Info On success the parameters of the folder opened or created
630 *
631 * @note folders are created with fMode = 0777
632 */
633static int vbsfOpenDir(SHFLCLIENTDATA *pClient, SHFLROOT root, const char *pszPath,
634 SHFLCREATEPARMS *pParms)
635{
636 LogFlow(("vbsfOpenDir: pszPath = %s, pParms = %p\n", pszPath, pParms));
637 Log(("SHFL create flags %08x\n", pParms->CreateFlags));
638
639 int rc = VERR_NO_MEMORY;
640 SHFLHANDLE handle = vbsfAllocDirHandle(pClient);
641 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, handle);
642 if (0 != pHandle)
643 {
644 pHandle->root = root;
645 rc = VINF_SUCCESS;
646 pParms->Result = SHFL_FILE_EXISTS; /* May be overwritten with SHFL_FILE_CREATED. */
647 /** @todo Can anyone think of a sensible, race-less way to do this? Although
648 I suspect that the race is inherent, due to the API available... */
649 /* Try to create the folder first if "create if new" is specified. If this
650 fails, and "open if exists" is specified, then we ignore the failure and try
651 to open the folder anyway. */
652 if ( SHFL_CF_ACT_CREATE_IF_NEW
653 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW))
654 {
655 /** @todo render supplied attributes.
656 * bird: The guest should specify this. For windows guests RTFS_DOS_DIRECTORY should suffice. */
657 RTFMODE fMode = 0777;
658
659 pParms->Result = SHFL_FILE_CREATED;
660 rc = RTDirCreate(pszPath, fMode, 0);
661 if (RT_FAILURE(rc))
662 {
663 switch (rc)
664 {
665 case VERR_ALREADY_EXISTS:
666 pParms->Result = SHFL_FILE_EXISTS;
667 break;
668 case VERR_PATH_NOT_FOUND:
669 pParms->Result = SHFL_PATH_NOT_FOUND;
670 break;
671 default:
672 pParms->Result = SHFL_NO_RESULT;
673 }
674 }
675 }
676 if ( RT_SUCCESS(rc)
677 || (SHFL_CF_ACT_OPEN_IF_EXISTS == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS)))
678 {
679 /* Open the directory now */
680 rc = RTDirOpenFiltered(&pHandle->dir.Handle, pszPath, RTDIRFILTER_NONE, 0 /*fFlags*/);
681 if (RT_SUCCESS(rc))
682 {
683 RTFSOBJINFO info;
684
685 rc = RTDirQueryInfo(pHandle->dir.Handle, &info, RTFSOBJATTRADD_NOTHING);
686 if (RT_SUCCESS(rc))
687 {
688 vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
689 }
690 }
691 else
692 {
693 switch (rc)
694 {
695 case VERR_FILE_NOT_FOUND: /* Does this make sense? */
696 pParms->Result = SHFL_FILE_NOT_FOUND;
697 break;
698 case VERR_PATH_NOT_FOUND:
699 pParms->Result = SHFL_PATH_NOT_FOUND;
700 break;
701 case VERR_ACCESS_DENIED:
702 pParms->Result = SHFL_FILE_EXISTS;
703 break;
704 default:
705 pParms->Result = SHFL_NO_RESULT;
706 }
707 }
708 }
709 }
710 if (RT_FAILURE(rc))
711 {
712 if ( (0 != pHandle)
713 && (0 != pHandle->dir.Handle))
714 {
715 RTDirClose(pHandle->dir.Handle);
716 pHandle->dir.Handle = 0;
717 }
718 if (SHFL_HANDLE_NIL != handle)
719 {
720 vbsfFreeFileHandle(pClient, handle);
721 }
722 pParms->Handle = SHFL_HANDLE_NIL;
723 }
724 else
725 {
726 pParms->Handle = handle;
727 }
728 LogFlow(("vbsfOpenDir: rc = %Rrc\n", rc));
729 return rc;
730}
731
732static int vbsfCloseDir(SHFLFILEHANDLE *pHandle)
733{
734 int rc = VINF_SUCCESS;
735
736 LogFlow(("vbsfCloseDir: Handle = %08X Search Handle = %08X\n",
737 pHandle->dir.Handle, pHandle->dir.SearchHandle));
738
739 RTDirClose(pHandle->dir.Handle);
740
741 if (pHandle->dir.SearchHandle)
742 RTDirClose(pHandle->dir.SearchHandle);
743
744 if (pHandle->dir.pLastValidEntry)
745 {
746 RTMemFree(pHandle->dir.pLastValidEntry);
747 pHandle->dir.pLastValidEntry = NULL;
748 }
749
750 LogFlow(("vbsfCloseDir: rc = %d\n", rc));
751
752 return rc;
753}
754
755
756static int vbsfCloseFile(SHFLFILEHANDLE *pHandle)
757{
758 int rc = VINF_SUCCESS;
759
760 LogFlow(("vbsfCloseFile: Handle = %08X\n",
761 pHandle->file.Handle));
762
763 rc = RTFileClose(pHandle->file.Handle);
764
765 LogFlow(("vbsfCloseFile: rc = %d\n", rc));
766
767 return rc;
768}
769
770/**
771 * Look up file or folder information by host path.
772 *
773 * @returns iprt status code (currently VINF_SUCCESS)
774 * @param pClient client data
775 * @param pszPath The path of the file to be looked up
776 * @retval pParms->Result Status of the operation (success or error)
777 * @retval pParms->Info On success, information returned about the file
778 */
779static int vbsfLookupFile(SHFLCLIENTDATA *pClient, char *pszPath, SHFLCREATEPARMS *pParms)
780{
781 RTFSOBJINFO info;
782 int rc;
783
784 rc = RTPathQueryInfoEx(pszPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
785 LogFlow(("SHFL_CF_LOOKUP\n"));
786 /* Client just wants to know if the object exists. */
787 switch (rc)
788 {
789 case VINF_SUCCESS:
790 {
791#ifdef RT_OS_WINDOWS
792 info.Attr.fMode |= 0111;
793#endif
794 vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
795 pParms->Result = SHFL_FILE_EXISTS;
796 break;
797 }
798
799 case VERR_FILE_NOT_FOUND:
800 {
801 pParms->Result = SHFL_FILE_NOT_FOUND;
802 rc = VINF_SUCCESS;
803 break;
804 }
805
806 case VERR_PATH_NOT_FOUND:
807 {
808 pParms->Result = SHFL_PATH_NOT_FOUND;
809 rc = VINF_SUCCESS;
810 break;
811 }
812 }
813 pParms->Handle = SHFL_HANDLE_NIL;
814 return rc;
815}
816
817#ifdef UNITTEST
818/** Unit test the SHFL_FN_CREATE API. Located here as a form of API
819 * documentation. */
820void testCreate(RTTEST hTest)
821{
822 /* Simple opening of an existing file. */
823 testCreateFileSimple(hTest);
824 testCreateFileSimpleCaseInsensitive(hTest);
825 /* Simple opening of an existing directory. */
826 /** @todo How do wildcards in the path name work? */
827 testCreateDirSimple(hTest);
828 /* If the number or types of parameters are wrong the API should fail. */
829 testCreateBadParameters(hTest);
830 /* Add tests as required... */
831}
832#endif
833
834/**
835 * Create or open a file or folder. Perform character set and case
836 * conversion on the file name if necessary.
837 *
838 * @returns IPRT status code, but see note below
839 * @param pClient Data structure describing the client accessing the shared
840 * folder
841 * @param root The index of the shared folder in the table of mappings.
842 * The host path of the shared folder is found using this.
843 * @param pPath The path of the file or folder relative to the host path
844 * indexed by root.
845 * @param cbPath Presumably the length of the path in pPath. Actually
846 * ignored, as pPath contains a length parameter.
847 * @param pParms @a Info If a new file is created or an old one overwritten, set
848 * these attributes
849 * @retval pParms @a Result Shared folder result code, see include/VBox/shflsvc.h
850 * @retval pParms @a Handle Shared folder handle to the newly opened file
851 * @retval pParms @a Info Attributes of the file or folder opened
852 *
853 * @note This function returns success if a "non-exceptional" error occurred,
854 * such as "no such file". In this case, the caller should check the
855 * pParms->Result return value and whether pParms->Handle is valid.
856 */
857int vbsfCreate(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, SHFLCREATEPARMS *pParms)
858{
859 int rc = VINF_SUCCESS;
860
861 LogFlow(("vbsfCreate: pClient = %p, pPath = %p, cbPath = %d, pParms = %p CreateFlags=%x\n",
862 pClient, pPath, cbPath, pParms, pParms->CreateFlags));
863
864 /* Check the client access rights to the root. */
865 /** @todo */
866
867 /* Build a host full path for the given path, handle file name case issues (if the guest
868 * expects case-insensitive paths but the host is case-sensitive) and convert ucs2 to utf8 if
869 * necessary.
870 */
871 char *pszFullPath = NULL;
872 uint32_t cbFullPathRoot = 0;
873
874 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot);
875 if (RT_SUCCESS(rc))
876 {
877 /* Reset return value in case client forgot to do so.
878 * pParms->Handle must not be reset here, as it is used
879 * in vbsfOpenFile to detect old additions.
880 */
881 pParms->Result = SHFL_NO_RESULT;
882
883 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_LOOKUP))
884 {
885 rc = vbsfLookupFile(pClient, pszFullPath, pParms);
886 }
887 else
888 {
889 /* Query path information. */
890 RTFSOBJINFO info;
891
892 rc = RTPathQueryInfoEx(pszFullPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
893 LogFlow(("RTPathQueryInfoEx returned %Rrc\n", rc));
894
895 if (RT_SUCCESS(rc))
896 {
897 /* Mark it as a directory in case the caller didn't. */
898 /**
899 * @todo I left this in in order not to change the behaviour of the
900 * function too much. Is it really needed, and should it really be
901 * here?
902 */
903 if (BIT_FLAG(info.Attr.fMode, RTFS_DOS_DIRECTORY))
904 {
905 pParms->CreateFlags |= SHFL_CF_DIRECTORY;
906 }
907
908 /**
909 * @todo This should be in the Windows Guest Additions, as no-one else
910 * needs it.
911 */
912 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_OPEN_TARGET_DIRECTORY))
913 {
914 vbsfStripLastComponent(pszFullPath, cbFullPathRoot);
915 pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_EXISTS;
916 pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_NEW;
917 pParms->CreateFlags |= SHFL_CF_DIRECTORY;
918 pParms->CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS;
919 pParms->CreateFlags |= SHFL_CF_ACT_FAIL_IF_NEW;
920 }
921 }
922
923 rc = VINF_SUCCESS;
924
925 /* Note: do not check the SHFL_CF_ACCESS_WRITE here, only check if the open operation
926 * will cause changes.
927 *
928 * Actual operations (write, set attr, etc), which can write to a shared folder, have
929 * the check and will return VERR_WRITE_PROTECT if the folder is not writable.
930 */
931 if ( (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_REPLACE_IF_EXISTS
932 || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_OVERWRITE_IF_EXISTS
933 || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_NEW) == SHFL_CF_ACT_CREATE_IF_NEW
934 )
935 {
936 /* is the guest allowed to write to this share? */
937 bool fWritable;
938 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
939 if (RT_FAILURE(rc) || !fWritable)
940 rc = VERR_WRITE_PROTECT;
941 }
942
943 if (RT_SUCCESS(rc))
944 {
945 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_DIRECTORY))
946 {
947 rc = vbsfOpenDir(pClient, root, pszFullPath, pParms);
948 }
949 else
950 {
951 rc = vbsfOpenFile(pClient, root, pszFullPath, pParms);
952 }
953 }
954 else
955 {
956 pParms->Handle = SHFL_HANDLE_NIL;
957 }
958 }
959
960 /* free the path string */
961 vbsfFreeFullPath(pszFullPath);
962 }
963
964 Log(("vbsfCreate: handle = %RX64 rc = %Rrc result=%x\n", (uint64_t)pParms->Handle, rc, pParms->Result));
965
966 return rc;
967}
968
969#ifdef UNITTEST
970/** Unit test the SHFL_FN_CLOSE API. Located here as a form of API
971 * documentation. */
972void testClose(RTTEST hTest)
973{
974 /* If the API parameters are invalid the API should fail. */
975 testCloseBadParameters(hTest);
976 /* Add tests as required... */
977}
978#endif
979
980int vbsfClose(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
981{
982 LogFunc(("pClient = %p, root 0x%RX32, Handle = 0x%RX64\n",
983 pClient, root, Handle));
984
985 int rc = VERR_INVALID_HANDLE;
986 uint32_t type = vbsfQueryHandleType(pClient, Handle);
987 Assert((type & ~(SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE)) == 0);
988 switch (type & (SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE))
989 {
990 case SHFL_HF_TYPE_DIR:
991 {
992 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
993 if (RT_LIKELY(pHandle && root == pHandle->root))
994 {
995 rc = vbsfCloseDir(pHandle);
996 vbsfFreeFileHandle(pClient, Handle);
997 }
998 break;
999 }
1000 case SHFL_HF_TYPE_FILE:
1001 {
1002 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1003 if (RT_LIKELY(pHandle && root == pHandle->root))
1004 {
1005 rc = vbsfCloseFile(pHandle);
1006 vbsfFreeFileHandle(pClient, Handle);
1007 }
1008 break;
1009 }
1010 default:
1011 break;
1012 }
1013
1014 LogFunc(("rc = %Rrc\n", rc));
1015 return rc;
1016}
1017
1018#ifdef UNITTEST
1019/** Unit test the SHFL_FN_READ API. Located here as a form of API
1020 * documentation. */
1021void testRead(RTTEST hTest)
1022{
1023 /* If the number or types of parameters are wrong the API should fail. */
1024 testReadBadParameters(hTest);
1025 /* Basic reading from a file. */
1026 testReadFileSimple(hTest);
1027 /* Add tests as required... */
1028}
1029#endif
1030int vbsfRead(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer)
1031{
1032 LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64, offset 0x%RX64, bytes 0x%RX32\n",
1033 pClient, root, Handle, offset, pcbBuffer? *pcbBuffer: 0));
1034
1035 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1036
1037 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1038 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1039 if (RT_SUCCESS(rc))
1040 { /* likely */ }
1041 else
1042 return rc;
1043
1044 if (RT_LIKELY(*pcbBuffer != 0))
1045 {
1046 rc = RTFileSeek(pHandle->file.Handle, offset, RTFILE_SEEK_BEGIN, NULL);
1047 if (RT_SUCCESS(rc))
1048 {
1049 size_t count = 0;
1050 rc = RTFileRead(pHandle->file.Handle, pBuffer, *pcbBuffer, &count);
1051 *pcbBuffer = (uint32_t)count;
1052 }
1053 else
1054 AssertRC(rc);
1055 }
1056 else
1057 {
1058 /* Reading zero bytes always succeeds. */
1059 rc = VINF_SUCCESS;
1060 }
1061
1062 LogFunc(("%Rrc bytes read 0x%RX32\n", rc, *pcbBuffer));
1063 return rc;
1064}
1065
1066#ifdef UNITTEST
1067/** Unit test the SHFL_FN_WRITE API. Located here as a form of API
1068 * documentation. */
1069void testWrite(RTTEST hTest)
1070{
1071 /* If the number or types of parameters are wrong the API should fail. */
1072 testWriteBadParameters(hTest);
1073 /* Simple test of writing to a file. */
1074 testWriteFileSimple(hTest);
1075 /* Add tests as required... */
1076}
1077#endif
1078int vbsfWrite(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer)
1079{
1080 LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64, offset 0x%RX64, bytes 0x%RX32\n",
1081 pClient, root, Handle, offset, pcbBuffer? *pcbBuffer: 0));
1082
1083 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1084
1085 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1086 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1087 if (RT_SUCCESS(rc))
1088 { /* likely */ }
1089 else
1090 return rc;
1091
1092 if (RT_LIKELY(*pcbBuffer != 0))
1093 {
1094 rc = RTFileSeek(pHandle->file.Handle, offset, RTFILE_SEEK_BEGIN, NULL);
1095 if (RT_SUCCESS(rc))
1096 {
1097 size_t count = 0;
1098 rc = RTFileWrite(pHandle->file.Handle, pBuffer, *pcbBuffer, &count);
1099 *pcbBuffer = (uint32_t)count;
1100 }
1101 else
1102 AssertRC(rc);
1103 }
1104 else
1105 {
1106 /** @todo What writing zero bytes should do? */
1107 rc = VINF_SUCCESS;
1108 }
1109
1110 LogFunc(("%Rrc bytes written 0x%RX32\n", rc, *pcbBuffer));
1111 return rc;
1112}
1113
1114
1115#ifdef UNITTEST
1116/** Unit test the SHFL_FN_FLUSH API. Located here as a form of API
1117 * documentation. */
1118void testFlush(RTTEST hTest)
1119{
1120 /* If the number or types of parameters are wrong the API should fail. */
1121 testFlushBadParameters(hTest);
1122 /* Simple opening and flushing of a file. */
1123 testFlushFileSimple(hTest);
1124 /* Add tests as required... */
1125}
1126#endif
1127
1128int vbsfFlush(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
1129{
1130 LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64\n",
1131 pClient, root, Handle));
1132
1133 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1134
1135 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1136 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1137 if (RT_SUCCESS(rc))
1138 { /* likely */ }
1139 else
1140 return rc;
1141
1142 rc = RTFileFlush(pHandle->file.Handle);
1143
1144 LogFunc(("%Rrc\n", rc));
1145 return rc;
1146}
1147
1148#ifdef UNITTEST
1149/** Unit test the SHFL_FN_LIST API. Located here as a form of API
1150 * documentation. */
1151void testDirList(RTTEST hTest)
1152{
1153 /* If the number or types of parameters are wrong the API should fail. */
1154 testDirListBadParameters(hTest);
1155 /* Test listing an empty directory (simple edge case). */
1156 testDirListEmpty(hTest);
1157 /* Add tests as required... */
1158}
1159#endif
1160int vbsfDirList(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, SHFLSTRING *pPath, uint32_t flags,
1161 uint32_t *pcbBuffer, uint8_t *pBuffer, uint32_t *pIndex, uint32_t *pcFiles)
1162{
1163 PRTDIRENTRYEX pDirEntry = 0, pDirEntryOrg;
1164 uint32_t cbDirEntry, cbBufferOrg;
1165 PSHFLDIRINFO pSFDEntry;
1166 PRTUTF16 pwszString;
1167 RTDIR hDir;
1168 const bool fUtf8 = BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8) != 0;
1169
1170 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1171
1172 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
1173 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1174 if (RT_SUCCESS(rc))
1175 { /* likely */ }
1176 else
1177 return rc;
1178
1179 Assert(*pIndex == 0);
1180
1181 cbDirEntry = 4096;
1182 pDirEntryOrg = pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry);
1183 if (pDirEntry == 0)
1184 {
1185 AssertFailed();
1186 return VERR_NO_MEMORY;
1187 }
1188
1189 cbBufferOrg = *pcbBuffer;
1190 *pcbBuffer = 0;
1191 pSFDEntry = (PSHFLDIRINFO)pBuffer;
1192
1193 *pIndex = 1; /* not yet complete */
1194 *pcFiles = 0;
1195
1196 if (!pPath)
1197 hDir = pHandle->dir.Handle;
1198 else
1199 {
1200 if (pHandle->dir.SearchHandle == 0)
1201 {
1202 /* Build a host full path for the given path
1203 * and convert ucs2 to utf8 if necessary.
1204 */
1205 char *pszFullPath = NULL;
1206
1207 Assert(pHandle->dir.pLastValidEntry == 0);
1208
1209 rc = vbsfBuildFullPath(pClient, root, pPath, pPath->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPath, NULL, true);
1210
1211 if (RT_SUCCESS(rc))
1212 {
1213 rc = RTDirOpenFiltered(&pHandle->dir.SearchHandle, pszFullPath, RTDIRFILTER_WINNT, 0 /*fFlags*/);
1214
1215 /* free the path string */
1216 vbsfFreeFullPath(pszFullPath);
1217
1218 if (RT_FAILURE(rc))
1219 goto end;
1220 }
1221 else
1222 goto end;
1223 flags &= ~SHFL_LIST_RESTART;
1224 }
1225 Assert(pHandle->dir.SearchHandle);
1226 hDir = pHandle->dir.SearchHandle;
1227 }
1228
1229 if (flags & SHFL_LIST_RESTART)
1230 {
1231 rc = RTDirRewind(hDir);
1232 if (RT_FAILURE(rc))
1233 goto end;
1234 }
1235
1236 while (cbBufferOrg)
1237 {
1238 size_t cbDirEntrySize = cbDirEntry;
1239 uint32_t cbNeeded;
1240
1241 /* Do we still have a valid last entry for the active search? If so, then return it here */
1242 if (pHandle->dir.pLastValidEntry)
1243 {
1244 pDirEntry = pHandle->dir.pLastValidEntry;
1245 }
1246 else
1247 {
1248 pDirEntry = pDirEntryOrg;
1249
1250 rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
1251 if (rc == VERR_NO_MORE_FILES)
1252 {
1253 *pIndex = 0; /* listing completed */
1254 break;
1255 }
1256
1257 if ( rc != VINF_SUCCESS
1258 && rc != VWRN_NO_DIRENT_INFO)
1259 {
1260 //AssertFailed();
1261 if ( rc == VERR_NO_TRANSLATION
1262 || rc == VERR_INVALID_UTF8_ENCODING)
1263 continue;
1264 break;
1265 }
1266 }
1267
1268 cbNeeded = RT_OFFSETOF(SHFLDIRINFO, name.String);
1269 if (fUtf8)
1270 cbNeeded += pDirEntry->cbName + 1;
1271 else
1272 /* Overestimating, but that's ok */
1273 cbNeeded += (pDirEntry->cbName + 1) * 2;
1274
1275 if (cbBufferOrg < cbNeeded)
1276 {
1277 /* No room, so save this directory entry, or else it's lost forever */
1278 pHandle->dir.pLastValidEntry = pDirEntry;
1279
1280 if (*pcFiles == 0)
1281 {
1282 AssertFailed();
1283 return VINF_BUFFER_OVERFLOW; /* Return directly and don't free pDirEntry */
1284 }
1285 return VINF_SUCCESS; /* Return directly and don't free pDirEntry */
1286 }
1287
1288#ifdef RT_OS_WINDOWS
1289 pDirEntry->Info.Attr.fMode |= 0111;
1290#endif
1291 vbfsCopyFsObjInfoFromIprt(&pSFDEntry->Info, &pDirEntry->Info);
1292 pSFDEntry->cucShortName = 0;
1293
1294 if (fUtf8)
1295 {
1296 void *src, *dst;
1297
1298 src = &pDirEntry->szName[0];
1299 dst = &pSFDEntry->name.String.utf8[0];
1300
1301 memcpy(dst, src, pDirEntry->cbName + 1);
1302
1303 pSFDEntry->name.u16Size = pDirEntry->cbName + 1;
1304 pSFDEntry->name.u16Length = pDirEntry->cbName;
1305 }
1306 else
1307 {
1308 pSFDEntry->name.String.ucs2[0] = 0;
1309 pwszString = pSFDEntry->name.String.ucs2;
1310 int rc2 = RTStrToUtf16Ex(pDirEntry->szName, RTSTR_MAX, &pwszString, pDirEntry->cbName+1, NULL);
1311 AssertRC(rc2);
1312
1313#ifdef RT_OS_DARWIN
1314/** @todo This belongs in rtPathToNative or in the windows shared folder file system driver...
1315 * The question is simply whether the NFD normalization is actually applied on a (virtual) file
1316 * system level in darwin, or just by the user mode application libs. */
1317 {
1318 // Convert to
1319 // Normalization Form C (composed Unicode). We need this because
1320 // Mac OS X file system uses NFD (Normalization Form D :decomposed Unicode)
1321 // while most other OS', server-side programs usually expect NFC.
1322 uint16_t ucs2Length;
1323 CFRange rangeCharacters;
1324 CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, 0);
1325
1326 ::CFStringAppendCharacters(inStr, (UniChar *)pwszString, RTUtf16Len(pwszString));
1327 ::CFStringNormalize(inStr, kCFStringNormalizationFormC);
1328 ucs2Length = ::CFStringGetLength(inStr);
1329
1330 rangeCharacters.location = 0;
1331 rangeCharacters.length = ucs2Length;
1332 ::CFStringGetCharacters(inStr, rangeCharacters, pwszString);
1333 pwszString[ucs2Length] = 0x0000; // NULL terminated
1334
1335 CFRelease(inStr);
1336 }
1337#endif
1338 pSFDEntry->name.u16Length = (uint32_t)RTUtf16Len(pSFDEntry->name.String.ucs2) * 2;
1339 pSFDEntry->name.u16Size = pSFDEntry->name.u16Length + 2;
1340
1341 Log(("SHFL: File name size %d\n", pSFDEntry->name.u16Size));
1342 Log(("SHFL: File name %ls\n", &pSFDEntry->name.String.ucs2));
1343
1344 // adjust cbNeeded (it was overestimated before)
1345 cbNeeded = RT_OFFSETOF(SHFLDIRINFO, name.String) + pSFDEntry->name.u16Size;
1346 }
1347
1348 pSFDEntry = (PSHFLDIRINFO)((uintptr_t)pSFDEntry + cbNeeded);
1349 *pcbBuffer += cbNeeded;
1350 cbBufferOrg-= cbNeeded;
1351
1352 *pcFiles += 1;
1353
1354 /* Free the saved last entry, that we've just returned */
1355 if (pHandle->dir.pLastValidEntry)
1356 {
1357 RTMemFree(pHandle->dir.pLastValidEntry);
1358 pHandle->dir.pLastValidEntry = NULL;
1359
1360 /* And use the newly allocated buffer from now. */
1361 pDirEntry = pDirEntryOrg;
1362 }
1363
1364 if (flags & SHFL_LIST_RETURN_ONE)
1365 break; /* we're done */
1366 }
1367 Assert(rc != VINF_SUCCESS || *pcbBuffer > 0);
1368
1369end:
1370 if (pDirEntry)
1371 RTMemFree(pDirEntry);
1372
1373 return rc;
1374}
1375
1376#ifdef UNITTEST
1377/** Unit test the SHFL_FN_READLINK API. Located here as a form of API
1378 * documentation. */
1379void testReadLink(RTTEST hTest)
1380{
1381 /* If the number or types of parameters are wrong the API should fail. */
1382 testReadLinkBadParameters(hTest);
1383 /* Add tests as required... */
1384}
1385#endif
1386int vbsfReadLink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint8_t *pBuffer, uint32_t cbBuffer)
1387{
1388 int rc = VINF_SUCCESS;
1389
1390 if (pPath == 0 || pBuffer == 0)
1391 {
1392 AssertFailed();
1393 return VERR_INVALID_PARAMETER;
1394 }
1395
1396 /* Build a host full path for the given path, handle file name case issues
1397 * (if the guest expects case-insensitive paths but the host is
1398 * case-sensitive) and convert ucs2 to utf8 if necessary.
1399 */
1400 char *pszFullPath = NULL;
1401 uint32_t cbFullPathRoot = 0;
1402
1403 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot);
1404
1405 if (RT_SUCCESS(rc))
1406 {
1407 rc = RTSymlinkRead(pszFullPath, (char *) pBuffer, cbBuffer, 0);
1408 if (RT_SUCCESS(rc))
1409 {
1410 /* Convert the slashes in the link target to the guest path separator characters. */
1411 char *psz = (char *)pBuffer;
1412 while (*psz != '\0')
1413 {
1414 if (*psz == RTPATH_DELIMITER)
1415 *psz = pClient->PathDelimiter;
1416 psz++;
1417 }
1418 }
1419
1420 /* free the path string */
1421 vbsfFreeFullPath(pszFullPath);
1422 }
1423
1424 return rc;
1425}
1426
1427int vbsfQueryFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
1428 uint32_t *pcbBuffer, uint8_t *pBuffer)
1429{
1430 RT_NOREF1(flags);
1431 uint32_t type = vbsfQueryHandleType(pClient, Handle);
1432 int rc = VINF_SUCCESS;
1433 SHFLFSOBJINFO *pObjInfo = (SHFLFSOBJINFO *)pBuffer;
1434 RTFSOBJINFO fileinfo;
1435
1436
1437 AssertReturn(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE, VERR_INVALID_PARAMETER);
1438 AssertReturn(pcbBuffer != NULL, VERR_INVALID_PARAMETER);
1439 AssertReturn(pObjInfo != NULL, VERR_INVALID_PARAMETER);
1440 AssertReturn(*pcbBuffer >= sizeof(SHFLFSOBJINFO), VERR_INVALID_PARAMETER);
1441
1442 /** @todo other options */
1443 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_FILE));
1444
1445 *pcbBuffer = 0;
1446
1447 if (type == SHFL_HF_TYPE_DIR)
1448 {
1449 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
1450 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1451 if (RT_SUCCESS(rc))
1452 rc = RTDirQueryInfo(pHandle->dir.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1453 }
1454 else
1455 {
1456 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1457 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1458 if (RT_SUCCESS(rc))
1459 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1460#ifdef RT_OS_WINDOWS
1461 if (RT_SUCCESS(rc) && RTFS_IS_FILE(pObjInfo->Attr.fMode))
1462 pObjInfo->Attr.fMode |= 0111;
1463#endif
1464 }
1465 if (rc == VINF_SUCCESS)
1466 {
1467 vbfsCopyFsObjInfoFromIprt(pObjInfo, &fileinfo);
1468 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1469 }
1470 else
1471 AssertFailed();
1472
1473 return rc;
1474}
1475
1476static int vbsfSetFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
1477 uint32_t *pcbBuffer, uint8_t *pBuffer)
1478{
1479 RT_NOREF1(flags);
1480 uint32_t type = vbsfQueryHandleType(pClient, Handle);
1481 int rc = VINF_SUCCESS;
1482 SHFLFSOBJINFO *pSFDEntry;
1483
1484 if ( !(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE)
1485 || pcbBuffer == 0
1486 || pBuffer == 0
1487 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
1488 {
1489 AssertFailed();
1490 return VERR_INVALID_PARAMETER;
1491 }
1492
1493 *pcbBuffer = 0;
1494 pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
1495
1496 Assert(flags == (SHFL_INFO_SET | SHFL_INFO_FILE));
1497
1498 /* Change only the time values that are not zero */
1499 if (type == SHFL_HF_TYPE_DIR)
1500 {
1501 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
1502 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1503 if (RT_SUCCESS(rc))
1504 rc = RTDirSetTimes(pHandle->dir.Handle,
1505 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1506 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1507 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1508 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1509 );
1510 }
1511 else
1512 {
1513 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1514 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1515 if (RT_SUCCESS(rc))
1516 rc = RTFileSetTimes(pHandle->file.Handle,
1517 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1518 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1519 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1520 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1521 );
1522 }
1523 if (rc != VINF_SUCCESS)
1524 {
1525 Log(("RTFileSetTimes failed with %Rrc\n", rc));
1526 Log(("AccessTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->AccessTime)));
1527 Log(("ModificationTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ModificationTime)));
1528 Log(("ChangeTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ChangeTime)));
1529 Log(("BirthTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->BirthTime)));
1530 /* temporary hack */
1531 rc = VINF_SUCCESS;
1532 }
1533
1534 if (type == SHFL_HF_TYPE_FILE)
1535 {
1536 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1537 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1538 if (RT_SUCCESS(rc))
1539 {
1540 /* Change file attributes if necessary */
1541 if (pSFDEntry->Attr.fMode)
1542 {
1543 RTFMODE fMode = pSFDEntry->Attr.fMode;
1544
1545#ifndef RT_OS_WINDOWS
1546 /* Don't allow the guest to clear the own bit, otherwise the guest wouldn't be
1547 * able to access this file anymore. Only for guests, which set the UNIX mode.
1548 * Also, clear bits which we don't pass through for security reasons. */
1549 if (fMode & RTFS_UNIX_MASK)
1550 {
1551 fMode |= RTFS_UNIX_IRUSR;
1552 fMode &= ~(RTFS_UNIX_ISUID | RTFS_UNIX_ISGID | RTFS_UNIX_ISTXT);
1553 }
1554#endif
1555
1556 rc = RTFileSetMode(pHandle->file.Handle, fMode);
1557 if (rc != VINF_SUCCESS)
1558 {
1559 Log(("RTFileSetMode %x failed with %Rrc\n", fMode, rc));
1560 /* silent failure, because this tends to fail with e.g. windows guest & linux host */
1561 rc = VINF_SUCCESS;
1562 }
1563 }
1564 }
1565 }
1566 /** @todo mode for directories */
1567
1568 if (rc == VINF_SUCCESS)
1569 {
1570 uint32_t bufsize = sizeof(*pSFDEntry);
1571
1572 rc = vbsfQueryFileInfo(pClient, root, Handle, SHFL_INFO_GET|SHFL_INFO_FILE, &bufsize, (uint8_t *)pSFDEntry);
1573 if (rc == VINF_SUCCESS)
1574 {
1575 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1576 }
1577 else
1578 AssertFailed();
1579 }
1580
1581 return rc;
1582}
1583
1584
1585static int vbsfSetEndOfFile(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
1586 uint32_t *pcbBuffer, uint8_t *pBuffer)
1587{
1588 RT_NOREF1(flags);
1589 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1590 SHFLFSOBJINFO *pSFDEntry;
1591
1592 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
1593 {
1594 AssertFailed();
1595 return VERR_INVALID_PARAMETER;
1596 }
1597
1598 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1599 if (RT_SUCCESS(rc))
1600 { /* likely */ }
1601 else
1602 return rc;
1603
1604 *pcbBuffer = 0;
1605 pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
1606
1607 if (flags & SHFL_INFO_SIZE)
1608 {
1609 rc = RTFileSetSize(pHandle->file.Handle, pSFDEntry->cbObject);
1610 if (rc != VINF_SUCCESS)
1611 AssertFailed();
1612 }
1613 else
1614 AssertFailed();
1615
1616 if (rc == VINF_SUCCESS)
1617 {
1618 RTFSOBJINFO fileinfo;
1619
1620 /* Query the new object info and return it */
1621 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1622 if (rc == VINF_SUCCESS)
1623 {
1624#ifdef RT_OS_WINDOWS
1625 fileinfo.Attr.fMode |= 0111;
1626#endif
1627 vbfsCopyFsObjInfoFromIprt(pSFDEntry, &fileinfo);
1628 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1629 }
1630 else
1631 AssertFailed();
1632 }
1633
1634 return rc;
1635}
1636
1637int vbsfQueryVolumeInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1638{
1639 RT_NOREF2(root, flags);
1640 int rc = VINF_SUCCESS;
1641 SHFLVOLINFO *pSFDEntry;
1642 char *pszFullPath = NULL;
1643 union
1644 {
1645 SHFLSTRING Dummy;
1646 uint8_t abDummy[SHFLSTRING_HEADER_SIZE + sizeof(RTUTF16)];
1647 } Buf;
1648
1649 if (pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLVOLINFO))
1650 {
1651 AssertFailed();
1652 return VERR_INVALID_PARAMETER;
1653 }
1654
1655 /** @todo other options */
1656 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_VOLUME));
1657
1658 *pcbBuffer = 0;
1659 pSFDEntry = (PSHFLVOLINFO)pBuffer;
1660
1661 ShflStringInitBuffer(&Buf.Dummy, sizeof(Buf));
1662 Buf.Dummy.String.ucs2[0] = '\0';
1663 rc = vbsfBuildFullPath(pClient, root, &Buf.Dummy, sizeof(Buf), &pszFullPath, NULL);
1664
1665 if (RT_SUCCESS(rc))
1666 {
1667 rc = RTFsQuerySizes(pszFullPath, &pSFDEntry->ullTotalAllocationBytes, &pSFDEntry->ullAvailableAllocationBytes, &pSFDEntry->ulBytesPerAllocationUnit, &pSFDEntry->ulBytesPerSector);
1668 if (rc != VINF_SUCCESS)
1669 goto exit;
1670
1671 rc = RTFsQuerySerial(pszFullPath, &pSFDEntry->ulSerial);
1672 if (rc != VINF_SUCCESS)
1673 goto exit;
1674
1675 RTFSPROPERTIES FsProperties;
1676 rc = RTFsQueryProperties(pszFullPath, &FsProperties);
1677 if (rc != VINF_SUCCESS)
1678 goto exit;
1679 vbfsCopyFsPropertiesFromIprt(&pSFDEntry->fsProperties, &FsProperties);
1680
1681 *pcbBuffer = sizeof(SHFLVOLINFO);
1682 }
1683 else AssertFailed();
1684
1685exit:
1686 AssertMsg(rc == VINF_SUCCESS, ("failure: rc = %Rrc\n", rc));
1687 /* free the path string */
1688 vbsfFreeFullPath(pszFullPath);
1689 return rc;
1690}
1691
1692int vbsfQueryFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1693{
1694 if (pcbBuffer == 0 || pBuffer == 0)
1695 {
1696 AssertFailed();
1697 return VERR_INVALID_PARAMETER;
1698 }
1699
1700 if (flags & SHFL_INFO_FILE)
1701 return vbsfQueryFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1702
1703 if (flags & SHFL_INFO_VOLUME)
1704 return vbsfQueryVolumeInfo(pClient, root, flags, pcbBuffer, pBuffer);
1705
1706 AssertFailed();
1707 return VERR_INVALID_PARAMETER;
1708}
1709
1710#ifdef UNITTEST
1711/** Unit test the SHFL_FN_INFORMATION API. Located here as a form of API
1712 * documentation. */
1713void testFSInfo(RTTEST hTest)
1714{
1715 /* If the number or types of parameters are wrong the API should fail. */
1716 testFSInfoBadParameters(hTest);
1717 /* Basic get and set file size test. */
1718 testFSInfoQuerySetFMode(hTest);
1719 /* Basic get and set dir atime test. */
1720 testFSInfoQuerySetDirATime(hTest);
1721 /* Basic get and set file atime test. */
1722 testFSInfoQuerySetFileATime(hTest);
1723 /* Basic set end of file. */
1724 testFSInfoQuerySetEndOfFile(hTest);
1725 /* Add tests as required... */
1726}
1727#endif
1728int vbsfSetFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1729{
1730 uint32_t type = vbsfQueryHandleType(pClient, Handle)
1731 & (SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE|SHFL_HF_TYPE_VOLUME);
1732
1733 if (type == 0 || pcbBuffer == 0 || pBuffer == 0)
1734 {
1735 AssertFailed();
1736 return VERR_INVALID_PARAMETER;
1737 }
1738
1739 if (flags & SHFL_INFO_FILE)
1740 return vbsfSetFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1741
1742 if (flags & SHFL_INFO_SIZE)
1743 return vbsfSetEndOfFile(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1744
1745// if (flags & SHFL_INFO_VOLUME)
1746// return vbsfVolumeInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1747 AssertFailed();
1748 return VERR_INVALID_PARAMETER;
1749}
1750
1751#ifdef UNITTEST
1752/** Unit test the SHFL_FN_LOCK API. Located here as a form of API
1753 * documentation. */
1754void testLock(RTTEST hTest)
1755{
1756 /* If the number or types of parameters are wrong the API should fail. */
1757 testLockBadParameters(hTest);
1758 /* Simple file locking and unlocking test. */
1759 testLockFileSimple(hTest);
1760 /* Add tests as required... */
1761}
1762#endif
1763
1764int vbsfLock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
1765{
1766 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1767 uint32_t fRTLock = 0;
1768
1769 Assert((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL);
1770
1771 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1772 if (RT_SUCCESS(rc))
1773 { /* likely */ }
1774 else
1775 return rc;
1776
1777 if ( ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL)
1778 || (flags & SHFL_LOCK_ENTIRE)
1779 )
1780 {
1781 AssertFailed();
1782 return VERR_INVALID_PARAMETER;
1783 }
1784
1785 /* Lock type */
1786 switch(flags & SHFL_LOCK_MODE_MASK)
1787 {
1788 case SHFL_LOCK_SHARED:
1789 fRTLock = RTFILE_LOCK_READ;
1790 break;
1791
1792 case SHFL_LOCK_EXCLUSIVE:
1793 fRTLock = RTFILE_LOCK_READ | RTFILE_LOCK_WRITE;
1794 break;
1795
1796 default:
1797 AssertFailed();
1798 return VERR_INVALID_PARAMETER;
1799 }
1800
1801 /* Lock wait type */
1802 if (flags & SHFL_LOCK_WAIT)
1803 fRTLock |= RTFILE_LOCK_WAIT;
1804 else
1805 fRTLock |= RTFILE_LOCK_IMMEDIATELY;
1806
1807#ifdef RT_OS_WINDOWS
1808 rc = RTFileLock(pHandle->file.Handle, fRTLock, offset, length);
1809 if (rc != VINF_SUCCESS)
1810 Log(("RTFileLock %RTfile %RX64 %RX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
1811#else
1812 Log(("vbsfLock: Pretend success handle=%x\n", Handle));
1813 rc = VINF_SUCCESS;
1814 RT_NOREF2(offset, length);
1815#endif
1816 return rc;
1817}
1818
1819int vbsfUnlock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
1820{
1821 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1822
1823 Assert((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL);
1824
1825 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1826 if (RT_SUCCESS(rc))
1827 { /* likely */ }
1828 else
1829 return rc;
1830
1831 if ( ((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL)
1832 || (flags & SHFL_LOCK_ENTIRE)
1833 )
1834 {
1835 return VERR_INVALID_PARAMETER;
1836 }
1837
1838#ifdef RT_OS_WINDOWS
1839 rc = RTFileUnlock(pHandle->file.Handle, offset, length);
1840 if (rc != VINF_SUCCESS)
1841 Log(("RTFileUnlock %RTfile %RX64 %RTX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
1842#else
1843 Log(("vbsfUnlock: Pretend success handle=%x\n", Handle));
1844 rc = VINF_SUCCESS;
1845 RT_NOREF2(offset, length);
1846#endif
1847
1848 return rc;
1849}
1850
1851
1852#ifdef UNITTEST
1853/** Unit test the SHFL_FN_REMOVE API. Located here as a form of API
1854 * documentation. */
1855void testRemove(RTTEST hTest)
1856{
1857 /* If the number or types of parameters are wrong the API should fail. */
1858 testRemoveBadParameters(hTest);
1859 /* Add tests as required... */
1860}
1861#endif
1862int vbsfRemove(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint32_t flags)
1863{
1864 int rc = VINF_SUCCESS;
1865
1866 /* Validate input */
1867 if ( flags & ~(SHFL_REMOVE_FILE|SHFL_REMOVE_DIR|SHFL_REMOVE_SYMLINK)
1868 || cbPath == 0
1869 || pPath == 0)
1870 {
1871 AssertFailed();
1872 return VERR_INVALID_PARAMETER;
1873 }
1874
1875 /* Build a host full path for the given path
1876 * and convert ucs2 to utf8 if necessary.
1877 */
1878 char *pszFullPath = NULL;
1879
1880 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, NULL);
1881 if (RT_SUCCESS(rc))
1882 {
1883 /* is the guest allowed to write to this share? */
1884 bool fWritable;
1885 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
1886 if (RT_FAILURE(rc) || !fWritable)
1887 rc = VERR_WRITE_PROTECT;
1888
1889 if (RT_SUCCESS(rc))
1890 {
1891 if (flags & SHFL_REMOVE_SYMLINK)
1892 rc = RTSymlinkDelete(pszFullPath, 0);
1893 else if (flags & SHFL_REMOVE_FILE)
1894 rc = RTFileDelete(pszFullPath);
1895 else
1896 rc = RTDirRemove(pszFullPath);
1897 }
1898
1899#ifndef DEBUG_dmik
1900 // VERR_ACCESS_DENIED for example?
1901 // Assert(rc == VINF_SUCCESS || rc == VERR_DIR_NOT_EMPTY);
1902#endif
1903 /* free the path string */
1904 vbsfFreeFullPath(pszFullPath);
1905 }
1906 return rc;
1907}
1908
1909
1910#ifdef UNITTEST
1911/** Unit test the SHFL_FN_RENAME API. Located here as a form of API
1912 * documentation. */
1913void testRename(RTTEST hTest)
1914{
1915 /* If the number or types of parameters are wrong the API should fail. */
1916 testRenameBadParameters(hTest);
1917 /* Add tests as required... */
1918}
1919#endif
1920int vbsfRename(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pSrc, SHFLSTRING *pDest, uint32_t flags)
1921{
1922 int rc = VINF_SUCCESS;
1923
1924 /* Validate input */
1925 if ( flags & ~(SHFL_RENAME_FILE|SHFL_RENAME_DIR|SHFL_RENAME_REPLACE_IF_EXISTS)
1926 || pSrc == 0
1927 || pDest == 0)
1928 {
1929 AssertFailed();
1930 return VERR_INVALID_PARAMETER;
1931 }
1932
1933 /* Build a host full path for the given path
1934 * and convert ucs2 to utf8 if necessary.
1935 */
1936 char *pszFullPathSrc = NULL;
1937 char *pszFullPathDest = NULL;
1938
1939 rc = vbsfBuildFullPath(pClient, root, pSrc, pSrc->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPathSrc, NULL);
1940 if (rc != VINF_SUCCESS)
1941 return rc;
1942
1943 rc = vbsfBuildFullPath(pClient, root, pDest, pDest->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPathDest, NULL, false, true);
1944 if (RT_SUCCESS (rc))
1945 {
1946 Log(("Rename %s to %s\n", pszFullPathSrc, pszFullPathDest));
1947
1948 /* is the guest allowed to write to this share? */
1949 bool fWritable;
1950 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
1951 if (RT_FAILURE(rc) || !fWritable)
1952 rc = VERR_WRITE_PROTECT;
1953
1954 if (RT_SUCCESS(rc))
1955 {
1956 if ((flags & (SHFL_RENAME_FILE | SHFL_RENAME_DIR)) == (SHFL_RENAME_FILE | SHFL_RENAME_DIR))
1957 {
1958 rc = RTPathRename(pszFullPathSrc, pszFullPathDest,
1959 flags & SHFL_RENAME_REPLACE_IF_EXISTS ? RTPATHRENAME_FLAGS_REPLACE : 0);
1960 }
1961 else if (flags & SHFL_RENAME_FILE)
1962 {
1963 rc = RTFileMove(pszFullPathSrc, pszFullPathDest,
1964 ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTFILEMOVE_FLAGS_REPLACE : 0));
1965 }
1966 else
1967 {
1968 /* NT ignores the REPLACE flag and simply return and already exists error. */
1969 rc = RTDirRename(pszFullPathSrc, pszFullPathDest,
1970 ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTPATHRENAME_FLAGS_REPLACE : 0));
1971 }
1972 }
1973
1974 /* free the path string */
1975 vbsfFreeFullPath(pszFullPathDest);
1976 }
1977 /* free the path string */
1978 vbsfFreeFullPath(pszFullPathSrc);
1979 return rc;
1980}
1981
1982#ifdef UNITTEST
1983/** Unit test the SHFL_FN_SYMLINK API. Located here as a form of API
1984 * documentation. */
1985void testSymlink(RTTEST hTest)
1986{
1987 /* If the number or types of parameters are wrong the API should fail. */
1988 testSymlinkBadParameters(hTest);
1989 /* Add tests as required... */
1990}
1991#endif
1992int vbsfSymlink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pNewPath, SHFLSTRING *pOldPath, SHFLFSOBJINFO *pInfo)
1993{
1994 int rc = VINF_SUCCESS;
1995
1996 char *pszFullNewPath = NULL;
1997 char *pszFullOldPath = NULL;
1998
1999 /* XXX: no support for UCS2 at the moment. */
2000 if (!BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
2001 return VERR_NOT_IMPLEMENTED;
2002
2003 bool fSymlinksCreate;
2004 rc = vbsfMappingsQuerySymlinksCreate(pClient, root, &fSymlinksCreate);
2005 AssertRCReturn(rc, rc);
2006 if (!fSymlinksCreate)
2007 return VERR_WRITE_PROTECT; /* XXX or VERR_TOO_MANY_SYMLINKS? */
2008
2009 rc = vbsfBuildFullPath(pClient, root, pNewPath, pNewPath->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullNewPath, NULL);
2010 AssertRCReturn(rc, rc);
2011
2012 /* Verify that the link target can be a valid host path, i.e. does not contain invalid characters. */
2013 uint32_t fu32PathFlags = 0;
2014 uint32_t fu32Options = 0;
2015 rc = vbsfPathGuestToHost(pClient, root, pOldPath, pOldPath->u16Size + SHFLSTRING_HEADER_SIZE,
2016 &pszFullOldPath, NULL, fu32Options, &fu32PathFlags);
2017 if (RT_FAILURE(rc))
2018 {
2019 vbsfFreeFullPath(pszFullNewPath);
2020 return rc;
2021 }
2022
2023 rc = RTSymlinkCreate(pszFullNewPath, (const char *)pOldPath->String.utf8,
2024 RTSYMLINKTYPE_UNKNOWN, 0);
2025 if (RT_SUCCESS(rc))
2026 {
2027 RTFSOBJINFO info;
2028 rc = RTPathQueryInfoEx(pszFullNewPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
2029 if (RT_SUCCESS(rc))
2030 vbfsCopyFsObjInfoFromIprt(pInfo, &info);
2031 }
2032
2033 vbsfFreeFullPath(pszFullOldPath);
2034 vbsfFreeFullPath(pszFullNewPath);
2035
2036 return rc;
2037}
2038
2039/*
2040 * Clean up our mess by freeing all handles that are still valid.
2041 *
2042 */
2043int vbsfDisconnect(SHFLCLIENTDATA *pClient)
2044{
2045 for (int i = 0; i < SHFLHANDLE_MAX; ++i)
2046 {
2047 SHFLFILEHANDLE *pHandle = NULL;
2048 SHFLHANDLE Handle = (SHFLHANDLE)i;
2049
2050 uint32_t type = vbsfQueryHandleType(pClient, Handle);
2051 switch (type & (SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE))
2052 {
2053 case SHFL_HF_TYPE_DIR:
2054 {
2055 pHandle = vbsfQueryDirHandle(pClient, Handle);
2056 break;
2057 }
2058 case SHFL_HF_TYPE_FILE:
2059 {
2060 pHandle = vbsfQueryFileHandle(pClient, Handle);
2061 break;
2062 }
2063 default:
2064 break;
2065 }
2066
2067 if (pHandle)
2068 {
2069 LogFunc(("Opened handle 0x%08x\n", i));
2070 vbsfClose(pClient, pHandle->root, Handle);
2071 }
2072 }
2073 return VINF_SUCCESS;
2074}
Note: See TracBrowser for help on using the repository browser.

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