VirtualBox

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

Last change on this file since 33944 was 33540, checked in by vboxsync, 14 years ago

*: spelling fixes, thanks Timeless!

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 44.3 KB
Line 
1/* $Id: VBoxFUSE.cpp 33540 2010-10-28 09:27:05Z vboxsync $ */
2/** @file
3 * VBoxFUSE - Disk Image Flattening FUSE Program.
4 */
5
6/*
7 * Copyright (C) 2009 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/VBoxHDD.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 rc = VDGetFormat(NULL /* pVDIIfsDisk */, pszImage, &pszFormat);
523 if (RT_FAILURE(rc))
524 {
525 LogRel(("VDGetFormat(%s,) failed, rc=%Rrc\n", pszImage, rc));
526 return rc;
527 }
528
529 PVBOXHDD pDisk = NULL;
530 rc = VDCreate(NULL /* pVDIIfsDisk */, &pDisk);
531 if (RT_SUCCESS(rc))
532 {
533 rc = VDOpen(pDisk, pszFormat, pszImage, 0, NULL /* pVDIfsImage */);
534 if (RT_FAILURE(rc))
535 {
536 LogRel(("VDCreate(,%s,%s,,,) failed, rc=%Rrc\n", pszFormat, pszImage, rc));
537 VDClose(pDisk, false /* fDeletes */);
538 }
539 }
540 else
541 Log(("VDCreate failed, rc=%Rrc\n", rc));
542 if (RT_FAILURE(rc))
543 {
544 RTStrFree(pszFormat);
545 return rc;
546 }
547
548 /*
549 * Allocate and initialize the new directory node.
550 */
551 rc = vboxfuseTreeLookupParentForInsert(pszPath, &pszName, &pParent);
552 if (RT_SUCCESS(rc))
553 {
554 PVBOXFUSEFLATIMAGE pNewFlatImage;
555 rc = vboxfuseNodeAlloc(sizeof(*pNewFlatImage), pszName, VBOXFUSETYPE_FLAT_IMAGE, pParent, (PVBOXFUSENODE *)&pNewFlatImage);
556 if (RT_SUCCESS(rc))
557 {
558 pNewFlatImage->pDisk = pDisk;
559 pNewFlatImage->pszFormat = pszFormat;
560 pNewFlatImage->cReaders = VDIsReadOnly(pNewFlatImage->pDisk) ? INT32_MAX / 2 : 0;
561 pNewFlatImage->cWriters = 0;
562 pNewFlatImage->Node.cbPrimary = VDGetSize(pNewFlatImage->pDisk, 0 /* base */);
563
564 /*
565 * Insert it.
566 */
567 rc = vboxfuseDirInsertChild(pParent, &pNewFlatImage->Node);
568 if ( RT_SUCCESS(rc)
569 && ppFile)
570 {
571 vboxfuseNodeLockAndRetain(&pNewFlatImage->Node);
572 *ppFile = pNewFlatImage;
573 }
574 vboxfuseNodeRelease(&pNewFlatImage->Node);
575 pDisk = NULL;
576 }
577 if (pParent)
578 vboxfuseNodeReleaseAndUnlock(&pParent->Node);
579 }
580 if (RT_FAILURE(rc) && pDisk != NULL)
581 VDClose(pDisk, false /* fDelete */);
582 return rc;
583}
584
585
586//static int vboxfuseTreeMkCtrlPipe(const char *pszPath, PVBOXFUSECTRLPIPE *ppPipe)
587//{
588//}
589
590
591/**
592 * Looks up a file in the tree.
593 *
594 * Upon successful return the returned node is both referenced and locked. The
595 * call will have to release and unlock it.
596 *
597 * @returns VBox status code
598 * @param pszPath The path to the file.
599 * @param ppNode Where to return the node.
600 */
601static int vboxfuseTreeLookup(const char *pszPath, PVBOXFUSENODE *ppNode)
602{
603 /*
604 * Root first.
605 */
606 const char *psz = pszPath;
607 if (*psz != '/')
608 return VERR_FILE_NOT_FOUND;
609
610 PVBOXFUSEDIR pDir = g_pTreeRoot;
611 vboxfuseNodeLockAndRetain(&pDir->Node);
612
613 do psz++;
614 while (*psz == '/');
615 if (!*psz)
616 {
617 /* looking for the root. */
618 *ppNode = &pDir->Node;
619 return VINF_SUCCESS;
620 }
621
622 /*
623 * Take it bit by bit from here on.
624 */
625 for (;;)
626 {
627 /*
628 * Find the length of the current directory entry and check if it must be file.
629 */
630 const char * const pszName = psz;
631 psz = strchr(psz, '/');
632 if (!psz)
633 psz = strchr(pszName, '\0');
634 size_t cchName = psz - pszName;
635
636 bool fMustBeDir = *psz == '/';
637 while (*psz == '/')
638 psz++;
639
640 /*
641 * Look it up.
642 * This is safe as the directory will hold a reference to each node
643 * so the nodes cannot possibly be destroyed while we're searching them.
644 */
645 PVBOXFUSENODE pNode = NULL;
646 uint32_t i = pDir->cEntries;
647 PVBOXFUSENODE *paEntries = pDir->paEntries;
648 while (i-- > 0)
649 {
650 PVBOXFUSENODE pCur = paEntries[i];
651 if ( pCur->cchName == cchName
652 && !memcmp(pCur->pszName, pszName, cchName))
653 {
654 pNode = pCur;
655 vboxfuseNodeLockAndRetain(pNode);
656 break;
657 }
658 }
659 vboxfuseNodeReleaseAndUnlock(&pDir->Node);
660
661 if (!pNode)
662 return *psz ? VERR_PATH_NOT_FOUND : VERR_FILE_NOT_FOUND;
663 if ( fMustBeDir
664 && pNode->enmType != VBOXFUSETYPE_DIRECTORY)
665 {
666 vboxfuseNodeReleaseAndUnlock(pNode);
667 return VERR_NOT_A_DIRECTORY;
668 }
669
670 /*
671 * Are we done?
672 */
673 if (!*psz)
674 {
675 *ppNode = pNode;
676 return VINF_SUCCESS;
677 }
678
679 /* advance */
680 pDir = (PVBOXFUSEDIR)pNode;
681 }
682}
683
684
685/**
686 * Errno conversion wrapper around vboxfuseTreeLookup().
687 *
688 * @returns 0 on success, negated errno on failure.
689 * @param pszPath The path to the file.
690 * @param ppNode Where to return the node.
691 */
692static int vboxfuseTreeLookupErrno(const char *pszPath, PVBOXFUSENODE *ppNode)
693{
694 int rc = vboxfuseTreeLookup(pszPath, ppNode);
695 if (RT_SUCCESS(rc))
696 return 0;
697 return -RTErrConvertToErrno(rc);
698}
699
700
701/**
702 * Looks up a parent directory in the tree.
703 *
704 * Upon successful return the returned directory is both referenced and locked.
705 * The call will have to release and unlock it.
706 *
707 * @returns VBox status code.
708 *
709 * @param pszPath The path to the file which parent we seek.
710 * @param ppszName Where to return the pointer to the child's name within
711 * pszPath.
712 * @param ppDir Where to return the parent directory.
713 */
714static int vboxfuseTreeLookupParent(const char *pszPath, const char **ppszName, PVBOXFUSEDIR *ppDir)
715{
716 /*
717 * Root first.
718 */
719 const char *psz = pszPath;
720 if (*psz != '/')
721 return VERR_INVALID_PARAMETER;
722 do psz++;
723 while (*psz == '/');
724 if (!*psz)
725 {
726 /* looking for the root. */
727 *ppszName = psz + 1;
728 *ppDir = NULL;
729 return VINF_SUCCESS;
730 }
731
732 /*
733 * Take it bit by bit from here on.
734 */
735 PVBOXFUSEDIR pDir = g_pTreeRoot;
736 AssertReturn(pDir, VERR_WRONG_ORDER);
737 vboxfuseNodeLockAndRetain(&pDir->Node);
738 for (;;)
739 {
740 /*
741 * Find the length of the current directory entry and check if it must be file.
742 */
743 const char * const pszName = psz;
744 psz = strchr(psz, '/');
745 if (!psz)
746 {
747 /* that's all folks.*/
748 *ppszName = pszName;
749 *ppDir = pDir;
750 return VINF_SUCCESS;
751 }
752 size_t cchName = psz - pszName;
753
754 bool fMustBeDir = *psz == '/';
755 while (*psz == '/')
756 psz++;
757
758 /* Trailing slashes are not allowed (because it's simpler without them). */
759 if (!*psz)
760 return VERR_INVALID_PARAMETER;
761
762 /*
763 * Look it up.
764 * This is safe as the directory will hold a reference to each node
765 * so the nodes cannot possibly be destroyed while we're searching them.
766 */
767 PVBOXFUSENODE pNode = NULL;
768 uint32_t i = pDir->cEntries;
769 PVBOXFUSENODE *paEntries = pDir->paEntries;
770 while (i-- > 0)
771 {
772 PVBOXFUSENODE pCur = paEntries[i];
773 if ( pCur->cchName == cchName
774 && !memcmp(pCur->pszName, pszName, cchName))
775 {
776 pNode = pCur;
777 vboxfuseNodeLockAndRetain(pNode);
778 break;
779 }
780 }
781 vboxfuseNodeReleaseAndUnlock(&pDir->Node);
782
783 if (!pNode)
784 return VERR_FILE_NOT_FOUND;
785 if ( fMustBeDir
786 && pNode->enmType != VBOXFUSETYPE_DIRECTORY)
787 {
788 vboxfuseNodeReleaseAndUnlock(pNode);
789 return VERR_PATH_NOT_FOUND;
790 }
791
792 /* advance */
793 pDir = (PVBOXFUSEDIR)pNode;
794 }
795}
796
797
798/**
799 * Looks up a parent directory in the tree and checking that the specified child
800 * doesn't already exist.
801 *
802 * Upon successful return the returned directory is both referenced and locked.
803 * The call will have to release and unlock it.
804 *
805 * @returns VBox status code.
806 *
807 * @param pszPath The path to the file which parent we seek.
808 * @param ppszName Where to return the pointer to the child's name within
809 * pszPath.
810 * @param ppDir Where to return the parent directory.
811 */
812static int vboxfuseTreeLookupParentForInsert(const char *pszPath, const char **ppszName, PVBOXFUSEDIR *ppDir)
813{
814 /*
815 * Lookup the parent directory using vboxfuseTreeLookupParent first.
816 */
817 const char *pszName;
818 PVBOXFUSEDIR pDir;
819 int rc = vboxfuseTreeLookupParent(pszPath, &pszName, &pDir);
820 if (RT_SUCCESS(rc))
821 {
822 /*
823 * Check that it doesn't exist already
824 */
825 if (pDir)
826 {
827 size_t const cchName = strlen(pszName);
828 uint32_t i = pDir->cEntries;
829 PVBOXFUSENODE *paEntries = pDir->paEntries;
830 while (i-- > 0)
831 {
832 PVBOXFUSENODE pCur = paEntries[i];
833 if ( pCur->cchName == cchName
834 && !memcmp(pCur->pszName, pszName, cchName))
835 {
836 vboxfuseNodeReleaseAndUnlock(&pDir->Node);
837 rc = VERR_ALREADY_EXISTS;
838 break;
839 }
840 }
841 }
842 if (RT_SUCCESS(rc))
843 {
844 *ppDir = pDir;
845 *ppszName = pszName;
846 }
847 }
848 return rc;
849}
850
851
852
853
854
855/** @copydoc fuse_operations::getattr */
856static int vboxfuseOp_getattr(const char *pszPath, struct stat *pStat)
857{
858 PVBOXFUSENODE pNode;
859 int rc = vboxfuseTreeLookupErrno(pszPath, &pNode);
860 if (!rc)
861 {
862 vboxfuseNodeFillStat(pNode, pStat);
863 vboxfuseNodeReleaseAndUnlock(pNode);
864 }
865 LogFlow(("vboxfuseOp_getattr: rc=%d \"%s\"\n", rc, pszPath));
866 return rc;
867}
868
869
870/** @copydoc fuse_operations::opendir */
871static int vboxfuseOp_opendir(const char *pszPath, struct fuse_file_info *pInfo)
872{
873 PVBOXFUSENODE pNode;
874 int rc = vboxfuseTreeLookupErrno(pszPath, &pNode);
875 if (!rc)
876 {
877 /*
878 * Check that it's a directory and that the caller should see it.
879 */
880 if (pNode->enmType != VBOXFUSETYPE_DIRECTORY)
881 rc = -ENOTDIR;
882 /** @todo access checks. */
883 else
884 {
885 /** @todo update the accessed TS? */
886
887 /*
888 * Put a reference to the node in the fuse_file_info::fh member so
889 * we don't have to parse the path in readdir.
890 */
891 pInfo->fh = (uintptr_t)pNode;
892 vboxfuseNodeUnlock(pNode);
893 }
894
895 /* cleanup */
896 if (rc)
897 vboxfuseNodeReleaseAndUnlock(pNode);
898 }
899 LogFlow(("vboxfuseOp_opendir: rc=%d \"%s\"\n", rc, pszPath));
900 return rc;
901}
902
903
904/** @copydoc fuse_operations::readdir */
905static int vboxfuseOp_readdir(const char *pszPath, void *pvBuf, fuse_fill_dir_t pfnFiller,
906 off_t offDir, struct fuse_file_info *pInfo)
907{
908 PVBOXFUSEDIR pDir = (PVBOXFUSEDIR)(uintptr_t)pInfo->fh;
909 AssertPtr(pDir);
910 Assert(pDir->Node.enmType == VBOXFUSETYPE_DIRECTORY);
911 vboxfuseNodeLock(&pDir->Node);
912 LogFlow(("vboxfuseOp_readdir: offDir=%llx \"%s\"\n", (uint64_t)offDir, pszPath));
913
914#define VBOXFUSE_FAKE_DIRENT_SIZE 512
915
916 /*
917 * First the mandatory dot and dot-dot entries.
918 */
919 struct stat st;
920 int rc = 0;
921 if (!offDir)
922 {
923 offDir += VBOXFUSE_FAKE_DIRENT_SIZE;
924 vboxfuseNodeFillStat(&pDir->Node, &st);
925 rc = pfnFiller(pvBuf, ".", &st, offDir);
926 }
927 if ( offDir == VBOXFUSE_FAKE_DIRENT_SIZE
928 && !rc)
929 {
930 offDir += VBOXFUSE_FAKE_DIRENT_SIZE;
931 rc = pfnFiller(pvBuf, "..", NULL, offDir);
932 }
933
934 /*
935 * Convert the offset to a directory index and start/continue filling the buffer.
936 * The entries only needs locking since the directory already has a reference
937 * to each of them.
938 */
939 Assert(offDir >= VBOXFUSE_FAKE_DIRENT_SIZE * 2 || rc);
940 uint32_t i = offDir / VBOXFUSE_FAKE_DIRENT_SIZE - 2;
941 while ( !rc
942 && i < pDir->cEntries)
943 {
944 PVBOXFUSENODE pNode = pDir->paEntries[i];
945 vboxfuseNodeLock(pNode);
946
947 vboxfuseNodeFillStat(pNode, &st);
948 offDir = (i + 3) * VBOXFUSE_FAKE_DIRENT_SIZE;
949 rc = pfnFiller(pvBuf, pNode->pszName, &st, offDir);
950
951 vboxfuseNodeUnlock(pNode);
952
953 /* next */
954 i++;
955 }
956
957 vboxfuseNodeUnlock(&pDir->Node);
958 LogFlow(("vboxfuseOp_readdir: returns offDir=%llx\n", (uint64_t)offDir));
959 return 0;
960}
961
962
963/** @copydoc fuse_operations::releasedir */
964static int vboxfuseOp_releasedir(const char *pszPath, struct fuse_file_info *pInfo)
965{
966 PVBOXFUSEDIR pDir = (PVBOXFUSEDIR)(uintptr_t)pInfo->fh;
967 AssertPtr(pDir);
968 Assert(pDir->Node.enmType == VBOXFUSETYPE_DIRECTORY);
969 pInfo->fh = 0;
970 vboxfuseNodeRelease(&pDir->Node);
971 LogFlow(("vboxfuseOp_releasedir: \"%s\"\n", pszPath));
972 return 0;
973}
974
975
976/** @copydoc fuse_operations::symlink */
977static int vboxfuseOp_symlink(const char *pszDst, const char *pszPath)
978{
979 /*
980 * "Interface" for mounting a image.
981 */
982 int rc = vboxfuseFlatImageCreate(pszPath, pszDst, NULL);
983 if (RT_SUCCESS(rc))
984 {
985 Log(("vboxfuseOp_symlink: \"%s\" => \"%s\" SUCCESS!\n", pszPath, pszDst));
986 return 0;
987 }
988
989 LogFlow(("vboxfuseOp_symlink: \"%s\" => \"%s\" rc=%Rrc\n", pszPath, pszDst, rc));
990 return -RTErrConvertToErrno(rc);
991}
992
993
994/** @copydoc fuse_operations::open */
995static int vboxfuseOp_open(const char *pszPath, struct fuse_file_info *pInfo)
996{
997 LogFlow(("vboxfuseOp_open(\"%s\", .flags=%#x)\n", pszPath, pInfo->flags));
998
999 /*
1000 * Validate incoming flags.
1001 */
1002#ifdef RT_OS_DARWIN
1003 if (pInfo->flags & (O_APPEND | O_NONBLOCK | O_SYMLINK | O_NOCTTY | O_SHLOCK | O_EXLOCK | O_ASYNC
1004 | O_CREAT | O_TRUNC | O_EXCL | O_EVTONLY))
1005 return -EINVAL;
1006 if ((pInfo->flags & O_ACCMODE) == O_ACCMODE)
1007 return -EINVAL;
1008#elif defined(RT_OS_LINUX)
1009 if (pInfo->flags & ( O_APPEND | O_ASYNC | O_DIRECT /* | O_LARGEFILE ? */
1010 | O_NOATIME | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK
1011 /* | O_SYNC ? */))
1012 return -EINVAL;
1013 if ((pInfo->flags & O_ACCMODE) == O_ACCMODE)
1014 return -EINVAL;
1015#elif defined(RT_OS_FREEBSD)
1016 if (pInfo->flags & ( O_APPEND | O_ASYNC | O_DIRECT /* | O_LARGEFILE ? */
1017 | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK
1018 /* | O_SYNC ? */))
1019 return -EINVAL;
1020 if ((pInfo->flags & O_ACCMODE) == O_ACCMODE)
1021 return -EINVAL;
1022#else
1023# error "Port me"
1024#endif
1025
1026 PVBOXFUSENODE pNode;
1027 int rc = vboxfuseTreeLookupErrno(pszPath, &pNode);
1028 if (!rc)
1029 {
1030 /*
1031 * Check flags and stuff.
1032 */
1033 switch (pNode->enmType)
1034 {
1035 /* not expected here? */
1036 case VBOXFUSETYPE_DIRECTORY:
1037 AssertFailed();
1038 rc = -EISDIR;
1039 break;
1040
1041 case VBOXFUSETYPE_FLAT_IMAGE:
1042 {
1043 PVBOXFUSEFLATIMAGE pFlatImage = (PVBOXFUSEFLATIMAGE)pNode;
1044#ifdef O_DIRECTORY
1045 if (pInfo->flags & O_DIRECTORY)
1046 rc = -ENOTDIR;
1047#endif
1048 if ( (pInfo->flags & O_ACCMODE) == O_WRONLY
1049 || (pInfo->flags & O_ACCMODE) == O_RDWR)
1050 {
1051 if ( pFlatImage->cWriters == 0
1052 && pFlatImage->cReaders == 0)
1053 pFlatImage->cWriters++;
1054 else
1055 rc = -ETXTBSY;
1056 }
1057 else if ((pInfo->flags & O_ACCMODE) == O_RDONLY)
1058 {
1059 if (pFlatImage->cWriters == 0)
1060 {
1061 if (pFlatImage->cReaders + 1 < ( pFlatImage->cReaders < INT32_MAX / 2
1062 ? INT32_MAX / 4
1063 : INT32_MAX / 2 + INT32_MAX / 4) )
1064 pFlatImage->cReaders++;
1065 else
1066 rc = -EMLINK;
1067 }
1068 else
1069 rc = -ETXTBSY;
1070 }
1071 break;
1072 }
1073
1074 case VBOXFUSETYPE_CONTROL_PIPE:
1075 rc = -ENOTSUP;
1076 break;
1077
1078 default:
1079 rc = -EDOOFUS;
1080 break;
1081 }
1082 if (!rc)
1083 {
1084 /*
1085 * Put a reference to the node in the fuse_file_info::fh member so
1086 * we don't have to parse the path in the other file methods.
1087 */
1088 pInfo->fh = (uintptr_t)pNode;
1089 vboxfuseNodeUnlock(pNode);
1090 }
1091 else
1092 {
1093 /* cleanup */
1094 vboxfuseNodeReleaseAndUnlock(pNode);
1095 }
1096 }
1097 LogFlow(("vboxfuseOp_opendir: rc=%d \"%s\"\n", rc, pszPath));
1098 return rc;
1099}
1100
1101
1102/** @copydoc fuse_operations::release */
1103static int vboxfuseOp_release(const char *pszPath, struct fuse_file_info *pInfo)
1104{
1105 PVBOXFUSENODE pNode = (PVBOXFUSENODE)(uintptr_t)pInfo->fh;
1106 AssertPtr(pNode);
1107 pInfo->fh = 0;
1108
1109 switch (pNode->enmType)
1110 {
1111 case VBOXFUSETYPE_DIRECTORY:
1112 /* nothing to do */
1113 vboxfuseNodeRelease(pNode);
1114 break;
1115
1116 case VBOXFUSETYPE_FLAT_IMAGE:
1117 {
1118 PVBOXFUSEFLATIMAGE pFlatImage = (PVBOXFUSEFLATIMAGE)pNode;
1119 vboxfuseNodeLock(&pFlatImage->Node);
1120
1121 if ( (pInfo->flags & O_ACCMODE) == O_WRONLY
1122 || (pInfo->flags & O_ACCMODE) == O_RDWR)
1123 {
1124 pFlatImage->cWriters--;
1125 Assert(pFlatImage->cWriters >= 0);
1126 }
1127 else if ((pInfo->flags & O_ACCMODE) == O_RDONLY)
1128 {
1129 pFlatImage->cReaders--;
1130 Assert(pFlatImage->cReaders >= 0);
1131 }
1132 else
1133 AssertFailed();
1134
1135 vboxfuseNodeReleaseAndUnlock(&pFlatImage->Node);
1136 break;
1137 }
1138
1139 case VBOXFUSETYPE_CONTROL_PIPE:
1140 /* nothing to do yet */
1141 vboxfuseNodeRelease(pNode);
1142 break;
1143
1144 default:
1145 AssertMsgFailed(("%s\n", pszPath));
1146 return -EDOOFUS;
1147 }
1148
1149 LogFlow(("vboxfuseOp_release: \"%s\"\n", pszPath));
1150 return 0;
1151}
1152
1153/** The VDRead/VDWrite block granularity. */
1154#define VBOXFUSE_MIN_SIZE 512
1155/** Offset mask corresponding to VBOXFUSE_MIN_SIZE. */
1156#define VBOXFUSE_MIN_SIZE_MASK_OFF (0x1ff)
1157/** Block mask corresponding to VBOXFUSE_MIN_SIZE. */
1158#define VBOXFUSE_MIN_SIZE_MASK_BLK (~UINT64_C(0x1ff))
1159
1160/** @copydoc fuse_operations::read */
1161static int vboxfuseOp_read(const char *pszPath, char *pbBuf, size_t cbBuf,
1162 off_t offFile, struct fuse_file_info *pInfo)
1163{
1164 /* paranoia */
1165 AssertReturn((int)cbBuf >= 0, -EINVAL);
1166 AssertReturn((unsigned)cbBuf == cbBuf, -EINVAL);
1167 AssertReturn(offFile >= 0, -EINVAL);
1168 AssertReturn((off_t)(offFile + cbBuf) >= offFile, -EINVAL);
1169
1170 PVBOXFUSENODE pNode = (PVBOXFUSENODE)(uintptr_t)pInfo->fh;
1171 AssertPtr(pNode);
1172 switch (pNode->enmType)
1173 {
1174 case VBOXFUSETYPE_DIRECTORY:
1175 return -ENOTSUP;
1176
1177 case VBOXFUSETYPE_FLAT_IMAGE:
1178 {
1179 PVBOXFUSEFLATIMAGE pFlatImage = (PVBOXFUSEFLATIMAGE)(uintptr_t)pInfo->fh;
1180 LogFlow(("vboxfuseOp_read: offFile=%#llx cbBuf=%#zx pszPath=\"%s\"\n", (uint64_t)offFile, cbBuf, pszPath));
1181 vboxfuseNodeLock(&pFlatImage->Node);
1182
1183 int rc;
1184 if ((off_t)(offFile + cbBuf) < offFile)
1185 rc = -EINVAL;
1186 else if (offFile >= pFlatImage->Node.cbPrimary)
1187 rc = 0;
1188 else if (!cbBuf)
1189 rc = 0;
1190 else
1191 {
1192 /* Adjust for EOF. */
1193 if ((off_t)(offFile + cbBuf) >= pFlatImage->Node.cbPrimary)
1194 cbBuf = pFlatImage->Node.cbPrimary - offFile;
1195
1196 /*
1197 * Aligned read?
1198 */
1199 int rc2;
1200 if ( !(offFile & VBOXFUSE_MIN_SIZE_MASK_OFF)
1201 && !(cbBuf & VBOXFUSE_MIN_SIZE_MASK_OFF))
1202 rc2 = VDRead(pFlatImage->pDisk, offFile, pbBuf, cbBuf);
1203 else
1204 {
1205 /*
1206 * Unaligned read - lots of extra work.
1207 */
1208 uint8_t abBlock[VBOXFUSE_MIN_SIZE];
1209 if (((offFile + cbBuf) & VBOXFUSE_MIN_SIZE_MASK_BLK) == (offFile & VBOXFUSE_MIN_SIZE_MASK_BLK))
1210 {
1211 /* a single partial block. */
1212 rc2 = VDRead(pFlatImage->pDisk, offFile & VBOXFUSE_MIN_SIZE_MASK_BLK, abBlock, VBOXFUSE_MIN_SIZE);
1213 if (RT_SUCCESS(rc2))
1214 memcpy(pbBuf, &abBlock[offFile & VBOXFUSE_MIN_SIZE_MASK_OFF], cbBuf);
1215 }
1216 else
1217 {
1218 /* read unaligned head. */
1219 rc2 = VINF_SUCCESS;
1220 if (offFile & VBOXFUSE_MIN_SIZE_MASK_OFF)
1221 {
1222 rc2 = VDRead(pFlatImage->pDisk, offFile & VBOXFUSE_MIN_SIZE_MASK_BLK, abBlock, VBOXFUSE_MIN_SIZE);
1223 if (RT_SUCCESS(rc2))
1224 {
1225 size_t cbCopy = VBOXFUSE_MIN_SIZE - (offFile & VBOXFUSE_MIN_SIZE_MASK_OFF);
1226 memcpy(pbBuf, &abBlock[offFile & VBOXFUSE_MIN_SIZE_MASK_OFF], cbCopy);
1227 pbBuf += cbCopy;
1228 offFile += cbCopy;
1229 cbBuf -= cbCopy;
1230 }
1231 }
1232
1233 /* read the middle. */
1234 Assert(!(offFile & VBOXFUSE_MIN_SIZE_MASK_OFF));
1235 if (cbBuf >= VBOXFUSE_MIN_SIZE && RT_SUCCESS(rc2))
1236 {
1237 size_t cbRead = cbBuf & VBOXFUSE_MIN_SIZE_MASK_BLK;
1238 rc2 = VDRead(pFlatImage->pDisk, offFile, pbBuf, cbRead);
1239 if (RT_SUCCESS(rc2))
1240 {
1241 pbBuf += cbRead;
1242 offFile += cbRead;
1243 cbBuf -= cbRead;
1244 }
1245 }
1246
1247 /* unaligned tail read. */
1248 Assert(cbBuf < VBOXFUSE_MIN_SIZE);
1249 Assert(!(offFile & VBOXFUSE_MIN_SIZE_MASK_OFF));
1250 if (cbBuf && RT_SUCCESS(rc2))
1251 {
1252 rc2 = VDRead(pFlatImage->pDisk, offFile, abBlock, VBOXFUSE_MIN_SIZE);
1253 if (RT_SUCCESS(rc2))
1254 memcpy(pbBuf, &abBlock[0], cbBuf);
1255 }
1256 }
1257 }
1258
1259 /* convert the return code */
1260 if (RT_SUCCESS(rc2))
1261 rc = cbBuf;
1262 else
1263 rc = -RTErrConvertToErrno(rc2);
1264 }
1265
1266 vboxfuseNodeUnlock(&pFlatImage->Node);
1267 return rc;
1268 }
1269
1270 case VBOXFUSETYPE_CONTROL_PIPE:
1271 return -ENOTSUP;
1272
1273 default:
1274 AssertMsgFailed(("%s\n", pszPath));
1275 return -EDOOFUS;
1276 }
1277}
1278
1279
1280/** @copydoc fuse_operations::write */
1281static int vboxfuseOp_write(const char *pszPath, const char *pbBuf, size_t cbBuf,
1282 off_t offFile, struct fuse_file_info *pInfo)
1283{
1284 /* paranoia */
1285 AssertReturn((int)cbBuf >= 0, -EINVAL);
1286 AssertReturn((unsigned)cbBuf == cbBuf, -EINVAL);
1287 AssertReturn(offFile >= 0, -EINVAL);
1288 AssertReturn((off_t)(offFile + cbBuf) >= offFile, -EINVAL);
1289
1290 PVBOXFUSENODE pNode = (PVBOXFUSENODE)(uintptr_t)pInfo->fh;
1291 AssertPtr(pNode);
1292 switch (pNode->enmType)
1293 {
1294 case VBOXFUSETYPE_DIRECTORY:
1295 return -ENOTSUP;
1296
1297 case VBOXFUSETYPE_FLAT_IMAGE:
1298 {
1299 PVBOXFUSEFLATIMAGE pFlatImage = (PVBOXFUSEFLATIMAGE)(uintptr_t)pInfo->fh;
1300 LogFlow(("vboxfuseOp_write: offFile=%#llx cbBuf=%#zx pszPath=\"%s\"\n", (uint64_t)offFile, cbBuf, pszPath));
1301 vboxfuseNodeLock(&pFlatImage->Node);
1302
1303 int rc;
1304 if ((off_t)(offFile + cbBuf) < offFile)
1305 rc = -EINVAL;
1306 else if (offFile >= pFlatImage->Node.cbPrimary)
1307 rc = 0;
1308 else if (!cbBuf)
1309 rc = 0;
1310 else
1311 {
1312 /* Adjust for EOF. */
1313 if ((off_t)(offFile + cbBuf) >= pFlatImage->Node.cbPrimary)
1314 cbBuf = pFlatImage->Node.cbPrimary - offFile;
1315
1316 /*
1317 * Aligned write?
1318 */
1319 int rc2;
1320 if ( !(offFile & VBOXFUSE_MIN_SIZE_MASK_OFF)
1321 && !(cbBuf & VBOXFUSE_MIN_SIZE_MASK_OFF))
1322 rc2 = VDWrite(pFlatImage->pDisk, offFile, pbBuf, cbBuf);
1323 else
1324 {
1325 /*
1326 * Unaligned write - lots of extra work.
1327 */
1328 uint8_t abBlock[VBOXFUSE_MIN_SIZE];
1329 if (((offFile + cbBuf) & VBOXFUSE_MIN_SIZE_MASK_BLK) == (offFile & VBOXFUSE_MIN_SIZE_MASK_BLK))
1330 {
1331 /* a single partial block. */
1332 rc2 = VDRead(pFlatImage->pDisk, offFile & VBOXFUSE_MIN_SIZE_MASK_BLK, abBlock, VBOXFUSE_MIN_SIZE);
1333 if (RT_SUCCESS(rc2))
1334 {
1335 memcpy(&abBlock[offFile & VBOXFUSE_MIN_SIZE_MASK_OFF], pbBuf, cbBuf);
1336 /* Update the block */
1337 rc2 = VDWrite(pFlatImage->pDisk, offFile & VBOXFUSE_MIN_SIZE_MASK_BLK, abBlock, VBOXFUSE_MIN_SIZE);
1338 }
1339 }
1340 else
1341 {
1342 /* read unaligned head. */
1343 rc2 = VINF_SUCCESS;
1344 if (offFile & VBOXFUSE_MIN_SIZE_MASK_OFF)
1345 {
1346 rc2 = VDRead(pFlatImage->pDisk, offFile & VBOXFUSE_MIN_SIZE_MASK_BLK, abBlock, VBOXFUSE_MIN_SIZE);
1347 if (RT_SUCCESS(rc2))
1348 {
1349 size_t cbCopy = VBOXFUSE_MIN_SIZE - (offFile & VBOXFUSE_MIN_SIZE_MASK_OFF);
1350 memcpy(&abBlock[offFile & VBOXFUSE_MIN_SIZE_MASK_OFF], pbBuf, cbCopy);
1351 pbBuf += cbCopy;
1352 offFile += cbCopy;
1353 cbBuf -= cbCopy;
1354 rc2 = VDWrite(pFlatImage->pDisk, offFile & VBOXFUSE_MIN_SIZE_MASK_BLK, abBlock, VBOXFUSE_MIN_SIZE);
1355 }
1356 }
1357
1358 /* write the middle. */
1359 Assert(!(offFile & VBOXFUSE_MIN_SIZE_MASK_OFF));
1360 if (cbBuf >= VBOXFUSE_MIN_SIZE && RT_SUCCESS(rc2))
1361 {
1362 size_t cbWrite = cbBuf & VBOXFUSE_MIN_SIZE_MASK_BLK;
1363 rc2 = VDWrite(pFlatImage->pDisk, offFile, pbBuf, cbWrite);
1364 if (RT_SUCCESS(rc2))
1365 {
1366 pbBuf += cbWrite;
1367 offFile += cbWrite;
1368 cbBuf -= cbWrite;
1369 }
1370 }
1371
1372 /* unaligned tail write. */
1373 Assert(cbBuf < VBOXFUSE_MIN_SIZE);
1374 Assert(!(offFile & VBOXFUSE_MIN_SIZE_MASK_OFF));
1375 if (cbBuf && RT_SUCCESS(rc2))
1376 {
1377 rc2 = VDRead(pFlatImage->pDisk, offFile, abBlock, VBOXFUSE_MIN_SIZE);
1378 if (RT_SUCCESS(rc2))
1379 {
1380 memcpy(&abBlock[0], pbBuf, cbBuf);
1381 rc2 = VDWrite(pFlatImage->pDisk, offFile, abBlock, VBOXFUSE_MIN_SIZE);
1382 }
1383 }
1384 }
1385 }
1386
1387 /* convert the return code */
1388 if (RT_SUCCESS(rc2))
1389 rc = cbBuf;
1390 else
1391 rc = -RTErrConvertToErrno(rc2);
1392 }
1393
1394 vboxfuseNodeUnlock(&pFlatImage->Node);
1395 return rc;
1396 }
1397
1398 case VBOXFUSETYPE_CONTROL_PIPE:
1399 return -ENOTSUP;
1400
1401 default:
1402 AssertMsgFailed(("%s\n", pszPath));
1403 return -EDOOFUS;
1404 }
1405}
1406
1407
1408/**
1409 * The FUSE operations.
1410 *
1411 * @remarks We'll initialize this manually since we cannot use C99 style
1412 * initialzer designations in C++ (yet).
1413 */
1414static struct fuse_operations g_vboxfuseOps;
1415
1416
1417
1418int main(int argc, char **argv)
1419{
1420 /*
1421 * Initialize the runtime and VD.
1422 */
1423 int rc = RTR3Init();
1424 if (RT_FAILURE(rc))
1425 {
1426 RTStrmPrintf(g_pStdErr, "VBoxFUSE: RTR3Init failed, rc=%Rrc\n", rc);
1427 return 1;
1428 }
1429 RTPrintf("VBoxFUSE: Hello...\n");
1430 rc = VDInit();
1431 if (RT_FAILURE(rc))
1432 {
1433 RTStrmPrintf(g_pStdErr, "VBoxFUSE: VDInit failed, rc=%Rrc\n", rc);
1434 return 1;
1435 }
1436
1437 /*
1438 * Initializes the globals and populate the file hierarchy.
1439 */
1440 rc = vboxfuseDirCreate("/", NULL);
1441 if (RT_SUCCESS(rc))
1442 rc = vboxfuseDirCreate("/FlattenedImages", NULL);
1443 if (RT_FAILURE(rc))
1444 {
1445 RTStrmPrintf(g_pStdErr, "VBoxFUSE: vboxfuseDirCreate failed, rc=%Rrc\n", rc);
1446 return 1;
1447 }
1448
1449 /*
1450 * Initialize the g_vboxfuseOps. (C++ sucks!)
1451 */
1452 memset(&g_vboxfuseOps, 0, sizeof(g_vboxfuseOps));
1453 g_vboxfuseOps.getattr = vboxfuseOp_getattr;
1454 g_vboxfuseOps.opendir = vboxfuseOp_opendir;
1455 g_vboxfuseOps.readdir = vboxfuseOp_readdir;
1456 g_vboxfuseOps.releasedir = vboxfuseOp_releasedir;
1457 g_vboxfuseOps.symlink = vboxfuseOp_symlink;
1458 g_vboxfuseOps.open = vboxfuseOp_open;
1459 g_vboxfuseOps.read = vboxfuseOp_read;
1460 g_vboxfuseOps.write = vboxfuseOp_write;
1461 g_vboxfuseOps.release = vboxfuseOp_release;
1462
1463 /*
1464 * Hand control over to libfuse.
1465 */
1466
1467#if 0
1468 /** @todo multithreaded fun. */
1469#else
1470 rc = fuse_main(argc, argv, &g_vboxfuseOps, NULL);
1471#endif
1472 RTPrintf("VBoxFUSE: fuse_main -> %d\n", rc);
1473 return rc;
1474}
1475
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