VirtualBox

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

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

SharedFolders: additional check

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

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