VirtualBox

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

Last change on this file since 58143 was 57782, checked in by vboxsync, 9 years ago

SharedFolders: options for guest to host path conversion.

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