VirtualBox

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

最後變更 在這個檔案從65123是 65123,由 vboxsync 提交於 8 年 前

HostServices: doxygen fixes

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette