VirtualBox

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

Last change on this file since 57766 was 57457, checked in by vboxsync, 10 years ago

SharedFolders: use the guest path delimiter.

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

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