VirtualBox

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

Last change on this file since 63939 was 63619, checked in by vboxsync, 8 years ago

SharedFolders: removed unnecessary assertion

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