VirtualBox

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

Last change on this file since 66012 was 65999, checked in by vboxsync, 8 years ago

AssertReturn on each condition is much more helpful when debugging.

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