VirtualBox

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

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

(C) 2016

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