VirtualBox

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

Last change on this file since 71930 was 70726, checked in by vboxsync, 7 years ago

Do not allow SUID, SGID or sticky bits on shared folders.
bugref:9047: Clean up Linux guest vboxuser device
Shared folders are primarily intended as a convenient method of file transfer
between guest and host, but not for more complicated things. Therefore the
SUID, SGID or sticky bits do not make sense there, and this change removes
them so that people do not get wrong ideas.
Not strictly related to the vboxuser device, but same general idea.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 66.4 KB
Line 
1/* $Id: vbsf.cpp 70726 2018-01-24 13:58:10Z 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#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 /*fFlags*/);
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 testCreateFileSimpleCaseInsensitive(hTest);
815 /* Simple opening of an existing directory. */
816 /** @todo How do wildcards in the path name work? */
817 testCreateDirSimple(hTest);
818 /* If the number or types of parameters are wrong the API should fail. */
819 testCreateBadParameters(hTest);
820 /* Add tests as required... */
821}
822#endif
823
824/**
825 * Create or open a file or folder. Perform character set and case
826 * conversion on the file name if necessary.
827 *
828 * @returns IPRT status code, but see note below
829 * @param pClient Data structure describing the client accessing the shared
830 * folder
831 * @param root The index of the shared folder in the table of mappings.
832 * The host path of the shared folder is found using this.
833 * @param pPath The path of the file or folder relative to the host path
834 * indexed by root.
835 * @param cbPath Presumably the length of the path in pPath. Actually
836 * ignored, as pPath contains a length parameter.
837 * @param pParms @a Info If a new file is created or an old one overwritten, set
838 * these attributes
839 * @retval pParms @a Result Shared folder result code, see include/VBox/shflsvc.h
840 * @retval pParms @a Handle Shared folder handle to the newly opened file
841 * @retval pParms @a Info Attributes of the file or folder opened
842 *
843 * @note This function returns success if a "non-exceptional" error occurred,
844 * such as "no such file". In this case, the caller should check the
845 * pParms->Result return value and whether pParms->Handle is valid.
846 */
847int vbsfCreate(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, SHFLCREATEPARMS *pParms)
848{
849 int rc = VINF_SUCCESS;
850
851 LogFlow(("vbsfCreate: pClient = %p, pPath = %p, cbPath = %d, pParms = %p CreateFlags=%x\n",
852 pClient, pPath, cbPath, pParms, pParms->CreateFlags));
853
854 /* Check the client access rights to the root. */
855 /** @todo */
856
857 /* Build a host full path for the given path, handle file name case issues (if the guest
858 * expects case-insensitive paths but the host is case-sensitive) and convert ucs2 to utf8 if
859 * necessary.
860 */
861 char *pszFullPath = NULL;
862 uint32_t cbFullPathRoot = 0;
863
864 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot);
865 if (RT_SUCCESS(rc))
866 {
867 /* Reset return value in case client forgot to do so.
868 * pParms->Handle must not be reset here, as it is used
869 * in vbsfOpenFile to detect old additions.
870 */
871 pParms->Result = SHFL_NO_RESULT;
872
873 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_LOOKUP))
874 {
875 rc = vbsfLookupFile(pClient, pszFullPath, pParms);
876 }
877 else
878 {
879 /* Query path information. */
880 RTFSOBJINFO info;
881
882 rc = RTPathQueryInfoEx(pszFullPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
883 LogFlow(("RTPathQueryInfoEx returned %Rrc\n", rc));
884
885 if (RT_SUCCESS(rc))
886 {
887 /* Mark it as a directory in case the caller didn't. */
888 /**
889 * @todo I left this in in order not to change the behaviour of the
890 * function too much. Is it really needed, and should it really be
891 * here?
892 */
893 if (BIT_FLAG(info.Attr.fMode, RTFS_DOS_DIRECTORY))
894 {
895 pParms->CreateFlags |= SHFL_CF_DIRECTORY;
896 }
897
898 /**
899 * @todo This should be in the Windows Guest Additions, as no-one else
900 * needs it.
901 */
902 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_OPEN_TARGET_DIRECTORY))
903 {
904 vbsfStripLastComponent(pszFullPath, cbFullPathRoot);
905 pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_EXISTS;
906 pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_NEW;
907 pParms->CreateFlags |= SHFL_CF_DIRECTORY;
908 pParms->CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS;
909 pParms->CreateFlags |= SHFL_CF_ACT_FAIL_IF_NEW;
910 }
911 }
912
913 rc = VINF_SUCCESS;
914
915 /* Note: do not check the SHFL_CF_ACCESS_WRITE here, only check if the open operation
916 * will cause changes.
917 *
918 * Actual operations (write, set attr, etc), which can write to a shared folder, have
919 * the check and will return VERR_WRITE_PROTECT if the folder is not writable.
920 */
921 if ( (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_REPLACE_IF_EXISTS
922 || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_OVERWRITE_IF_EXISTS
923 || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_NEW) == SHFL_CF_ACT_CREATE_IF_NEW
924 )
925 {
926 /* is the guest allowed to write to this share? */
927 bool fWritable;
928 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
929 if (RT_FAILURE(rc) || !fWritable)
930 rc = VERR_WRITE_PROTECT;
931 }
932
933 if (RT_SUCCESS(rc))
934 {
935 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_DIRECTORY))
936 {
937 rc = vbsfOpenDir(pClient, root, pszFullPath, pParms);
938 }
939 else
940 {
941 rc = vbsfOpenFile(pClient, root, pszFullPath, pParms);
942 }
943 }
944 else
945 {
946 pParms->Handle = SHFL_HANDLE_NIL;
947 }
948 }
949
950 /* free the path string */
951 vbsfFreeFullPath(pszFullPath);
952 }
953
954 Log(("vbsfCreate: handle = %RX64 rc = %Rrc result=%x\n", (uint64_t)pParms->Handle, rc, pParms->Result));
955
956 return rc;
957}
958
959#ifdef UNITTEST
960/** Unit test the SHFL_FN_CLOSE API. Located here as a form of API
961 * documentation. */
962void testClose(RTTEST hTest)
963{
964 /* If the API parameters are invalid the API should fail. */
965 testCloseBadParameters(hTest);
966 /* Add tests as required... */
967}
968#endif
969
970int vbsfClose(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
971{
972 LogFunc(("pClient = %p, root 0x%RX32, Handle = 0x%RX64\n",
973 pClient, root, Handle));
974
975 int rc = VERR_INVALID_HANDLE;
976 uint32_t type = vbsfQueryHandleType(pClient, Handle);
977 Assert((type & ~(SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE)) == 0);
978 switch (type & (SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE))
979 {
980 case SHFL_HF_TYPE_DIR:
981 {
982 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
983 if (RT_LIKELY(pHandle && root == pHandle->root))
984 {
985 rc = vbsfCloseDir(pHandle);
986 vbsfFreeFileHandle(pClient, Handle);
987 }
988 break;
989 }
990 case SHFL_HF_TYPE_FILE:
991 {
992 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
993 if (RT_LIKELY(pHandle && root == pHandle->root))
994 {
995 rc = vbsfCloseFile(pHandle);
996 vbsfFreeFileHandle(pClient, Handle);
997 }
998 break;
999 }
1000 default:
1001 break;
1002 }
1003
1004 LogFunc(("rc = %Rrc\n", rc));
1005 return rc;
1006}
1007
1008#ifdef UNITTEST
1009/** Unit test the SHFL_FN_READ API. Located here as a form of API
1010 * documentation. */
1011void testRead(RTTEST hTest)
1012{
1013 /* If the number or types of parameters are wrong the API should fail. */
1014 testReadBadParameters(hTest);
1015 /* Basic reading from a file. */
1016 testReadFileSimple(hTest);
1017 /* Add tests as required... */
1018}
1019#endif
1020int vbsfRead(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer)
1021{
1022 LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64, offset 0x%RX64, bytes 0x%RX32\n",
1023 pClient, root, Handle, offset, pcbBuffer? *pcbBuffer: 0));
1024
1025 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1026
1027 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1028 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1029 if (RT_SUCCESS(rc))
1030 { /* likely */ }
1031 else
1032 return rc;
1033
1034 if (RT_LIKELY(*pcbBuffer != 0))
1035 {
1036 rc = RTFileSeek(pHandle->file.Handle, offset, RTFILE_SEEK_BEGIN, NULL);
1037 if (RT_SUCCESS(rc))
1038 {
1039 size_t count = 0;
1040 rc = RTFileRead(pHandle->file.Handle, pBuffer, *pcbBuffer, &count);
1041 *pcbBuffer = (uint32_t)count;
1042 }
1043 else
1044 AssertRC(rc);
1045 }
1046 else
1047 {
1048 /* Reading zero bytes always succeeds. */
1049 rc = VINF_SUCCESS;
1050 }
1051
1052 LogFunc(("%Rrc bytes read 0x%RX32\n", rc, *pcbBuffer));
1053 return rc;
1054}
1055
1056#ifdef UNITTEST
1057/** Unit test the SHFL_FN_WRITE API. Located here as a form of API
1058 * documentation. */
1059void testWrite(RTTEST hTest)
1060{
1061 /* If the number or types of parameters are wrong the API should fail. */
1062 testWriteBadParameters(hTest);
1063 /* Simple test of writing to a file. */
1064 testWriteFileSimple(hTest);
1065 /* Add tests as required... */
1066}
1067#endif
1068int vbsfWrite(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer)
1069{
1070 LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64, offset 0x%RX64, bytes 0x%RX32\n",
1071 pClient, root, Handle, offset, pcbBuffer? *pcbBuffer: 0));
1072
1073 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1074
1075 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1076 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1077 if (RT_SUCCESS(rc))
1078 { /* likely */ }
1079 else
1080 return rc;
1081
1082 if (RT_LIKELY(*pcbBuffer != 0))
1083 {
1084 rc = RTFileSeek(pHandle->file.Handle, offset, RTFILE_SEEK_BEGIN, NULL);
1085 if (RT_SUCCESS(rc))
1086 {
1087 size_t count = 0;
1088 rc = RTFileWrite(pHandle->file.Handle, pBuffer, *pcbBuffer, &count);
1089 *pcbBuffer = (uint32_t)count;
1090 }
1091 else
1092 AssertRC(rc);
1093 }
1094 else
1095 {
1096 /** @todo What writing zero bytes should do? */
1097 rc = VINF_SUCCESS;
1098 }
1099
1100 LogFunc(("%Rrc bytes written 0x%RX32\n", rc, *pcbBuffer));
1101 return rc;
1102}
1103
1104
1105#ifdef UNITTEST
1106/** Unit test the SHFL_FN_FLUSH API. Located here as a form of API
1107 * documentation. */
1108void testFlush(RTTEST hTest)
1109{
1110 /* If the number or types of parameters are wrong the API should fail. */
1111 testFlushBadParameters(hTest);
1112 /* Simple opening and flushing of a file. */
1113 testFlushFileSimple(hTest);
1114 /* Add tests as required... */
1115}
1116#endif
1117
1118int vbsfFlush(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
1119{
1120 LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64\n",
1121 pClient, root, Handle));
1122
1123 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1124
1125 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1126 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1127 if (RT_SUCCESS(rc))
1128 { /* likely */ }
1129 else
1130 return rc;
1131
1132 rc = RTFileFlush(pHandle->file.Handle);
1133
1134 LogFunc(("%Rrc\n", rc));
1135 return rc;
1136}
1137
1138#ifdef UNITTEST
1139/** Unit test the SHFL_FN_LIST API. Located here as a form of API
1140 * documentation. */
1141void testDirList(RTTEST hTest)
1142{
1143 /* If the number or types of parameters are wrong the API should fail. */
1144 testDirListBadParameters(hTest);
1145 /* Test listing an empty directory (simple edge case). */
1146 testDirListEmpty(hTest);
1147 /* Add tests as required... */
1148}
1149#endif
1150int vbsfDirList(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, SHFLSTRING *pPath, uint32_t flags,
1151 uint32_t *pcbBuffer, uint8_t *pBuffer, uint32_t *pIndex, uint32_t *pcFiles)
1152{
1153 PRTDIRENTRYEX pDirEntry = 0, pDirEntryOrg;
1154 uint32_t cbDirEntry, cbBufferOrg;
1155 PSHFLDIRINFO pSFDEntry;
1156 PRTUTF16 pwszString;
1157 RTDIR hDir;
1158 const bool fUtf8 = BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8) != 0;
1159
1160 AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
1161
1162 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
1163 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1164 if (RT_SUCCESS(rc))
1165 { /* likely */ }
1166 else
1167 return rc;
1168
1169 Assert(*pIndex == 0);
1170 hDir = pHandle->dir.Handle;
1171
1172 cbDirEntry = 4096;
1173 pDirEntryOrg = pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry);
1174 if (pDirEntry == 0)
1175 {
1176 AssertFailed();
1177 return VERR_NO_MEMORY;
1178 }
1179
1180 cbBufferOrg = *pcbBuffer;
1181 *pcbBuffer = 0;
1182 pSFDEntry = (PSHFLDIRINFO)pBuffer;
1183
1184 *pIndex = 1; /* not yet complete */
1185 *pcFiles = 0;
1186
1187 if (pPath)
1188 {
1189 if (pHandle->dir.SearchHandle == 0)
1190 {
1191 /* Build a host full path for the given path
1192 * and convert ucs2 to utf8 if necessary.
1193 */
1194 char *pszFullPath = NULL;
1195
1196 Assert(pHandle->dir.pLastValidEntry == 0);
1197
1198 rc = vbsfBuildFullPath(pClient, root, pPath, pPath->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPath, NULL, true);
1199
1200 if (RT_SUCCESS(rc))
1201 {
1202 rc = RTDirOpenFiltered(&pHandle->dir.SearchHandle, pszFullPath, RTDIRFILTER_WINNT, 0 /*fFlags*/);
1203
1204 /* free the path string */
1205 vbsfFreeFullPath(pszFullPath);
1206
1207 if (RT_FAILURE(rc))
1208 goto end;
1209 }
1210 else
1211 goto end;
1212 }
1213 Assert(pHandle->dir.SearchHandle);
1214 hDir = pHandle->dir.SearchHandle;
1215 }
1216
1217 while (cbBufferOrg)
1218 {
1219 size_t cbDirEntrySize = cbDirEntry;
1220 uint32_t cbNeeded;
1221
1222 /* Do we still have a valid last entry for the active search? If so, then return it here */
1223 if (pHandle->dir.pLastValidEntry)
1224 {
1225 pDirEntry = pHandle->dir.pLastValidEntry;
1226 }
1227 else
1228 {
1229 pDirEntry = pDirEntryOrg;
1230
1231 rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
1232 if (rc == VERR_NO_MORE_FILES)
1233 {
1234 *pIndex = 0; /* listing completed */
1235 break;
1236 }
1237
1238 if ( rc != VINF_SUCCESS
1239 && rc != VWRN_NO_DIRENT_INFO)
1240 {
1241 //AssertFailed();
1242 if ( rc == VERR_NO_TRANSLATION
1243 || rc == VERR_INVALID_UTF8_ENCODING)
1244 continue;
1245 break;
1246 }
1247 }
1248
1249 cbNeeded = RT_OFFSETOF(SHFLDIRINFO, name.String);
1250 if (fUtf8)
1251 cbNeeded += pDirEntry->cbName + 1;
1252 else
1253 /* Overestimating, but that's ok */
1254 cbNeeded += (pDirEntry->cbName + 1) * 2;
1255
1256 if (cbBufferOrg < cbNeeded)
1257 {
1258 /* No room, so save this directory entry, or else it's lost forever */
1259 pHandle->dir.pLastValidEntry = pDirEntry;
1260
1261 if (*pcFiles == 0)
1262 {
1263 AssertFailed();
1264 return VINF_BUFFER_OVERFLOW; /* Return directly and don't free pDirEntry */
1265 }
1266 return VINF_SUCCESS; /* Return directly and don't free pDirEntry */
1267 }
1268
1269#ifdef RT_OS_WINDOWS
1270 pDirEntry->Info.Attr.fMode |= 0111;
1271#endif
1272 vbfsCopyFsObjInfoFromIprt(&pSFDEntry->Info, &pDirEntry->Info);
1273 pSFDEntry->cucShortName = 0;
1274
1275 if (fUtf8)
1276 {
1277 void *src, *dst;
1278
1279 src = &pDirEntry->szName[0];
1280 dst = &pSFDEntry->name.String.utf8[0];
1281
1282 memcpy(dst, src, pDirEntry->cbName + 1);
1283
1284 pSFDEntry->name.u16Size = pDirEntry->cbName + 1;
1285 pSFDEntry->name.u16Length = pDirEntry->cbName;
1286 }
1287 else
1288 {
1289 pSFDEntry->name.String.ucs2[0] = 0;
1290 pwszString = pSFDEntry->name.String.ucs2;
1291 int rc2 = RTStrToUtf16Ex(pDirEntry->szName, RTSTR_MAX, &pwszString, pDirEntry->cbName+1, NULL);
1292 AssertRC(rc2);
1293
1294#ifdef RT_OS_DARWIN
1295/** @todo This belongs in rtPathToNative or in the windows shared folder file system driver...
1296 * The question is simply whether the NFD normalization is actually applied on a (virtual) file
1297 * system level in darwin, or just by the user mode application libs. */
1298 {
1299 // Convert to
1300 // Normalization Form C (composed Unicode). We need this because
1301 // Mac OS X file system uses NFD (Normalization Form D :decomposed Unicode)
1302 // while most other OS', server-side programs usually expect NFC.
1303 uint16_t ucs2Length;
1304 CFRange rangeCharacters;
1305 CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, 0);
1306
1307 ::CFStringAppendCharacters(inStr, (UniChar *)pwszString, RTUtf16Len(pwszString));
1308 ::CFStringNormalize(inStr, kCFStringNormalizationFormC);
1309 ucs2Length = ::CFStringGetLength(inStr);
1310
1311 rangeCharacters.location = 0;
1312 rangeCharacters.length = ucs2Length;
1313 ::CFStringGetCharacters(inStr, rangeCharacters, pwszString);
1314 pwszString[ucs2Length] = 0x0000; // NULL terminated
1315
1316 CFRelease(inStr);
1317 }
1318#endif
1319 pSFDEntry->name.u16Length = (uint32_t)RTUtf16Len(pSFDEntry->name.String.ucs2) * 2;
1320 pSFDEntry->name.u16Size = pSFDEntry->name.u16Length + 2;
1321
1322 Log(("SHFL: File name size %d\n", pSFDEntry->name.u16Size));
1323 Log(("SHFL: File name %ls\n", &pSFDEntry->name.String.ucs2));
1324
1325 // adjust cbNeeded (it was overestimated before)
1326 cbNeeded = RT_OFFSETOF(SHFLDIRINFO, name.String) + pSFDEntry->name.u16Size;
1327 }
1328
1329 pSFDEntry = (PSHFLDIRINFO)((uintptr_t)pSFDEntry + cbNeeded);
1330 *pcbBuffer += cbNeeded;
1331 cbBufferOrg-= cbNeeded;
1332
1333 *pcFiles += 1;
1334
1335 /* Free the saved last entry, that we've just returned */
1336 if (pHandle->dir.pLastValidEntry)
1337 {
1338 RTMemFree(pHandle->dir.pLastValidEntry);
1339 pHandle->dir.pLastValidEntry = NULL;
1340
1341 /* And use the newly allocated buffer from now. */
1342 pDirEntry = pDirEntryOrg;
1343 }
1344
1345 if (flags & SHFL_LIST_RETURN_ONE)
1346 break; /* we're done */
1347 }
1348 Assert(rc != VINF_SUCCESS || *pcbBuffer > 0);
1349
1350end:
1351 if (pDirEntry)
1352 RTMemFree(pDirEntry);
1353
1354 return rc;
1355}
1356
1357#ifdef UNITTEST
1358/** Unit test the SHFL_FN_READLINK API. Located here as a form of API
1359 * documentation. */
1360void testReadLink(RTTEST hTest)
1361{
1362 /* If the number or types of parameters are wrong the API should fail. */
1363 testReadLinkBadParameters(hTest);
1364 /* Add tests as required... */
1365}
1366#endif
1367int vbsfReadLink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint8_t *pBuffer, uint32_t cbBuffer)
1368{
1369 int rc = VINF_SUCCESS;
1370
1371 if (pPath == 0 || pBuffer == 0)
1372 {
1373 AssertFailed();
1374 return VERR_INVALID_PARAMETER;
1375 }
1376
1377 /* Build a host full path for the given path, handle file name case issues
1378 * (if the guest expects case-insensitive paths but the host is
1379 * case-sensitive) and convert ucs2 to utf8 if necessary.
1380 */
1381 char *pszFullPath = NULL;
1382 uint32_t cbFullPathRoot = 0;
1383
1384 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot);
1385
1386 if (RT_SUCCESS(rc))
1387 {
1388 rc = RTSymlinkRead(pszFullPath, (char *) pBuffer, cbBuffer, 0);
1389 if (RT_SUCCESS(rc))
1390 {
1391 /* Convert the slashes in the link target to the guest path separator characters. */
1392 char *psz = (char *)pBuffer;
1393 while (*psz != '\0')
1394 {
1395 if (*psz == RTPATH_DELIMITER)
1396 *psz = pClient->PathDelimiter;
1397 psz++;
1398 }
1399 }
1400
1401 /* free the path string */
1402 vbsfFreeFullPath(pszFullPath);
1403 }
1404
1405 return rc;
1406}
1407
1408int vbsfQueryFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
1409 uint32_t *pcbBuffer, uint8_t *pBuffer)
1410{
1411 RT_NOREF1(flags);
1412 uint32_t type = vbsfQueryHandleType(pClient, Handle);
1413 int rc = VINF_SUCCESS;
1414 SHFLFSOBJINFO *pObjInfo = (SHFLFSOBJINFO *)pBuffer;
1415 RTFSOBJINFO fileinfo;
1416
1417
1418 AssertReturn(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE, VERR_INVALID_PARAMETER);
1419 AssertReturn(pcbBuffer != NULL, VERR_INVALID_PARAMETER);
1420 AssertReturn(pObjInfo != NULL, VERR_INVALID_PARAMETER);
1421 AssertReturn(*pcbBuffer >= sizeof(SHFLFSOBJINFO), VERR_INVALID_PARAMETER);
1422
1423 /** @todo other options */
1424 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_FILE));
1425
1426 *pcbBuffer = 0;
1427
1428 if (type == SHFL_HF_TYPE_DIR)
1429 {
1430 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
1431 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1432 if (RT_SUCCESS(rc))
1433 rc = RTDirQueryInfo(pHandle->dir.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1434 }
1435 else
1436 {
1437 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1438 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1439 if (RT_SUCCESS(rc))
1440 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1441#ifdef RT_OS_WINDOWS
1442 if (RT_SUCCESS(rc) && RTFS_IS_FILE(pObjInfo->Attr.fMode))
1443 pObjInfo->Attr.fMode |= 0111;
1444#endif
1445 }
1446 if (rc == VINF_SUCCESS)
1447 {
1448 vbfsCopyFsObjInfoFromIprt(pObjInfo, &fileinfo);
1449 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1450 }
1451 else
1452 AssertFailed();
1453
1454 return rc;
1455}
1456
1457static int vbsfSetFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
1458 uint32_t *pcbBuffer, uint8_t *pBuffer)
1459{
1460 RT_NOREF1(flags);
1461 uint32_t type = vbsfQueryHandleType(pClient, Handle);
1462 int rc = VINF_SUCCESS;
1463 SHFLFSOBJINFO *pSFDEntry;
1464
1465 if ( !(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE)
1466 || pcbBuffer == 0
1467 || pBuffer == 0
1468 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
1469 {
1470 AssertFailed();
1471 return VERR_INVALID_PARAMETER;
1472 }
1473
1474 *pcbBuffer = 0;
1475 pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
1476
1477 Assert(flags == (SHFL_INFO_SET | SHFL_INFO_FILE));
1478
1479 /* Change only the time values that are not zero */
1480 if (type == SHFL_HF_TYPE_DIR)
1481 {
1482 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
1483 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1484 if (RT_SUCCESS(rc))
1485 rc = RTDirSetTimes(pHandle->dir.Handle,
1486 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1487 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1488 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1489 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1490 );
1491 }
1492 else
1493 {
1494 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1495 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1496 if (RT_SUCCESS(rc))
1497 rc = RTFileSetTimes(pHandle->file.Handle,
1498 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1499 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1500 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1501 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1502 );
1503 }
1504 if (rc != VINF_SUCCESS)
1505 {
1506 Log(("RTFileSetTimes failed with %Rrc\n", rc));
1507 Log(("AccessTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->AccessTime)));
1508 Log(("ModificationTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ModificationTime)));
1509 Log(("ChangeTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ChangeTime)));
1510 Log(("BirthTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->BirthTime)));
1511 /* temporary hack */
1512 rc = VINF_SUCCESS;
1513 }
1514
1515 if (type == SHFL_HF_TYPE_FILE)
1516 {
1517 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1518 rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1519 if (RT_SUCCESS(rc))
1520 {
1521 /* Change file attributes if necessary */
1522 if (pSFDEntry->Attr.fMode)
1523 {
1524 RTFMODE fMode = pSFDEntry->Attr.fMode;
1525
1526#ifndef RT_OS_WINDOWS
1527 /* Don't allow the guest to clear the own bit, otherwise the guest wouldn't be
1528 * able to access this file anymore. Only for guests, which set the UNIX mode.
1529 * Also, clear bits which we don't pass through for security reasons. */
1530 if (fMode & RTFS_UNIX_MASK)
1531 {
1532 fMode |= RTFS_UNIX_IRUSR;
1533 fMode &= ~(RTFS_UNIX_ISUID | RTFS_UNIX_ISGID | RTFS_UNIX_ISTXT);
1534 }
1535#endif
1536
1537 rc = RTFileSetMode(pHandle->file.Handle, fMode);
1538 if (rc != VINF_SUCCESS)
1539 {
1540 Log(("RTFileSetMode %x failed with %Rrc\n", fMode, rc));
1541 /* silent failure, because this tends to fail with e.g. windows guest & linux host */
1542 rc = VINF_SUCCESS;
1543 }
1544 }
1545 }
1546 }
1547 /** @todo mode for directories */
1548
1549 if (rc == VINF_SUCCESS)
1550 {
1551 uint32_t bufsize = sizeof(*pSFDEntry);
1552
1553 rc = vbsfQueryFileInfo(pClient, root, Handle, SHFL_INFO_GET|SHFL_INFO_FILE, &bufsize, (uint8_t *)pSFDEntry);
1554 if (rc == VINF_SUCCESS)
1555 {
1556 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1557 }
1558 else
1559 AssertFailed();
1560 }
1561
1562 return rc;
1563}
1564
1565
1566static int vbsfSetEndOfFile(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
1567 uint32_t *pcbBuffer, uint8_t *pBuffer)
1568{
1569 RT_NOREF1(flags);
1570 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1571 SHFLFSOBJINFO *pSFDEntry;
1572
1573 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
1574 {
1575 AssertFailed();
1576 return VERR_INVALID_PARAMETER;
1577 }
1578
1579 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
1580 if (RT_SUCCESS(rc))
1581 { /* likely */ }
1582 else
1583 return rc;
1584
1585 *pcbBuffer = 0;
1586 pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
1587
1588 if (flags & SHFL_INFO_SIZE)
1589 {
1590 rc = RTFileSetSize(pHandle->file.Handle, pSFDEntry->cbObject);
1591 if (rc != VINF_SUCCESS)
1592 AssertFailed();
1593 }
1594 else
1595 AssertFailed();
1596
1597 if (rc == VINF_SUCCESS)
1598 {
1599 RTFSOBJINFO fileinfo;
1600
1601 /* Query the new object info and return it */
1602 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1603 if (rc == VINF_SUCCESS)
1604 {
1605#ifdef RT_OS_WINDOWS
1606 fileinfo.Attr.fMode |= 0111;
1607#endif
1608 vbfsCopyFsObjInfoFromIprt(pSFDEntry, &fileinfo);
1609 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1610 }
1611 else
1612 AssertFailed();
1613 }
1614
1615 return rc;
1616}
1617
1618int vbsfQueryVolumeInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1619{
1620 RT_NOREF2(root, flags);
1621 int rc = VINF_SUCCESS;
1622 SHFLVOLINFO *pSFDEntry;
1623 char *pszFullPath = NULL;
1624 union
1625 {
1626 SHFLSTRING Dummy;
1627 uint8_t abDummy[SHFLSTRING_HEADER_SIZE + sizeof(RTUTF16)];
1628 } Buf;
1629
1630 if (pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLVOLINFO))
1631 {
1632 AssertFailed();
1633 return VERR_INVALID_PARAMETER;
1634 }
1635
1636 /** @todo other options */
1637 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_VOLUME));
1638
1639 *pcbBuffer = 0;
1640 pSFDEntry = (PSHFLVOLINFO)pBuffer;
1641
1642 ShflStringInitBuffer(&Buf.Dummy, sizeof(Buf));
1643 Buf.Dummy.String.ucs2[0] = '\0';
1644 rc = vbsfBuildFullPath(pClient, root, &Buf.Dummy, sizeof(Buf), &pszFullPath, NULL);
1645
1646 if (RT_SUCCESS(rc))
1647 {
1648 rc = RTFsQuerySizes(pszFullPath, &pSFDEntry->ullTotalAllocationBytes, &pSFDEntry->ullAvailableAllocationBytes, &pSFDEntry->ulBytesPerAllocationUnit, &pSFDEntry->ulBytesPerSector);
1649 if (rc != VINF_SUCCESS)
1650 goto exit;
1651
1652 rc = RTFsQuerySerial(pszFullPath, &pSFDEntry->ulSerial);
1653 if (rc != VINF_SUCCESS)
1654 goto exit;
1655
1656 RTFSPROPERTIES FsProperties;
1657 rc = RTFsQueryProperties(pszFullPath, &FsProperties);
1658 if (rc != VINF_SUCCESS)
1659 goto exit;
1660 vbfsCopyFsPropertiesFromIprt(&pSFDEntry->fsProperties, &FsProperties);
1661
1662 *pcbBuffer = sizeof(SHFLVOLINFO);
1663 }
1664 else AssertFailed();
1665
1666exit:
1667 AssertMsg(rc == VINF_SUCCESS, ("failure: rc = %Rrc\n", rc));
1668 /* free the path string */
1669 vbsfFreeFullPath(pszFullPath);
1670 return rc;
1671}
1672
1673int vbsfQueryFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1674{
1675 if (pcbBuffer == 0 || pBuffer == 0)
1676 {
1677 AssertFailed();
1678 return VERR_INVALID_PARAMETER;
1679 }
1680
1681 if (flags & SHFL_INFO_FILE)
1682 return vbsfQueryFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1683
1684 if (flags & SHFL_INFO_VOLUME)
1685 return vbsfQueryVolumeInfo(pClient, root, flags, pcbBuffer, pBuffer);
1686
1687 AssertFailed();
1688 return VERR_INVALID_PARAMETER;
1689}
1690
1691#ifdef UNITTEST
1692/** Unit test the SHFL_FN_INFORMATION API. Located here as a form of API
1693 * documentation. */
1694void testFSInfo(RTTEST hTest)
1695{
1696 /* If the number or types of parameters are wrong the API should fail. */
1697 testFSInfoBadParameters(hTest);
1698 /* Basic get and set file size test. */
1699 testFSInfoQuerySetFMode(hTest);
1700 /* Basic get and set dir atime test. */
1701 testFSInfoQuerySetDirATime(hTest);
1702 /* Basic get and set file atime test. */
1703 testFSInfoQuerySetFileATime(hTest);
1704 /* Basic set end of file. */
1705 testFSInfoQuerySetEndOfFile(hTest);
1706 /* Add tests as required... */
1707}
1708#endif
1709int vbsfSetFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1710{
1711 uint32_t type = vbsfQueryHandleType(pClient, Handle)
1712 & (SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE|SHFL_HF_TYPE_VOLUME);
1713
1714 if (type == 0 || pcbBuffer == 0 || pBuffer == 0)
1715 {
1716 AssertFailed();
1717 return VERR_INVALID_PARAMETER;
1718 }
1719
1720 if (flags & SHFL_INFO_FILE)
1721 return vbsfSetFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1722
1723 if (flags & SHFL_INFO_SIZE)
1724 return vbsfSetEndOfFile(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1725
1726// if (flags & SHFL_INFO_VOLUME)
1727// return vbsfVolumeInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1728 AssertFailed();
1729 return VERR_INVALID_PARAMETER;
1730}
1731
1732#ifdef UNITTEST
1733/** Unit test the SHFL_FN_LOCK API. Located here as a form of API
1734 * documentation. */
1735void testLock(RTTEST hTest)
1736{
1737 /* If the number or types of parameters are wrong the API should fail. */
1738 testLockBadParameters(hTest);
1739 /* Simple file locking and unlocking test. */
1740 testLockFileSimple(hTest);
1741 /* Add tests as required... */
1742}
1743#endif
1744
1745int vbsfLock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
1746{
1747 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1748 uint32_t fRTLock = 0;
1749
1750 Assert((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL);
1751
1752 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1753 if (RT_SUCCESS(rc))
1754 { /* likely */ }
1755 else
1756 return rc;
1757
1758 if ( ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL)
1759 || (flags & SHFL_LOCK_ENTIRE)
1760 )
1761 {
1762 AssertFailed();
1763 return VERR_INVALID_PARAMETER;
1764 }
1765
1766 /* Lock type */
1767 switch(flags & SHFL_LOCK_MODE_MASK)
1768 {
1769 case SHFL_LOCK_SHARED:
1770 fRTLock = RTFILE_LOCK_READ;
1771 break;
1772
1773 case SHFL_LOCK_EXCLUSIVE:
1774 fRTLock = RTFILE_LOCK_READ | RTFILE_LOCK_WRITE;
1775 break;
1776
1777 default:
1778 AssertFailed();
1779 return VERR_INVALID_PARAMETER;
1780 }
1781
1782 /* Lock wait type */
1783 if (flags & SHFL_LOCK_WAIT)
1784 fRTLock |= RTFILE_LOCK_WAIT;
1785 else
1786 fRTLock |= RTFILE_LOCK_IMMEDIATELY;
1787
1788#ifdef RT_OS_WINDOWS
1789 rc = RTFileLock(pHandle->file.Handle, fRTLock, offset, length);
1790 if (rc != VINF_SUCCESS)
1791 Log(("RTFileLock %RTfile %RX64 %RX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
1792#else
1793 Log(("vbsfLock: Pretend success handle=%x\n", Handle));
1794 rc = VINF_SUCCESS;
1795 RT_NOREF2(offset, length);
1796#endif
1797 return rc;
1798}
1799
1800int vbsfUnlock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
1801{
1802 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1803
1804 Assert((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL);
1805
1806 int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
1807 if (RT_SUCCESS(rc))
1808 { /* likely */ }
1809 else
1810 return rc;
1811
1812 if ( ((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL)
1813 || (flags & SHFL_LOCK_ENTIRE)
1814 )
1815 {
1816 return VERR_INVALID_PARAMETER;
1817 }
1818
1819#ifdef RT_OS_WINDOWS
1820 rc = RTFileUnlock(pHandle->file.Handle, offset, length);
1821 if (rc != VINF_SUCCESS)
1822 Log(("RTFileUnlock %RTfile %RX64 %RTX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
1823#else
1824 Log(("vbsfUnlock: Pretend success handle=%x\n", Handle));
1825 rc = VINF_SUCCESS;
1826 RT_NOREF2(offset, length);
1827#endif
1828
1829 return rc;
1830}
1831
1832
1833#ifdef UNITTEST
1834/** Unit test the SHFL_FN_REMOVE API. Located here as a form of API
1835 * documentation. */
1836void testRemove(RTTEST hTest)
1837{
1838 /* If the number or types of parameters are wrong the API should fail. */
1839 testRemoveBadParameters(hTest);
1840 /* Add tests as required... */
1841}
1842#endif
1843int vbsfRemove(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint32_t flags)
1844{
1845 int rc = VINF_SUCCESS;
1846
1847 /* Validate input */
1848 if ( flags & ~(SHFL_REMOVE_FILE|SHFL_REMOVE_DIR|SHFL_REMOVE_SYMLINK)
1849 || cbPath == 0
1850 || pPath == 0)
1851 {
1852 AssertFailed();
1853 return VERR_INVALID_PARAMETER;
1854 }
1855
1856 /* Build a host full path for the given path
1857 * and convert ucs2 to utf8 if necessary.
1858 */
1859 char *pszFullPath = NULL;
1860
1861 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, NULL);
1862 if (RT_SUCCESS(rc))
1863 {
1864 /* is the guest allowed to write to this share? */
1865 bool fWritable;
1866 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
1867 if (RT_FAILURE(rc) || !fWritable)
1868 rc = VERR_WRITE_PROTECT;
1869
1870 if (RT_SUCCESS(rc))
1871 {
1872 if (flags & SHFL_REMOVE_SYMLINK)
1873 rc = RTSymlinkDelete(pszFullPath, 0);
1874 else if (flags & SHFL_REMOVE_FILE)
1875 rc = RTFileDelete(pszFullPath);
1876 else
1877 rc = RTDirRemove(pszFullPath);
1878 }
1879
1880#ifndef DEBUG_dmik
1881 // VERR_ACCESS_DENIED for example?
1882 // Assert(rc == VINF_SUCCESS || rc == VERR_DIR_NOT_EMPTY);
1883#endif
1884 /* free the path string */
1885 vbsfFreeFullPath(pszFullPath);
1886 }
1887 return rc;
1888}
1889
1890
1891#ifdef UNITTEST
1892/** Unit test the SHFL_FN_RENAME API. Located here as a form of API
1893 * documentation. */
1894void testRename(RTTEST hTest)
1895{
1896 /* If the number or types of parameters are wrong the API should fail. */
1897 testRenameBadParameters(hTest);
1898 /* Add tests as required... */
1899}
1900#endif
1901int vbsfRename(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pSrc, SHFLSTRING *pDest, uint32_t flags)
1902{
1903 int rc = VINF_SUCCESS;
1904
1905 /* Validate input */
1906 if ( flags & ~(SHFL_REMOVE_FILE|SHFL_REMOVE_DIR|SHFL_RENAME_REPLACE_IF_EXISTS)
1907 || pSrc == 0
1908 || pDest == 0)
1909 {
1910 AssertFailed();
1911 return VERR_INVALID_PARAMETER;
1912 }
1913
1914 /* Build a host full path for the given path
1915 * and convert ucs2 to utf8 if necessary.
1916 */
1917 char *pszFullPathSrc = NULL;
1918 char *pszFullPathDest = NULL;
1919
1920 rc = vbsfBuildFullPath(pClient, root, pSrc, pSrc->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPathSrc, NULL);
1921 if (rc != VINF_SUCCESS)
1922 return rc;
1923
1924 rc = vbsfBuildFullPath(pClient, root, pDest, pDest->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPathDest, NULL, false, true);
1925 if (RT_SUCCESS (rc))
1926 {
1927 Log(("Rename %s to %s\n", pszFullPathSrc, pszFullPathDest));
1928
1929 /* is the guest allowed to write to this share? */
1930 bool fWritable;
1931 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
1932 if (RT_FAILURE(rc) || !fWritable)
1933 rc = VERR_WRITE_PROTECT;
1934
1935 if (RT_SUCCESS(rc))
1936 {
1937 if (flags & SHFL_RENAME_FILE)
1938 {
1939 rc = RTFileMove(pszFullPathSrc, pszFullPathDest,
1940 ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTFILEMOVE_FLAGS_REPLACE : 0));
1941 }
1942 else
1943 {
1944 /* NT ignores the REPLACE flag and simply return and already exists error. */
1945 rc = RTDirRename(pszFullPathSrc, pszFullPathDest,
1946 ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTPATHRENAME_FLAGS_REPLACE : 0));
1947 }
1948 }
1949
1950 /* free the path string */
1951 vbsfFreeFullPath(pszFullPathDest);
1952 }
1953 /* free the path string */
1954 vbsfFreeFullPath(pszFullPathSrc);
1955 return rc;
1956}
1957
1958#ifdef UNITTEST
1959/** Unit test the SHFL_FN_SYMLINK API. Located here as a form of API
1960 * documentation. */
1961void testSymlink(RTTEST hTest)
1962{
1963 /* If the number or types of parameters are wrong the API should fail. */
1964 testSymlinkBadParameters(hTest);
1965 /* Add tests as required... */
1966}
1967#endif
1968int vbsfSymlink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pNewPath, SHFLSTRING *pOldPath, SHFLFSOBJINFO *pInfo)
1969{
1970 int rc = VINF_SUCCESS;
1971
1972 char *pszFullNewPath = NULL;
1973 char *pszFullOldPath = NULL;
1974
1975 /* XXX: no support for UCS2 at the moment. */
1976 if (!BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
1977 return VERR_NOT_IMPLEMENTED;
1978
1979 bool fSymlinksCreate;
1980 rc = vbsfMappingsQuerySymlinksCreate(pClient, root, &fSymlinksCreate);
1981 AssertRCReturn(rc, rc);
1982 if (!fSymlinksCreate)
1983 return VERR_WRITE_PROTECT; /* XXX or VERR_TOO_MANY_SYMLINKS? */
1984
1985 rc = vbsfBuildFullPath(pClient, root, pNewPath, pNewPath->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullNewPath, NULL);
1986 AssertRCReturn(rc, rc);
1987
1988 /* Verify that the link target can be a valid host path, i.e. does not contain invalid characters. */
1989 uint32_t fu32PathFlags = 0;
1990 uint32_t fu32Options = 0;
1991 rc = vbsfPathGuestToHost(pClient, root, pOldPath, pOldPath->u16Size + SHFLSTRING_HEADER_SIZE,
1992 &pszFullOldPath, NULL, fu32Options, &fu32PathFlags);
1993 if (RT_FAILURE(rc))
1994 {
1995 vbsfFreeFullPath(pszFullNewPath);
1996 return rc;
1997 }
1998
1999 rc = RTSymlinkCreate(pszFullNewPath, (const char *)pOldPath->String.utf8,
2000 RTSYMLINKTYPE_UNKNOWN, 0);
2001 if (RT_SUCCESS(rc))
2002 {
2003 RTFSOBJINFO info;
2004 rc = RTPathQueryInfoEx(pszFullNewPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
2005 if (RT_SUCCESS(rc))
2006 vbfsCopyFsObjInfoFromIprt(pInfo, &info);
2007 }
2008
2009 vbsfFreeFullPath(pszFullOldPath);
2010 vbsfFreeFullPath(pszFullNewPath);
2011
2012 return rc;
2013}
2014
2015/*
2016 * Clean up our mess by freeing all handles that are still valid.
2017 *
2018 */
2019int vbsfDisconnect(SHFLCLIENTDATA *pClient)
2020{
2021 for (int i = 0; i < SHFLHANDLE_MAX; ++i)
2022 {
2023 SHFLFILEHANDLE *pHandle = NULL;
2024 SHFLHANDLE Handle = (SHFLHANDLE)i;
2025
2026 uint32_t type = vbsfQueryHandleType(pClient, Handle);
2027 switch (type & (SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE))
2028 {
2029 case SHFL_HF_TYPE_DIR:
2030 {
2031 pHandle = vbsfQueryDirHandle(pClient, Handle);
2032 break;
2033 }
2034 case SHFL_HF_TYPE_FILE:
2035 {
2036 pHandle = vbsfQueryFileHandle(pClient, Handle);
2037 break;
2038 }
2039 default:
2040 break;
2041 }
2042
2043 if (pHandle)
2044 {
2045 LogFunc(("Opened handle 0x%08x\n", i));
2046 vbsfClose(pClient, pHandle->root, Handle);
2047 }
2048 }
2049 return VINF_SUCCESS;
2050}
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