VirtualBox

source: vbox/trunk/src/VBox/ImageMounter/VBoxFUSE/VBoxFUSE.cpp@ 76678

Last change on this file since 76678 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 44.9 KB
Line 
1/* $Id: VBoxFUSE.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * VBoxFUSE - Disk Image Flattening FUSE Program.
4 */
5
6/*
7 * Copyright (C) 2009-2019 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
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DEFAULT /** @todo log group */
23#include <iprt/types.h>
24
25#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) || defined(RT_OS_LINUX)
26# include <sys/param.h>
27# undef PVM /* Blasted old BSD mess still hanging around darwin. */
28#endif
29#define FUSE_USE_VERSION 27
30#include <fuse.h>
31#include <errno.h>
32#include <fcntl.h>
33#ifndef EDOOFUS
34# ifdef EBADMACHO
35# define EDOOFUS EBADMACHO
36# elif defined(EPROTO)
37# define EDOOFUS EPROTO /* What a boring lot. */
38//# elif defined(EXYZ)
39//# define EDOOFUS EXYZ
40# else
41# error "Choose an unlikely and (if possible) fun error number for EDOOFUS."
42# endif
43#endif
44
45#include <VBox/vd.h>
46#include <VBox/log.h>
47#include <VBox/err.h>
48#include <iprt/critsect.h>
49#include <iprt/assert.h>
50#include <iprt/asm.h>
51#include <iprt/mem.h>
52#include <iprt/string.h>
53#include <iprt/initterm.h>
54#include <iprt/stream.h>
55
56
57/*********************************************************************************************************************************
58* Structures and Typedefs *
59*********************************************************************************************************************************/
60/**
61 * Node type.
62 */
63typedef enum VBOXFUSETYPE
64{
65 VBOXFUSETYPE_INVALID = 0,
66 VBOXFUSETYPE_DIRECTORY,
67 VBOXFUSETYPE_FLAT_IMAGE,
68 VBOXFUSETYPE_CONTROL_PIPE
69} VBOXFUSETYPE;
70
71/**
72 * Stuff common to both directories and files.
73 */
74typedef struct VBOXFUSENODE
75{
76 /** The directory name. */
77 const char *pszName;
78 /** The name length. */
79 size_t cchName;
80 /** The node type. */
81 VBOXFUSETYPE enmType;
82 /** The number of references.
83 * The directory linking this node will always retain one. */
84 int32_t volatile cRefs;
85 /** Critical section serializing access to the node data. */
86 RTCRITSECT CritSect;
87 /** Pointer to the directory (parent). */
88 struct VBOXFUSEDIR *pDir;
89 /** The mode mask. */
90 RTFMODE fMode;
91 /** The User ID of the directory owner. */
92 RTUID Uid;
93 /** The Group ID of the directory. */
94 RTUID Gid;
95 /** The link count. */
96 uint32_t cLinks;
97 /** The inode number. */
98 RTINODE Ino;
99 /** The size of the primary stream. */
100 RTFOFF cbPrimary;
101} VBOXFUSENODE;
102typedef VBOXFUSENODE *PVBOXFUSENODE;
103
104/**
105 * A flat image file.
106 */
107typedef struct VBOXFUSEFLATIMAGE
108{
109 /** The standard bits. */
110 VBOXFUSENODE Node;
111 /** The virtual disk container. */
112 PVBOXHDD pDisk;
113 /** The format name. */
114 char *pszFormat;
115 /** The number of readers.
116 * Read only images will have this set to INT32_MAX/2 on creation. */
117 int32_t cReaders;
118 /** The number of writers. (Just 1 or 0 really.) */
119 int32_t cWriters;
120} VBOXFUSEFLATIMAGE;
121typedef VBOXFUSEFLATIMAGE *PVBOXFUSEFLATIMAGE;
122
123/**
124 * A control pipe (file).
125 */
126typedef struct VBOXFUSECTRLPIPE
127{
128 /** The standard bits. */
129 VBOXFUSENODE Node;
130} VBOXFUSECTRLPIPE;
131typedef VBOXFUSECTRLPIPE *PVBOXFUSECTRLPIPE;
132
133
134/**
135 * A Directory.
136 *
137 * This is just a container of files and subdirectories, nothing special.
138 */
139typedef struct VBOXFUSEDIR
140{
141 /** The standard bits. */
142 VBOXFUSENODE Node;
143 /** The number of directory entries. */
144 uint32_t cEntries;
145 /** Array of pointers to directory entries.
146 * Whether what's being pointed to is a file, directory or something else can be
147 * determined by the enmType field. */
148 PVBOXFUSENODE *paEntries;
149} VBOXFUSEDIR;
150typedef VBOXFUSEDIR *PVBOXFUSEDIR;
151
152/** The number of elements to grow VBOXFUSEDIR::paEntries by. */
153#define VBOXFUSE_DIR_GROW_BY 2 /* 32 */
154
155
156/*********************************************************************************************************************************
157* Global Variables *
158*********************************************************************************************************************************/
159/** The root of the file hierarchy. */
160static VBOXFUSEDIR *g_pTreeRoot;
161/** The next inode number. */
162static RTINODE volatile g_NextIno = 1;
163
164
165/*********************************************************************************************************************************
166* Internal Functions *
167*********************************************************************************************************************************/
168static int vboxfuseTreeLookupParent(const char *pszPath, const char **ppszName, PVBOXFUSEDIR *ppDir);
169static int vboxfuseTreeLookupParentForInsert(const char *pszPath, const char **ppszName, PVBOXFUSEDIR *ppDir);
170
171
172/**
173 * Node destructor.
174 *
175 * @returns true.
176 * @param pNode The node.
177 * @param fLocked Whether it's locked.
178 */
179static bool vboxfuseNodeDestroy(PVBOXFUSENODE pNode, bool fLocked)
180{
181 Assert(pNode->cRefs == 0);
182
183 /*
184 * Type specific cleanups.
185 */
186 switch (pNode->enmType)
187 {
188 case VBOXFUSETYPE_DIRECTORY:
189 {
190 PVBOXFUSEDIR pDir = (PVBOXFUSEDIR)pNode;
191 RTMemFree(pDir->paEntries);
192 pDir->paEntries = NULL;
193 pDir->cEntries = 0;
194 break;
195 }
196
197 case VBOXFUSETYPE_FLAT_IMAGE:
198 {
199 PVBOXFUSEFLATIMAGE pFlatImage = (PVBOXFUSEFLATIMAGE)pNode;
200 if (pFlatImage->pDisk)
201 {
202 int rc2 = VDClose(pFlatImage->pDisk, false /* fDelete */); AssertRC(rc2);
203 pFlatImage->pDisk = NULL;
204 }
205 RTStrFree(pFlatImage->pszFormat);
206 pFlatImage->pszFormat = NULL;
207 break;
208 }
209
210 case VBOXFUSETYPE_CONTROL_PIPE:
211 break;
212
213 default:
214 AssertMsgFailed(("%d\n", pNode->enmType));
215 break;
216 }
217
218 /*
219 * Generic cleanup.
220 */
221 pNode->enmType = VBOXFUSETYPE_INVALID;
222 pNode->pszName = NULL;
223
224 /*
225 * Unlock and destroy the lock, before we finally frees the node.
226 */
227 if (fLocked)
228 RTCritSectLeave(&pNode->CritSect);
229 RTCritSectDelete(&pNode->CritSect);
230
231 RTMemFree(pNode);
232
233 return true;
234}
235
236
237/**
238 * Locks a FUSE node.
239 *
240 * @param pNode The node.
241 */
242static void vboxfuseNodeLock(PVBOXFUSENODE pNode)
243{
244 int rc = RTCritSectEnter(&pNode->CritSect);
245 AssertRC(rc);
246}
247
248
249/**
250 * Unlocks a FUSE node.
251 *
252 * @param pNode The node.
253 */
254static void vboxfuseNodeUnlock(PVBOXFUSENODE pNode)
255{
256 int rc = RTCritSectLeave(&pNode->CritSect);
257 AssertRC(rc);
258}
259
260
261/**
262 * Retain a VBoxFUSE node.
263 *
264 * @param pNode The node.
265 */
266static void vboxfuseNodeRetain(PVBOXFUSENODE pNode)
267{
268 int32_t cNewRefs = ASMAtomicIncS32(&pNode->cRefs);
269 Assert(cNewRefs != 1);
270}
271
272
273/**
274 * Releases a VBoxFUSE node reference.
275 *
276 * @returns true if deleted, false if not.
277 * @param pNode The node.
278 */
279static bool vboxfuseNodeRelease(PVBOXFUSENODE pNode)
280{
281 if (ASMAtomicDecS32(&pNode->cRefs) == 0)
282 return vboxfuseNodeDestroy(pNode, false /* fLocked */);
283 return false;
284}
285
286
287/**
288 * Locks and retains a VBoxFUSE node.
289 *
290 * @param pNode The node.
291 */
292static void vboxfuseNodeLockAndRetain(PVBOXFUSENODE pNode)
293{
294 vboxfuseNodeLock(pNode);
295 vboxfuseNodeRetain(pNode);
296}
297
298
299/**
300 * Releases a VBoxFUSE node reference and unlocks it.
301 *
302 * @returns true if deleted, false if not.
303 * @param pNode The node.
304 */
305static bool vboxfuseNodeReleaseAndUnlock(PVBOXFUSENODE pNode)
306{
307 if (ASMAtomicDecS32(&pNode->cRefs) == 0)
308 return vboxfuseNodeDestroy(pNode, true /* fLocked */);
309 vboxfuseNodeUnlock(pNode);
310 return false;
311}
312
313
314/**
315 * Creates stat info for a locked node.
316 *
317 * @param pNode The node (locked).
318 */
319static void vboxfuseNodeFillStat(PVBOXFUSENODE pNode, struct stat *pStat)
320{
321 pStat->st_dev = 0; /* ignored */
322 pStat->st_ino = pNode->Ino; /* maybe ignored */
323 pStat->st_mode = pNode->fMode;
324 pStat->st_nlink = pNode->cLinks;
325 pStat->st_uid = pNode->Uid;
326 pStat->st_gid = pNode->Gid;
327 pStat->st_rdev = 0;
328 /** @todo file times */
329 pStat->st_atime = 0;
330// pStat->st_atimensec = 0;
331 pStat->st_mtime = 0;
332// pStat->st_mtimensec = 0;
333 pStat->st_ctime = 0;
334// pStat->st_ctimensec = 0;
335 pStat->st_size = pNode->cbPrimary;
336 pStat->st_blocks = (pNode->cbPrimary + DEV_BSIZE - 1) / DEV_BSIZE;
337 pStat->st_blksize = 0x1000; /* ignored */
338#ifndef RT_OS_LINUX
339 pStat->st_flags = 0;
340 pStat->st_gen = 0;
341#endif
342}
343
344
345/**
346 * Allocates a new node and initialize the node part of it.
347 *
348 * The returned node has one reference.
349 *
350 * @returns VBox status code.
351 *
352 * @param cbNode The size of the node.
353 * @param pszName The name of the node.
354 * @param enmType The node type.
355 * @param pDir The directory (parent).
356 * @param ppNode Where to return the pointer to the node.
357 */
358static int vboxfuseNodeAlloc(size_t cbNode, const char *pszName, VBOXFUSETYPE enmType, PVBOXFUSEDIR pDir,
359 PVBOXFUSENODE *ppNode)
360{
361 Assert(cbNode >= sizeof(VBOXFUSENODE));
362
363 /*
364 * Allocate the memory for it and init the critical section.
365 */
366 size_t cchName = strlen(pszName);
367 PVBOXFUSENODE pNode = (PVBOXFUSENODE)RTMemAlloc(cchName + 1 + RT_ALIGN_Z(cbNode, 8));
368 if (!pNode)
369 return VERR_NO_MEMORY;
370
371 int rc = RTCritSectInit(&pNode->CritSect);
372 if (RT_FAILURE(rc))
373 {
374 RTMemFree(pNode);
375 return rc;
376 }
377
378 /*
379 * Initialize the members.
380 */
381 pNode->pszName = (char *)memcpy((uint8_t *)pNode + RT_ALIGN_Z(cbNode, 8), pszName, cchName + 1);
382 pNode->cchName = cchName;
383 pNode->enmType = enmType;
384 pNode->cRefs = 1;
385 pNode->pDir = pDir;
386#if 0
387 pNode->fMode = enmType == VBOXFUSETYPE_DIRECTORY ? S_IFDIR | 0755 : S_IFREG | 0644;
388#else
389 pNode->fMode = enmType == VBOXFUSETYPE_DIRECTORY ? S_IFDIR | 0777 : S_IFREG | 0666;
390#endif
391 pNode->Uid = 0;
392 pNode->Gid = 0;
393 pNode->cLinks = 0;
394 pNode->Ino = g_NextIno++; /** @todo make this safe! */
395 pNode->cbPrimary = 0;
396
397 *ppNode = pNode;
398 return VINF_SUCCESS;
399}
400
401
402/**
403 * Inserts a node into a directory
404 *
405 * The caller has locked and referenced the directory as well as checked that
406 * the name doesn't already exist within it. On success both the reference and
407 * and link counters will be incremented.
408 *
409 * @returns VBox status code.
410 *
411 * @param pDir The parent directory. Can be NULL when creating the root
412 * directory.
413 * @param pNode The node to insert.
414 */
415static int vboxfuseDirInsertChild(PVBOXFUSEDIR pDir, PVBOXFUSENODE pNode)
416{
417 if (!pDir)
418 {
419 /*
420 * Special case: Root Directory.
421 */
422 AssertReturn(!g_pTreeRoot, VERR_ALREADY_EXISTS);
423 AssertReturn(pNode->enmType == VBOXFUSETYPE_DIRECTORY, VERR_INTERNAL_ERROR);
424 g_pTreeRoot = (PVBOXFUSEDIR)pNode;
425 }
426 else
427 {
428 /*
429 * Common case.
430 */
431 if (!(pDir->cEntries % VBOXFUSE_DIR_GROW_BY))
432 {
433 void *pvNew = RTMemRealloc(pDir->paEntries, sizeof(*pDir->paEntries) * (pDir->cEntries + VBOXFUSE_DIR_GROW_BY));
434 if (!pvNew)
435 return VERR_NO_MEMORY;
436 pDir->paEntries = (PVBOXFUSENODE *)pvNew;
437 }
438 pDir->paEntries[pDir->cEntries++] = pNode;
439 pDir->Node.cLinks++;
440 }
441
442 vboxfuseNodeRetain(pNode);
443 pNode->cLinks++;
444 return VINF_SUCCESS;
445}
446
447
448/**
449 * Create a directory node.
450 *
451 * @returns VBox status code.
452 * @param pszPath The path to the directory.
453 * @param ppDir Optional, where to return the new directory locked and
454 * referenced (making cRefs == 2).
455 */
456static int vboxfuseDirCreate(const char *pszPath, PVBOXFUSEDIR *ppDir)
457{
458 /*
459 * Figure out where the directory is going.
460 */
461 const char *pszName;
462 PVBOXFUSEDIR pParent;
463 int rc = vboxfuseTreeLookupParentForInsert(pszPath, &pszName, &pParent);
464 if (RT_FAILURE(rc))
465 return rc;
466
467 /*
468 * Allocate and initialize the new directory node.
469 */
470 PVBOXFUSEDIR pNewDir;
471 rc = vboxfuseNodeAlloc(sizeof(*pNewDir), pszName, VBOXFUSETYPE_DIRECTORY, pParent, (PVBOXFUSENODE *)&pNewDir);
472 if (RT_SUCCESS(rc))
473 {
474 pNewDir->cEntries = 0;
475 pNewDir->paEntries = NULL;
476
477 /*
478 * Insert it.
479 */
480 rc = vboxfuseDirInsertChild(pParent, &pNewDir->Node);
481 if ( RT_SUCCESS(rc)
482 && ppDir)
483 {
484 vboxfuseNodeLockAndRetain(&pNewDir->Node);
485 *ppDir = pNewDir;
486 }
487 vboxfuseNodeRelease(&pNewDir->Node);
488 }
489 if (pParent)
490 vboxfuseNodeReleaseAndUnlock(&pParent->Node);
491 return rc;
492}
493
494
495/**
496 * Creates a flattened image
497 *
498 * @returns VBox status code.
499 * @param pszPath Where to create the flattened file in the FUSE file
500 * system.
501 * @param pszImage The image to flatten.
502 * @param ppFile Where to return the pointer to the instance.
503 * Optional.
504 */
505static int vboxfuseFlatImageCreate(const char *pszPath, const char *pszImage, PVBOXFUSEFLATIMAGE *ppFile)
506{
507 /*
508 * Check that we can create this file.
509 */
510 const char *pszName;
511 PVBOXFUSEDIR pParent;
512 int rc = vboxfuseTreeLookupParentForInsert(pszPath, &pszName, &pParent);
513 if (RT_FAILURE(rc))
514 return rc;
515 if (pParent)
516 vboxfuseNodeReleaseAndUnlock(&pParent->Node);
517
518 /*
519 * Try open the image file (without holding any locks).
520 */
521 char *pszFormat;
522 VDTYPE enmType;
523 rc = VDGetFormat(NULL /* pVDIIfsDisk */, NULL /* pVDIIfsImage*/, pszImage, &pszFormat, &enmType);
524 if (RT_FAILURE(rc))
525 {
526 LogRel(("VDGetFormat(%s,) failed, rc=%Rrc\n", pszImage, rc));
527 return rc;
528 }
529
530 PVBOXHDD pDisk = NULL;
531 rc = VDCreate(NULL /* pVDIIfsDisk */, enmType, &pDisk);
532 if (RT_SUCCESS(rc))
533 {
534 rc = VDOpen(pDisk, pszFormat, pszImage, 0, NULL /* pVDIfsImage */);
535 if (RT_FAILURE(rc))
536 {
537 LogRel(("VDCreate(,%s,%s,,,) failed, rc=%Rrc\n", pszFormat, pszImage, rc));
538 VDClose(pDisk, false /* fDeletes */);
539 }
540 }
541 else
542 Log(("VDCreate failed, rc=%Rrc\n", rc));
543 if (RT_FAILURE(rc))
544 {
545 RTStrFree(pszFormat);
546 return rc;
547 }
548
549 /*
550 * Allocate and initialize the new directory node.
551 */
552 rc = vboxfuseTreeLookupParentForInsert(pszPath, &pszName, &pParent);
553 if (RT_SUCCESS(rc))
554 {
555 PVBOXFUSEFLATIMAGE pNewFlatImage;
556 rc = vboxfuseNodeAlloc(sizeof(*pNewFlatImage), pszName, VBOXFUSETYPE_FLAT_IMAGE, pParent, (PVBOXFUSENODE *)&pNewFlatImage);
557 if (RT_SUCCESS(rc))
558 {
559 pNewFlatImage->pDisk = pDisk;
560 pNewFlatImage->pszFormat = pszFormat;
561 pNewFlatImage->cReaders = VDIsReadOnly(pNewFlatImage->pDisk) ? INT32_MAX / 2 : 0;
562 pNewFlatImage->cWriters = 0;
563 pNewFlatImage->Node.cbPrimary = VDGetSize(pNewFlatImage->pDisk, 0 /* base */);
564
565 /*
566 * Insert it.
567 */
568 rc = vboxfuseDirInsertChild(pParent, &pNewFlatImage->Node);
569 if ( RT_SUCCESS(rc)
570 && ppFile)
571 {
572 vboxfuseNodeLockAndRetain(&pNewFlatImage->Node);
573 *ppFile = pNewFlatImage;
574 }
575 vboxfuseNodeRelease(&pNewFlatImage->Node);
576 pDisk = NULL;
577 }
578 if (pParent)
579 vboxfuseNodeReleaseAndUnlock(&pParent->Node);
580 }
581 if (RT_FAILURE(rc) && pDisk != NULL)
582 VDClose(pDisk, false /* fDelete */);
583 return rc;
584}
585
586
587//static int vboxfuseTreeMkCtrlPipe(const char *pszPath, PVBOXFUSECTRLPIPE *ppPipe)
588//{
589//}
590
591
592/**
593 * Looks up a file in the tree.
594 *
595 * Upon successful return the returned node is both referenced and locked. The
596 * call will have to release and unlock it.
597 *
598 * @returns VBox status code
599 * @param pszPath The path to the file.
600 * @param ppNode Where to return the node.
601 */
602static int vboxfuseTreeLookup(const char *pszPath, PVBOXFUSENODE *ppNode)
603{
604 /*
605 * Root first.
606 */
607 const char *psz = pszPath;
608 if (*psz != '/')
609 return VERR_FILE_NOT_FOUND;
610
611 PVBOXFUSEDIR pDir = g_pTreeRoot;
612 vboxfuseNodeLockAndRetain(&pDir->Node);
613
614 do psz++;
615 while (*psz == '/');
616 if (!*psz)
617 {
618 /* looking for the root. */
619 *ppNode = &pDir->Node;
620 return VINF_SUCCESS;
621 }
622
623 /*
624 * Take it bit by bit from here on.
625 */
626 for (;;)
627 {
628 /*
629 * Find the length of the current directory entry and check if it must be file.
630 */
631 const char * const pszName = psz;
632 psz = strchr(psz, '/');
633 if (!psz)
634 psz = strchr(pszName, '\0');
635 size_t cchName = psz - pszName;
636
637 bool fMustBeDir = *psz == '/';
638 while (*psz == '/')
639 psz++;
640
641 /*
642 * Look it up.
643 * This is safe as the directory will hold a reference to each node
644 * so the nodes cannot possibly be destroyed while we're searching them.
645 */
646 PVBOXFUSENODE pNode = NULL;
647 uint32_t i = pDir->cEntries;
648 PVBOXFUSENODE *paEntries = pDir->paEntries;
649 while (i-- > 0)
650 {
651 PVBOXFUSENODE pCur = paEntries[i];
652 if ( pCur->cchName == cchName
653 && !memcmp(pCur->pszName, pszName, cchName))
654 {
655 pNode = pCur;
656 vboxfuseNodeLockAndRetain(pNode);
657 break;
658 }
659 }
660 vboxfuseNodeReleaseAndUnlock(&pDir->Node);
661
662 if (!pNode)
663 return *psz ? VERR_PATH_NOT_FOUND : VERR_FILE_NOT_FOUND;
664 if ( fMustBeDir
665 && pNode->enmType != VBOXFUSETYPE_DIRECTORY)
666 {
667 vboxfuseNodeReleaseAndUnlock(pNode);
668 return VERR_NOT_A_DIRECTORY;
669 }
670
671 /*
672 * Are we done?
673 */
674 if (!*psz)
675 {
676 *ppNode = pNode;
677 return VINF_SUCCESS;
678 }
679
680 /* advance */
681 pDir = (PVBOXFUSEDIR)pNode;
682 }
683}
684
685
686/**
687 * Errno conversion wrapper around vboxfuseTreeLookup().
688 *
689 * @returns 0 on success, negated errno on failure.
690 * @param pszPath The path to the file.
691 * @param ppNode Where to return the node.
692 */
693static int vboxfuseTreeLookupErrno(const char *pszPath, PVBOXFUSENODE *ppNode)
694{
695 int rc = vboxfuseTreeLookup(pszPath, ppNode);
696 if (RT_SUCCESS(rc))
697 return 0;
698 return -RTErrConvertToErrno(rc);
699}
700
701
702/**
703 * Looks up a parent directory in the tree.
704 *
705 * Upon successful return the returned directory is both referenced and locked.
706 * The call will have to release and unlock it.
707 *
708 * @returns VBox status code.
709 *
710 * @param pszPath The path to the file which parent we seek.
711 * @param ppszName Where to return the pointer to the child's name within
712 * pszPath.
713 * @param ppDir Where to return the parent directory.
714 */
715static int vboxfuseTreeLookupParent(const char *pszPath, const char **ppszName, PVBOXFUSEDIR *ppDir)
716{
717 /*
718 * Root first.
719 */
720 const char *psz = pszPath;
721 if (*psz != '/')
722 return VERR_INVALID_PARAMETER;
723 do psz++;
724 while (*psz == '/');
725 if (!*psz)
726 {
727 /* looking for the root. */
728 *ppszName = psz + 1;
729 *ppDir = NULL;
730 return VINF_SUCCESS;
731 }
732
733 /*
734 * Take it bit by bit from here on.
735 */
736 PVBOXFUSEDIR pDir = g_pTreeRoot;
737 AssertReturn(pDir, VERR_WRONG_ORDER);
738 vboxfuseNodeLockAndRetain(&pDir->Node);
739 for (;;)
740 {
741 /*
742 * Find the length of the current directory entry and check if it must be file.
743 */
744 const char * const pszName = psz;
745 psz = strchr(psz, '/');
746 if (!psz)
747 {
748 /* that's all folks.*/
749 *ppszName = pszName;
750 *ppDir = pDir;
751 return VINF_SUCCESS;
752 }
753 size_t cchName = psz - pszName;
754
755 bool fMustBeDir = *psz == '/';
756 while (*psz == '/')
757 psz++;
758
759 /* Trailing slashes are not allowed (because it's simpler without them). */
760 if (!*psz)
761 return VERR_INVALID_PARAMETER;
762
763 /*
764 * Look it up.
765 * This is safe as the directory will hold a reference to each node
766 * so the nodes cannot possibly be destroyed while we're searching them.
767 */
768 PVBOXFUSENODE pNode = NULL;
769 uint32_t i = pDir->cEntries;
770 PVBOXFUSENODE *paEntries = pDir->paEntries;
771 while (i-- > 0)
772 {
773 PVBOXFUSENODE pCur = paEntries[i];
774 if ( pCur->cchName == cchName
775 && !memcmp(pCur->pszName, pszName, cchName))
776 {
777 pNode = pCur;
778 vboxfuseNodeLockAndRetain(pNode);
779 break;
780 }
781 }
782 vboxfuseNodeReleaseAndUnlock(&pDir->Node);
783
784 if (!pNode)
785 return VERR_FILE_NOT_FOUND;
786 if ( fMustBeDir
787 && pNode->enmType != VBOXFUSETYPE_DIRECTORY)
788 {
789 vboxfuseNodeReleaseAndUnlock(pNode);
790 return VERR_PATH_NOT_FOUND;
791 }
792
793 /* advance */
794 pDir = (PVBOXFUSEDIR)pNode;
795 }
796}
797
798
799/**
800 * Looks up a parent directory in the tree and checking that the specified child
801 * doesn't already exist.
802 *
803 * Upon successful return the returned directory is both referenced and locked.
804 * The call will have to release and unlock it.
805 *
806 * @returns VBox status code.
807 *
808 * @param pszPath The path to the file which parent we seek.
809 * @param ppszName Where to return the pointer to the child's name within
810 * pszPath.
811 * @param ppDir Where to return the parent directory.
812 */
813static int vboxfuseTreeLookupParentForInsert(const char *pszPath, const char **ppszName, PVBOXFUSEDIR *ppDir)
814{
815 /*
816 * Lookup the parent directory using vboxfuseTreeLookupParent first.
817 */
818 const char *pszName;
819 PVBOXFUSEDIR pDir;
820 int rc = vboxfuseTreeLookupParent(pszPath, &pszName, &pDir);
821 if (RT_SUCCESS(rc))
822 {
823 /*
824 * Check that it doesn't exist already
825 */
826 if (pDir)
827 {
828 size_t const cchName = strlen(pszName);
829 uint32_t i = pDir->cEntries;
830 PVBOXFUSENODE *paEntries = pDir->paEntries;
831 while (i-- > 0)
832 {
833 PVBOXFUSENODE pCur = paEntries[i];
834 if ( pCur->cchName == cchName
835 && !memcmp(pCur->pszName, pszName, cchName))
836 {
837 vboxfuseNodeReleaseAndUnlock(&pDir->Node);
838 rc = VERR_ALREADY_EXISTS;
839 break;
840 }
841 }
842 }
843 if (RT_SUCCESS(rc))
844 {
845 *ppDir = pDir;
846 *ppszName = pszName;
847 }
848 }
849 return rc;
850}
851
852
853
854
855
856/** @copydoc fuse_operations::getattr */
857static int vboxfuseOp_getattr(const char *pszPath, struct stat *pStat)
858{
859 PVBOXFUSENODE pNode;
860 int rc = vboxfuseTreeLookupErrno(pszPath, &pNode);
861 if (!rc)
862 {
863 vboxfuseNodeFillStat(pNode, pStat);
864 vboxfuseNodeReleaseAndUnlock(pNode);
865 }
866 LogFlow(("vboxfuseOp_getattr: rc=%d \"%s\"\n", rc, pszPath));
867 return rc;
868}
869
870
871/** @copydoc fuse_operations::opendir */
872static int vboxfuseOp_opendir(const char *pszPath, struct fuse_file_info *pInfo)
873{
874 PVBOXFUSENODE pNode;
875 int rc = vboxfuseTreeLookupErrno(pszPath, &pNode);
876 if (!rc)
877 {
878 /*
879 * Check that it's a directory and that the caller should see it.
880 */
881 if (pNode->enmType != VBOXFUSETYPE_DIRECTORY)
882 rc = -ENOTDIR;
883 /** @todo access checks. */
884 else
885 {
886 /** @todo update the accessed TS? */
887
888 /*
889 * Put a reference to the node in the fuse_file_info::fh member so
890 * we don't have to parse the path in readdir.
891 */
892 pInfo->fh = (uintptr_t)pNode;
893 vboxfuseNodeUnlock(pNode);
894 }
895
896 /* cleanup */
897 if (rc)
898 vboxfuseNodeReleaseAndUnlock(pNode);
899 }
900 LogFlow(("vboxfuseOp_opendir: rc=%d \"%s\"\n", rc, pszPath));
901 return rc;
902}
903
904
905/** @copydoc fuse_operations::readdir */
906static int vboxfuseOp_readdir(const char *pszPath, void *pvBuf, fuse_fill_dir_t pfnFiller,
907 off_t offDir, struct fuse_file_info *pInfo)
908{
909 PVBOXFUSEDIR pDir = (PVBOXFUSEDIR)(uintptr_t)pInfo->fh;
910 AssertPtr(pDir);
911 Assert(pDir->Node.enmType == VBOXFUSETYPE_DIRECTORY);
912 vboxfuseNodeLock(&pDir->Node);
913 LogFlow(("vboxfuseOp_readdir: offDir=%llx \"%s\"\n", (uint64_t)offDir, pszPath));
914
915#define VBOXFUSE_FAKE_DIRENT_SIZE 512
916
917 /*
918 * First the mandatory dot and dot-dot entries.
919 */
920 struct stat st;
921 int rc = 0;
922 if (!offDir)
923 {
924 offDir += VBOXFUSE_FAKE_DIRENT_SIZE;
925 vboxfuseNodeFillStat(&pDir->Node, &st);
926 rc = pfnFiller(pvBuf, ".", &st, offDir);
927 }
928 if ( offDir == VBOXFUSE_FAKE_DIRENT_SIZE
929 && !rc)
930 {
931 offDir += VBOXFUSE_FAKE_DIRENT_SIZE;
932 rc = pfnFiller(pvBuf, "..", NULL, offDir);
933 }
934
935 /*
936 * Convert the offset to a directory index and start/continue filling the buffer.
937 * The entries only needs locking since the directory already has a reference
938 * to each of them.
939 */
940 Assert(offDir >= VBOXFUSE_FAKE_DIRENT_SIZE * 2 || rc);
941 uint32_t i = offDir / VBOXFUSE_FAKE_DIRENT_SIZE - 2;
942 while ( !rc
943 && i < pDir->cEntries)
944 {
945 PVBOXFUSENODE pNode = pDir->paEntries[i];
946 vboxfuseNodeLock(pNode);
947
948 vboxfuseNodeFillStat(pNode, &st);
949 offDir = (i + 3) * VBOXFUSE_FAKE_DIRENT_SIZE;
950 rc = pfnFiller(pvBuf, pNode->pszName, &st, offDir);
951
952 vboxfuseNodeUnlock(pNode);
953
954 /* next */
955 i++;
956 }
957
958 vboxfuseNodeUnlock(&pDir->Node);
959 LogFlow(("vboxfuseOp_readdir: returns offDir=%llx\n", (uint64_t)offDir));
960 return 0;
961}
962
963
964/** @copydoc fuse_operations::releasedir */
965static int vboxfuseOp_releasedir(const char *pszPath, struct fuse_file_info *pInfo)
966{
967 PVBOXFUSEDIR pDir = (PVBOXFUSEDIR)(uintptr_t)pInfo->fh;
968 AssertPtr(pDir);
969 Assert(pDir->Node.enmType == VBOXFUSETYPE_DIRECTORY);
970 pInfo->fh = 0;
971 vboxfuseNodeRelease(&pDir->Node);
972 LogFlow(("vboxfuseOp_releasedir: \"%s\"\n", pszPath));
973 return 0;
974}
975
976
977/** @copydoc fuse_operations::symlink */
978static int vboxfuseOp_symlink(const char *pszDst, const char *pszPath)
979{
980 /*
981 * "Interface" for mounting a image.
982 */
983 int rc = vboxfuseFlatImageCreate(pszPath, pszDst, NULL);
984 if (RT_SUCCESS(rc))
985 {
986 Log(("vboxfuseOp_symlink: \"%s\" => \"%s\" SUCCESS!\n", pszPath, pszDst));
987 return 0;
988 }
989
990 LogFlow(("vboxfuseOp_symlink: \"%s\" => \"%s\" rc=%Rrc\n", pszPath, pszDst, rc));
991 return -RTErrConvertToErrno(rc);
992}
993
994
995/** @copydoc fuse_operations::open */
996static int vboxfuseOp_open(const char *pszPath, struct fuse_file_info *pInfo)
997{
998 LogFlow(("vboxfuseOp_open(\"%s\", .flags=%#x)\n", pszPath, pInfo->flags));
999
1000 /*
1001 * Validate incoming flags.
1002 */
1003#ifdef RT_OS_DARWIN
1004 if (pInfo->flags & (O_APPEND | O_NONBLOCK | O_SYMLINK | O_NOCTTY | O_SHLOCK | O_EXLOCK | O_ASYNC
1005 | O_CREAT | O_TRUNC | O_EXCL | O_EVTONLY))
1006 return -EINVAL;
1007 if ((pInfo->flags & O_ACCMODE) == O_ACCMODE)
1008 return -EINVAL;
1009#elif defined(RT_OS_LINUX)
1010 if (pInfo->flags & ( O_APPEND | O_ASYNC | O_DIRECT /* | O_LARGEFILE ? */
1011 | O_NOATIME | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK
1012 /* | O_SYNC ? */))
1013 return -EINVAL;
1014 if ((pInfo->flags & O_ACCMODE) == O_ACCMODE)
1015 return -EINVAL;
1016#elif defined(RT_OS_FREEBSD)
1017 if (pInfo->flags & ( O_APPEND | O_ASYNC | O_DIRECT /* | O_LARGEFILE ? */
1018 | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK
1019 /* | O_SYNC ? */))
1020 return -EINVAL;
1021 if ((pInfo->flags & O_ACCMODE) == O_ACCMODE)
1022 return -EINVAL;
1023#else
1024# error "Port me"
1025#endif
1026
1027 PVBOXFUSENODE pNode;
1028 int rc = vboxfuseTreeLookupErrno(pszPath, &pNode);
1029 if (!rc)
1030 {
1031 /*
1032 * Check flags and stuff.
1033 */
1034 switch (pNode->enmType)
1035 {
1036 /* not expected here? */
1037 case VBOXFUSETYPE_DIRECTORY:
1038 AssertFailed();
1039 rc = -EISDIR;
1040 break;
1041
1042 case VBOXFUSETYPE_FLAT_IMAGE:
1043 {
1044 PVBOXFUSEFLATIMAGE pFlatImage = (PVBOXFUSEFLATIMAGE)pNode;
1045#ifdef O_DIRECTORY
1046 if (pInfo->flags & O_DIRECTORY)
1047 rc = -ENOTDIR;
1048#endif
1049 if ( (pInfo->flags & O_ACCMODE) == O_WRONLY
1050 || (pInfo->flags & O_ACCMODE) == O_RDWR)
1051 {
1052 if ( pFlatImage->cWriters == 0
1053 && pFlatImage->cReaders == 0)
1054 pFlatImage->cWriters++;
1055 else
1056 rc = -ETXTBSY;
1057 }
1058 else if ((pInfo->flags & O_ACCMODE) == O_RDONLY)
1059 {
1060 if (pFlatImage->cWriters == 0)
1061 {
1062 if (pFlatImage->cReaders + 1 < ( pFlatImage->cReaders < INT32_MAX / 2
1063 ? INT32_MAX / 4
1064 : INT32_MAX / 2 + INT32_MAX / 4) )
1065 pFlatImage->cReaders++;
1066 else
1067 rc = -EMLINK;
1068 }
1069 else
1070 rc = -ETXTBSY;
1071 }
1072 break;
1073 }
1074
1075 case VBOXFUSETYPE_CONTROL_PIPE:
1076 rc = -ENOTSUP;
1077 break;
1078
1079 default:
1080 rc = -EDOOFUS;
1081 break;
1082 }
1083 if (!rc)
1084 {
1085 /*
1086 * Put a reference to the node in the fuse_file_info::fh member so
1087 * we don't have to parse the path in the other file methods.
1088 */
1089 pInfo->fh = (uintptr_t)pNode;
1090 vboxfuseNodeUnlock(pNode);
1091 }
1092 else
1093 {
1094 /* cleanup */
1095 vboxfuseNodeReleaseAndUnlock(pNode);
1096 }
1097 }
1098 LogFlow(("vboxfuseOp_opendir: rc=%d \"%s\"\n", rc, pszPath));
1099 return rc;
1100}
1101
1102
1103/** @copydoc fuse_operations::release */
1104static int vboxfuseOp_release(const char *pszPath, struct fuse_file_info *pInfo)
1105{
1106 PVBOXFUSENODE pNode = (PVBOXFUSENODE)(uintptr_t)pInfo->fh;
1107 AssertPtr(pNode);
1108 pInfo->fh = 0;
1109
1110 switch (pNode->enmType)
1111 {
1112 case VBOXFUSETYPE_DIRECTORY:
1113 /* nothing to do */
1114 vboxfuseNodeRelease(pNode);
1115 break;
1116
1117 case VBOXFUSETYPE_FLAT_IMAGE:
1118 {
1119 PVBOXFUSEFLATIMAGE pFlatImage = (PVBOXFUSEFLATIMAGE)pNode;
1120 vboxfuseNodeLock(&pFlatImage->Node);
1121
1122 if ( (pInfo->flags & O_ACCMODE) == O_WRONLY
1123 || (pInfo->flags & O_ACCMODE) == O_RDWR)
1124 {
1125 pFlatImage->cWriters--;
1126 Assert(pFlatImage->cWriters >= 0);
1127 }
1128 else if ((pInfo->flags & O_ACCMODE) == O_RDONLY)
1129 {
1130 pFlatImage->cReaders--;
1131 Assert(pFlatImage->cReaders >= 0);
1132 }
1133 else
1134 AssertFailed();
1135
1136 vboxfuseNodeReleaseAndUnlock(&pFlatImage->Node);
1137 break;
1138 }
1139
1140 case VBOXFUSETYPE_CONTROL_PIPE:
1141 /* nothing to do yet */
1142 vboxfuseNodeRelease(pNode);
1143 break;
1144
1145 default:
1146 AssertMsgFailed(("%s\n", pszPath));
1147 return -EDOOFUS;
1148 }
1149
1150 LogFlow(("vboxfuseOp_release: \"%s\"\n", pszPath));
1151 return 0;
1152}
1153
1154/** The VDRead/VDWrite block granularity. */
1155#define VBOXFUSE_MIN_SIZE 512
1156/** Offset mask corresponding to VBOXFUSE_MIN_SIZE. */
1157#define VBOXFUSE_MIN_SIZE_MASK_OFF (0x1ff)
1158/** Block mask corresponding to VBOXFUSE_MIN_SIZE. */
1159#define VBOXFUSE_MIN_SIZE_MASK_BLK (~UINT64_C(0x1ff))
1160
1161/** @copydoc fuse_operations::read */
1162static int vboxfuseOp_read(const char *pszPath, char *pbBuf, size_t cbBuf,
1163 off_t offFile, struct fuse_file_info *pInfo)
1164{
1165 /* paranoia */
1166 AssertReturn((int)cbBuf >= 0, -EINVAL);
1167 AssertReturn((unsigned)cbBuf == cbBuf, -EINVAL);
1168 AssertReturn(offFile >= 0, -EINVAL);
1169 AssertReturn((off_t)(offFile + cbBuf) >= offFile, -EINVAL);
1170
1171 PVBOXFUSENODE pNode = (PVBOXFUSENODE)(uintptr_t)pInfo->fh;
1172 AssertPtr(pNode);
1173 switch (pNode->enmType)
1174 {
1175 case VBOXFUSETYPE_DIRECTORY:
1176 return -ENOTSUP;
1177
1178 case VBOXFUSETYPE_FLAT_IMAGE:
1179 {
1180 PVBOXFUSEFLATIMAGE pFlatImage = (PVBOXFUSEFLATIMAGE)(uintptr_t)pInfo->fh;
1181 LogFlow(("vboxfuseOp_read: offFile=%#llx cbBuf=%#zx pszPath=\"%s\"\n", (uint64_t)offFile, cbBuf, pszPath));
1182 vboxfuseNodeLock(&pFlatImage->Node);
1183
1184 int rc;
1185 if ((off_t)(offFile + cbBuf) < offFile)
1186 rc = -EINVAL;
1187 else if (offFile >= pFlatImage->Node.cbPrimary)
1188 rc = 0;
1189 else if (!cbBuf)
1190 rc = 0;
1191 else
1192 {
1193 /* Adjust for EOF. */
1194 if ((off_t)(offFile + cbBuf) >= pFlatImage->Node.cbPrimary)
1195 cbBuf = pFlatImage->Node.cbPrimary - offFile;
1196
1197 /*
1198 * Aligned read?
1199 */
1200 int rc2;
1201 if ( !(offFile & VBOXFUSE_MIN_SIZE_MASK_OFF)
1202 && !(cbBuf & VBOXFUSE_MIN_SIZE_MASK_OFF))
1203 rc2 = VDRead(pFlatImage->pDisk, offFile, pbBuf, cbBuf);
1204 else
1205 {
1206 /*
1207 * Unaligned read - lots of extra work.
1208 */
1209 uint8_t abBlock[VBOXFUSE_MIN_SIZE];
1210 if (((offFile + cbBuf) & VBOXFUSE_MIN_SIZE_MASK_BLK) == (offFile & VBOXFUSE_MIN_SIZE_MASK_BLK))
1211 {
1212 /* a single partial block. */
1213 rc2 = VDRead(pFlatImage->pDisk, offFile & VBOXFUSE_MIN_SIZE_MASK_BLK, abBlock, VBOXFUSE_MIN_SIZE);
1214 if (RT_SUCCESS(rc2))
1215 memcpy(pbBuf, &abBlock[offFile & VBOXFUSE_MIN_SIZE_MASK_OFF], cbBuf);
1216 }
1217 else
1218 {
1219 /* read unaligned head. */
1220 rc2 = VINF_SUCCESS;
1221 if (offFile & VBOXFUSE_MIN_SIZE_MASK_OFF)
1222 {
1223 rc2 = VDRead(pFlatImage->pDisk, offFile & VBOXFUSE_MIN_SIZE_MASK_BLK, abBlock, VBOXFUSE_MIN_SIZE);
1224 if (RT_SUCCESS(rc2))
1225 {
1226 size_t cbCopy = VBOXFUSE_MIN_SIZE - (offFile & VBOXFUSE_MIN_SIZE_MASK_OFF);
1227 memcpy(pbBuf, &abBlock[offFile & VBOXFUSE_MIN_SIZE_MASK_OFF], cbCopy);
1228 pbBuf += cbCopy;
1229 offFile += cbCopy;
1230 cbBuf -= cbCopy;
1231 }
1232 }
1233
1234 /* read the middle. */
1235 Assert(!(offFile & VBOXFUSE_MIN_SIZE_MASK_OFF));
1236 if (cbBuf >= VBOXFUSE_MIN_SIZE && RT_SUCCESS(rc2))
1237 {
1238 size_t cbRead = cbBuf & VBOXFUSE_MIN_SIZE_MASK_BLK;
1239 rc2 = VDRead(pFlatImage->pDisk, offFile, pbBuf, cbRead);
1240 if (RT_SUCCESS(rc2))
1241 {
1242 pbBuf += cbRead;
1243 offFile += cbRead;
1244 cbBuf -= cbRead;
1245 }
1246 }
1247
1248 /* unaligned tail read. */
1249 Assert(cbBuf < VBOXFUSE_MIN_SIZE);
1250 Assert(!(offFile & VBOXFUSE_MIN_SIZE_MASK_OFF));
1251 if (cbBuf && RT_SUCCESS(rc2))
1252 {
1253 rc2 = VDRead(pFlatImage->pDisk, offFile, abBlock, VBOXFUSE_MIN_SIZE);
1254 if (RT_SUCCESS(rc2))
1255 memcpy(pbBuf, &abBlock[0], cbBuf);
1256 }
1257 }
1258 }
1259
1260 /* convert the return code */
1261 if (RT_SUCCESS(rc2))
1262 rc = cbBuf;
1263 else
1264 rc = -RTErrConvertToErrno(rc2);
1265 }
1266
1267 vboxfuseNodeUnlock(&pFlatImage->Node);
1268 return rc;
1269 }
1270
1271 case VBOXFUSETYPE_CONTROL_PIPE:
1272 return -ENOTSUP;
1273
1274 default:
1275 AssertMsgFailed(("%s\n", pszPath));
1276 return -EDOOFUS;
1277 }
1278}
1279
1280
1281/** @copydoc fuse_operations::write */
1282static int vboxfuseOp_write(const char *pszPath, const char *pbBuf, size_t cbBuf,
1283 off_t offFile, struct fuse_file_info *pInfo)
1284{
1285 /* paranoia */
1286 AssertReturn((int)cbBuf >= 0, -EINVAL);
1287 AssertReturn((unsigned)cbBuf == cbBuf, -EINVAL);
1288 AssertReturn(offFile >= 0, -EINVAL);
1289 AssertReturn((off_t)(offFile + cbBuf) >= offFile, -EINVAL);
1290
1291 PVBOXFUSENODE pNode = (PVBOXFUSENODE)(uintptr_t)pInfo->fh;
1292 AssertPtr(pNode);
1293 switch (pNode->enmType)
1294 {
1295 case VBOXFUSETYPE_DIRECTORY:
1296 return -ENOTSUP;
1297
1298 case VBOXFUSETYPE_FLAT_IMAGE:
1299 {
1300 PVBOXFUSEFLATIMAGE pFlatImage = (PVBOXFUSEFLATIMAGE)(uintptr_t)pInfo->fh;
1301 LogFlow(("vboxfuseOp_write: offFile=%#llx cbBuf=%#zx pszPath=\"%s\"\n", (uint64_t)offFile, cbBuf, pszPath));
1302 vboxfuseNodeLock(&pFlatImage->Node);
1303
1304 int rc;
1305 if ((off_t)(offFile + cbBuf) < offFile)
1306 rc = -EINVAL;
1307 else if (offFile >= pFlatImage->Node.cbPrimary)
1308 rc = 0;
1309 else if (!cbBuf)
1310 rc = 0;
1311 else
1312 {
1313 /* Adjust for EOF. */
1314 if ((off_t)(offFile + cbBuf) >= pFlatImage->Node.cbPrimary)
1315 cbBuf = pFlatImage->Node.cbPrimary - offFile;
1316
1317 /*
1318 * Aligned write?
1319 */
1320 int rc2;
1321 if ( !(offFile & VBOXFUSE_MIN_SIZE_MASK_OFF)
1322 && !(cbBuf & VBOXFUSE_MIN_SIZE_MASK_OFF))
1323 rc2 = VDWrite(pFlatImage->pDisk, offFile, pbBuf, cbBuf);
1324 else
1325 {
1326 /*
1327 * Unaligned write - lots of extra work.
1328 */
1329 uint8_t abBlock[VBOXFUSE_MIN_SIZE];
1330 if (((offFile + cbBuf) & VBOXFUSE_MIN_SIZE_MASK_BLK) == (offFile & VBOXFUSE_MIN_SIZE_MASK_BLK))
1331 {
1332 /* a single partial block. */
1333 rc2 = VDRead(pFlatImage->pDisk, offFile & VBOXFUSE_MIN_SIZE_MASK_BLK, abBlock, VBOXFUSE_MIN_SIZE);
1334 if (RT_SUCCESS(rc2))
1335 {
1336 memcpy(&abBlock[offFile & VBOXFUSE_MIN_SIZE_MASK_OFF], pbBuf, cbBuf);
1337 /* Update the block */
1338 rc2 = VDWrite(pFlatImage->pDisk, offFile & VBOXFUSE_MIN_SIZE_MASK_BLK, abBlock, VBOXFUSE_MIN_SIZE);
1339 }
1340 }
1341 else
1342 {
1343 /* read unaligned head. */
1344 rc2 = VINF_SUCCESS;
1345 if (offFile & VBOXFUSE_MIN_SIZE_MASK_OFF)
1346 {
1347 rc2 = VDRead(pFlatImage->pDisk, offFile & VBOXFUSE_MIN_SIZE_MASK_BLK, abBlock, VBOXFUSE_MIN_SIZE);
1348 if (RT_SUCCESS(rc2))
1349 {
1350 size_t cbCopy = VBOXFUSE_MIN_SIZE - (offFile & VBOXFUSE_MIN_SIZE_MASK_OFF);
1351 memcpy(&abBlock[offFile & VBOXFUSE_MIN_SIZE_MASK_OFF], pbBuf, cbCopy);
1352 pbBuf += cbCopy;
1353 offFile += cbCopy;
1354 cbBuf -= cbCopy;
1355 rc2 = VDWrite(pFlatImage->pDisk, offFile & VBOXFUSE_MIN_SIZE_MASK_BLK, abBlock, VBOXFUSE_MIN_SIZE);
1356 }
1357 }
1358
1359 /* write the middle. */
1360 Assert(!(offFile & VBOXFUSE_MIN_SIZE_MASK_OFF));
1361 if (cbBuf >= VBOXFUSE_MIN_SIZE && RT_SUCCESS(rc2))
1362 {
1363 size_t cbWrite = cbBuf & VBOXFUSE_MIN_SIZE_MASK_BLK;
1364 rc2 = VDWrite(pFlatImage->pDisk, offFile, pbBuf, cbWrite);
1365 if (RT_SUCCESS(rc2))
1366 {
1367 pbBuf += cbWrite;
1368 offFile += cbWrite;
1369 cbBuf -= cbWrite;
1370 }
1371 }
1372
1373 /* unaligned tail write. */
1374 Assert(cbBuf < VBOXFUSE_MIN_SIZE);
1375 Assert(!(offFile & VBOXFUSE_MIN_SIZE_MASK_OFF));
1376 if (cbBuf && RT_SUCCESS(rc2))
1377 {
1378 rc2 = VDRead(pFlatImage->pDisk, offFile, abBlock, VBOXFUSE_MIN_SIZE);
1379 if (RT_SUCCESS(rc2))
1380 {
1381 memcpy(&abBlock[0], pbBuf, cbBuf);
1382 rc2 = VDWrite(pFlatImage->pDisk, offFile, abBlock, VBOXFUSE_MIN_SIZE);
1383 }
1384 }
1385 }
1386 }
1387
1388 /* convert the return code */
1389 if (RT_SUCCESS(rc2))
1390 rc = cbBuf;
1391 else
1392 rc = -RTErrConvertToErrno(rc2);
1393 }
1394
1395 vboxfuseNodeUnlock(&pFlatImage->Node);
1396 return rc;
1397 }
1398
1399 case VBOXFUSETYPE_CONTROL_PIPE:
1400 return -ENOTSUP;
1401
1402 default:
1403 AssertMsgFailed(("%s\n", pszPath));
1404 return -EDOOFUS;
1405 }
1406}
1407
1408
1409/**
1410 * The FUSE operations.
1411 *
1412 * @remarks We'll initialize this manually since we cannot use C99 style
1413 * initialzer designations in C++ (yet).
1414 */
1415static struct fuse_operations g_vboxfuseOps;
1416
1417
1418
1419int main(int argc, char **argv)
1420{
1421 /*
1422 * Initialize the runtime and VD.
1423 */
1424 int rc = RTR3InitExe(argc, &argv, 0);
1425 if (RT_FAILURE(rc))
1426 {
1427 RTStrmPrintf(g_pStdErr, "VBoxFUSE: RTR3InitExe failed, rc=%Rrc\n", rc);
1428 return 1;
1429 }
1430 RTPrintf("VBoxFUSE: Hello...\n");
1431 rc = VDInit();
1432 if (RT_FAILURE(rc))
1433 {
1434 RTStrmPrintf(g_pStdErr, "VBoxFUSE: VDInit failed, rc=%Rrc\n", rc);
1435 return 1;
1436 }
1437
1438 /*
1439 * Initializes the globals and populate the file hierarchy.
1440 */
1441 rc = vboxfuseDirCreate("/", NULL);
1442 if (RT_SUCCESS(rc))
1443 rc = vboxfuseDirCreate("/FlattenedImages", NULL);
1444 if (RT_FAILURE(rc))
1445 {
1446 RTStrmPrintf(g_pStdErr, "VBoxFUSE: vboxfuseDirCreate failed, rc=%Rrc\n", rc);
1447 return 1;
1448 }
1449
1450 /*
1451 * Initialize the g_vboxfuseOps. (C++ sucks!)
1452 */
1453 memset(&g_vboxfuseOps, 0, sizeof(g_vboxfuseOps));
1454 g_vboxfuseOps.getattr = vboxfuseOp_getattr;
1455 g_vboxfuseOps.opendir = vboxfuseOp_opendir;
1456 g_vboxfuseOps.readdir = vboxfuseOp_readdir;
1457 g_vboxfuseOps.releasedir = vboxfuseOp_releasedir;
1458 g_vboxfuseOps.symlink = vboxfuseOp_symlink;
1459 g_vboxfuseOps.open = vboxfuseOp_open;
1460 g_vboxfuseOps.read = vboxfuseOp_read;
1461 g_vboxfuseOps.write = vboxfuseOp_write;
1462 g_vboxfuseOps.release = vboxfuseOp_release;
1463
1464 /*
1465 * Hand control over to libfuse.
1466 */
1467
1468#if 0
1469 /** @todo multithreaded fun. */
1470#else
1471 rc = fuse_main(argc, argv, &g_vboxfuseOps, NULL);
1472#endif
1473 RTPrintf("VBoxFUSE: fuse_main -> %d\n", rc);
1474 return rc;
1475}
1476
Note: See TracBrowser for help on using the repository browser.

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