VirtualBox

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

Last change on this file since 80224 was 79965, checked in by vboxsync, 6 years ago

Storage: Added a desired format parameter to VDGetFormat() so Main can pass along the device type. Bumped the RAW backend down after the CUE and VISO to prevent (impossible) mixups of tiny files. Extended rawProbe() to look for a valid ISO-9660/UDF descriptor sequence on the image if the caller doesn't say it should be a floppy or hdd, only if that fail go by the extension. Note! the pfnProbe callback should probably get a score return parameter to indicate how confient the backend is about the probe result, as this would prevent the RAW backend from accidentally grabbing images which belongs to a plug-in. bugref:9151

  • 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 79965 2019-07-24 20:32:32Z 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, VDTYPE_INVALID,&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.

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