VirtualBox

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

Last change on this file since 21134 was 21129, checked in by vboxsync, 16 years ago

VBoxFUSE: Export to OSE.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.3 KB
Line 
1/* $Id: VBoxFUSE.cpp 21129 2009-07-01 14:49:43Z vboxsync $ */
2/** @file
3 * VBoxFUSE - Disk Image Flattening FUSE Program.
4 */
5
6/*
7 * Copyright (C) 2009 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_DEFAULT /** @todo log group */
27#include <iprt/types.h>
28
29#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
30# include <sys/param.h>
31# undef PVM /* Blasted old BSD mess still hanging around darwin. */
32#endif
33#define FUSE_USE_VERSION 27
34#include <fuse.h>
35#include <errno.h>
36#include <fcntl.h>
37#ifndef EDOOFUS
38# ifdef EBADMACHO
39# define EDOOFUS EBADMACHO
40//# elif defined(EXYZ)
41//# define EDOOFUS EXYZ
42# else
43# error "Choose an unlikely and (if possible) fun error number for EDOOFUS."
44# endif
45#endif
46
47#include <VBox/VBoxHDD.h>
48#include <VBox/log.h>
49#include <VBox/err.h>
50#include <iprt/critsect.h>
51#include <iprt/assert.h>
52#include <iprt/asm.h>
53#include <iprt/mem.h>
54#include <iprt/string.h>
55#include <iprt/initterm.h>
56#include <iprt/stream.h>
57
58
59/*******************************************************************************
60* Structures and Typedefs *
61*******************************************************************************/
62/**
63 * Node type.
64 */
65typedef enum VBOXFUSETYPE
66{
67 VBOXFUSETYPE_INVALID = 0,
68 VBOXFUSETYPE_DIRECTORY,
69 VBOXFUSETYPE_FLAT_IMAGE,
70 VBOXFUSETYPE_CONTROL_PIPE
71} VBOXFUSETYPE;
72
73/**
74 * Stuff common to both directories and files.
75 */
76typedef struct VBOXFUSENODE
77{
78 /** The directory name. */
79 const char *pszName;
80 /** The name length. */
81 size_t cchName;
82 /** The node type. */
83 VBOXFUSETYPE enmType;
84 /** The number of references.
85 * The directory linking this node will always retain one. */
86 int32_t volatile cRefs;
87 /** Critical section serializing access to the node data. */
88 RTCRITSECT CritSect;
89 /** Pointer to the directory (parent). */
90 struct VBOXFUSEDIR *pDir;
91 /** The mode mask. */
92 RTFMODE fMode;
93 /** The User ID of the directory owner. */
94 RTUID Uid;
95 /** The Group ID of the directory. */
96 RTUID Gid;
97 /** The link count. */
98 uint32_t cLinks;
99 /** The inode number. */
100 RTINODE Ino;
101 /** The size of the primary stream. */
102 RTFOFF cbPrimary;
103} VBOXFUSENODE;
104typedef VBOXFUSENODE *PVBOXFUSENODE;
105
106/**
107 * A flat image file.
108 */
109typedef struct VBOXFUSEFLATIMAGE
110{
111 /** The standard bits. */
112 VBOXFUSENODE Node;
113 /** The virtual disk container. */
114 PVBOXHDD pDisk;
115 /** The format name. */
116 char *pszFormat;
117 /** The number of readers.
118 * Read only images will have this set to INT32_MAX/2 on creation. */
119 int32_t cReaders;
120 /** The number of writers. (Just 1 or 0 really.) */
121 int32_t cWriters;
122} VBOXFUSEFLATIMAGE;
123typedef VBOXFUSEFLATIMAGE *PVBOXFUSEFLATIMAGE;
124
125/**
126 * A control pipe (file).
127 */
128typedef struct VBOXFUSECTRLPIPE
129{
130 /** The standard bits. */
131 VBOXFUSENODE Node;
132} VBOXFUSECTRLPIPE;
133typedef VBOXFUSECTRLPIPE *PVBOXFUSECTRLPIPE;
134
135
136/**
137 * A Directory.
138 *
139 * This is just a container of files and subdirectories, nothing special.
140 */
141typedef struct VBOXFUSEDIR
142{
143 /** The standard bits. */
144 VBOXFUSENODE Node;
145 /** The number of directory entries. */
146 uint32_t cEntries;
147 /** Array of pointers to directory entries.
148 * Whether what's being pointed to is a file, directory or something else can be
149 * determined by the enmType field. */
150 PVBOXFUSENODE *paEntries;
151} VBOXFUSEDIR;
152typedef VBOXFUSEDIR *PVBOXFUSEDIR;
153
154/** The number of elements to grow VBOXFUSEDIR::paEntries by. */
155#define VBOXFUSE_DIR_GROW_BY 2 /* 32 */
156
157
158/*******************************************************************************
159* Global Variables *
160*******************************************************************************/
161/** The root of the file hierarchy. */
162static VBOXFUSEDIR *g_pTreeRoot;
163/** The next inode number. */
164static RTINODE volatile g_NextIno = 1;
165
166
167/*******************************************************************************
168* Internal Functions *
169*******************************************************************************/
170static int vboxfuseTreeLookupParent(const char *pszPath, const char **ppszName, PVBOXFUSEDIR *ppDir);
171static int vboxfuseTreeLookupParentForInsert(const char *pszPath, const char **ppszName, PVBOXFUSEDIR *ppDir);
172
173
174/**
175 * Node destructor.
176 *
177 * @returns true.
178 * @param pNode The node.
179 * @param fLocked Whether it's locked.
180 */
181static bool vboxfuseNodeDestroy(PVBOXFUSENODE pNode, bool fLocked)
182{
183 Assert(pNode->cRefs == 0);
184
185 /*
186 * Type specific cleanups.
187 */
188 switch (pNode->enmType)
189 {
190 case VBOXFUSETYPE_DIRECTORY:
191 {
192 PVBOXFUSEDIR pDir = (PVBOXFUSEDIR)pNode;
193 RTMemFree(pDir->paEntries);
194 pDir->paEntries = NULL;
195 pDir->cEntries = 0;
196 break;
197 }
198
199 case VBOXFUSETYPE_FLAT_IMAGE:
200 {
201 PVBOXFUSEFLATIMAGE pFlatImage = (PVBOXFUSEFLATIMAGE)pNode;
202 if (pFlatImage->pDisk)
203 {
204 int rc2 = VDClose(pFlatImage->pDisk, false /* fDelete */); AssertRC(rc2);
205 pFlatImage->pDisk = NULL;
206 }
207 RTStrFree(pFlatImage->pszFormat);
208 pFlatImage->pszFormat = NULL;
209 break;
210 }
211
212 case VBOXFUSETYPE_CONTROL_PIPE:
213 break;
214
215 default:
216 AssertMsgFailed(("%d\n", pNode->enmType));
217 break;
218 }
219
220 /*
221 * Generic cleanup.
222 */
223 pNode->enmType = VBOXFUSETYPE_INVALID;
224 pNode->pszName = NULL;
225
226 /*
227 * Unlock and destroy the lock, before we finally frees the node.
228 */
229 if (fLocked)
230 RTCritSectLeave(&pNode->CritSect);
231 RTCritSectDelete(&pNode->CritSect);
232
233 RTMemFree(pNode);
234
235 return true;
236}
237
238
239/**
240 * Locks a FUSE node.
241 *
242 * @param pNode The node.
243 */
244static void vboxfuseNodeLock(PVBOXFUSENODE pNode)
245{
246 int rc = RTCritSectEnter(&pNode->CritSect);
247 AssertRC(rc);
248}
249
250
251/**
252 * Unlocks a FUSE node.
253 *
254 * @param pNode The node.
255 */
256static void vboxfuseNodeUnlock(PVBOXFUSENODE pNode)
257{
258 int rc = RTCritSectLeave(&pNode->CritSect);
259 AssertRC(rc);
260}
261
262
263/**
264 * Retain a VBoxFUSE node.
265 *
266 * @param pNode The node.
267 */
268static void vboxfuseNodeRetain(PVBOXFUSENODE pNode)
269{
270 int32_t cNewRefs = ASMAtomicIncS32(&pNode->cRefs);
271 Assert(cNewRefs != 1);
272}
273
274
275/**
276 * Releases a VBoxFUSE node reference.
277 *
278 * @returns true if deleted, false if not.
279 * @param pNode The node.
280 */
281static bool vboxfuseNodeRelease(PVBOXFUSENODE pNode)
282{
283 if (ASMAtomicDecS32(&pNode->cRefs) == 0)
284 return vboxfuseNodeDestroy(pNode, false /* fLocked */);
285 return false;
286}
287
288
289/**
290 * Locks and retains a VBoxFUSE node.
291 *
292 * @param pNode The node.
293 */
294static void vboxfuseNodeLockAndRetain(PVBOXFUSENODE pNode)
295{
296 vboxfuseNodeLock(pNode);
297 vboxfuseNodeRetain(pNode);
298}
299
300
301/**
302 * Releases a VBoxFUSE node reference and unlocks it.
303 *
304 * @returns true if deleted, false if not.
305 * @param pNode The node.
306 */
307static bool vboxfuseNodeReleaseAndUnlock(PVBOXFUSENODE pNode)
308{
309 if (ASMAtomicDecS32(&pNode->cRefs) == 0)
310 return vboxfuseNodeDestroy(pNode, true /* fLocked */);
311 vboxfuseNodeUnlock(pNode);
312 return false;
313}
314
315
316/**
317 * Creates stat info for a locked node.
318 *
319 * @param pNode The node (locked).
320 */
321static void vboxfuseNodeFillStat(PVBOXFUSENODE pNode, struct stat *pStat)
322{
323 pStat->st_dev = 0; /* ignored */
324 pStat->st_ino = pNode->Ino; /* maybe ignored */
325 pStat->st_mode = pNode->fMode;
326 pStat->st_nlink = pNode->cLinks;
327 pStat->st_uid = pNode->Uid;
328 pStat->st_gid = pNode->Gid;
329 pStat->st_rdev = 0;
330 /** @todo file times */
331 pStat->st_atime = 0;
332// pStat->st_atimensec = 0;
333 pStat->st_mtime = 0;
334// pStat->st_mtimensec = 0;
335 pStat->st_ctime = 0;
336// pStat->st_ctimensec = 0;
337 pStat->st_size = pNode->cbPrimary;
338 pStat->st_blocks = (pNode->cbPrimary + DEV_BSIZE - 1) / DEV_BSIZE;
339 pStat->st_blksize = 0x1000; /* ignored */
340 pStat->st_flags = 0;
341 pStat->st_gen = 0;
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 referneced 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(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, VD_OPEN_FLAGS_READONLY, 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 successfull 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 refernece 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 convertsion 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 successfull 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 refernece 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 successfull 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#else
1009# error "Port me"
1010#endif
1011
1012 PVBOXFUSENODE pNode;
1013 int rc = vboxfuseTreeLookupErrno(pszPath, &pNode);
1014 if (!rc)
1015 {
1016 /*
1017 * Check flags and stuff.
1018 */
1019 switch (pNode->enmType)
1020 {
1021 /* not expected here? */
1022 case VBOXFUSETYPE_DIRECTORY:
1023 AssertFailed();
1024 rc = -EISDIR;
1025 break;
1026
1027 case VBOXFUSETYPE_FLAT_IMAGE:
1028 {
1029 PVBOXFUSEFLATIMAGE pFlatImage = (PVBOXFUSEFLATIMAGE)pNode;
1030#ifdef O_DIRECTORY
1031 if (pInfo->flags & O_DIRECTORY)
1032 rc = -ENOTDIR;
1033#endif
1034 if ( (pInfo->flags & O_ACCMODE) == O_WRONLY
1035 || (pInfo->flags & O_ACCMODE) == O_RDWR)
1036 {
1037 if ( pFlatImage->cWriters == 0
1038 && pFlatImage->cReaders == 0)
1039 pFlatImage->cWriters++;
1040 else
1041 rc = -ETXTBSY;
1042 }
1043 else if ((pInfo->flags & O_ACCMODE) == O_RDONLY)
1044 {
1045 if (pFlatImage->cWriters == 0)
1046 {
1047 if (pFlatImage->cReaders + 1 < ( pFlatImage->cReaders < INT32_MAX / 2
1048 ? INT32_MAX / 4
1049 : INT32_MAX / 2 + INT32_MAX / 4) )
1050 pFlatImage->cReaders++;
1051 else
1052 rc = -EMLINK;
1053 }
1054 else
1055 rc = -ETXTBSY;
1056 }
1057 break;
1058 }
1059
1060 case VBOXFUSETYPE_CONTROL_PIPE:
1061 rc = -ENOTSUP;
1062 break;
1063
1064 default:
1065 rc = -EDOOFUS;
1066 break;
1067 }
1068 if (!rc)
1069 {
1070 /*
1071 * Put a reference to the node in the fuse_file_info::fh member so
1072 * we don't have to parse the path in the other file methods.
1073 */
1074 pInfo->fh = (uintptr_t)pNode;
1075 vboxfuseNodeUnlock(pNode);
1076 }
1077 else
1078 {
1079 /* cleanup */
1080 vboxfuseNodeReleaseAndUnlock(pNode);
1081 }
1082 }
1083 LogFlow(("vboxfuseOp_opendir: rc=%d \"%s\"\n", rc, pszPath));
1084 return rc;
1085}
1086
1087
1088/** @copydoc fuse_operations::release */
1089static int vboxfuseOp_release(const char *pszPath, struct fuse_file_info *pInfo)
1090{
1091 PVBOXFUSENODE pNode = (PVBOXFUSENODE)(uintptr_t)pInfo->fh;
1092 AssertPtr(pNode);
1093 pInfo->fh = 0;
1094
1095 switch (pNode->enmType)
1096 {
1097 case VBOXFUSETYPE_DIRECTORY:
1098 /* nothing to do */
1099 vboxfuseNodeRelease(pNode);
1100 break;
1101
1102 case VBOXFUSETYPE_FLAT_IMAGE:
1103 {
1104 PVBOXFUSEFLATIMAGE pFlatImage = (PVBOXFUSEFLATIMAGE)pNode;
1105 vboxfuseNodeLock(&pFlatImage->Node);
1106
1107 if ( (pInfo->flags & O_ACCMODE) == O_WRONLY
1108 || (pInfo->flags & O_ACCMODE) == O_RDWR)
1109 {
1110 pFlatImage->cWriters--;
1111 Assert(pFlatImage->cWriters >= 0);
1112 }
1113 else if ((pInfo->flags & O_ACCMODE) == O_RDONLY)
1114 {
1115 pFlatImage->cReaders--;
1116 Assert(pFlatImage->cReaders >= 0);
1117 }
1118 else
1119 AssertFailed();
1120
1121 vboxfuseNodeReleaseAndUnlock(&pFlatImage->Node);
1122 break;
1123 }
1124
1125 case VBOXFUSETYPE_CONTROL_PIPE:
1126 /* nothing to do yet */
1127 vboxfuseNodeRelease(pNode);
1128 break;
1129
1130 default:
1131 AssertMsgFailed(("%s\n", pszPath));
1132 return -EDOOFUS;
1133 }
1134
1135 LogFlow(("vboxfuseOp_release: \"%s\"\n", pszPath));
1136 return 0;
1137}
1138
1139/** The VDRead/VDWrite block granularity. */
1140#define VBOXFUSE_MIN_SIZE 512
1141/** Offset mask corresponding to VBOXFUSE_MIN_SIZE. */
1142#define VBOXFUSE_MIN_SIZE_MASK_OFF (0x1ff)
1143/** Block mask corresponding to VBOXFUSE_MIN_SIZE. */
1144#define VBOXFUSE_MIN_SIZE_MASK_BLK (~UINT64_C(0x1ff))
1145
1146/** @copydoc fuse_operations::read */
1147static int vboxfuseOp_read(const char *pszPath, char *pbBuf, size_t cbBuf,
1148 off_t offFile, struct fuse_file_info *pInfo)
1149{
1150 /* paranoia */
1151 AssertReturn((int)cbBuf >= 0, -EINVAL);
1152 AssertReturn((unsigned)cbBuf == cbBuf, -EINVAL);
1153 AssertReturn(offFile >= 0, -EINVAL);
1154 AssertReturn(offFile + cbBuf >= offFile, -EINVAL);
1155
1156 PVBOXFUSENODE pNode = (PVBOXFUSENODE)(uintptr_t)pInfo->fh;
1157 AssertPtr(pNode);
1158 switch (pNode->enmType)
1159 {
1160 case VBOXFUSETYPE_DIRECTORY:
1161 return -ENOTSUP;
1162
1163 case VBOXFUSETYPE_FLAT_IMAGE:
1164 {
1165 PVBOXFUSEFLATIMAGE pFlatImage = (PVBOXFUSEFLATIMAGE)(uintptr_t)pInfo->fh;
1166 LogFlow(("vboxfuseOp_read: offFile=%#llx cbBuf=%#zx pszPath=\"%s\"\n", (uint64_t)offFile, cbBuf, pszPath));
1167 vboxfuseNodeLock(&pFlatImage->Node);
1168
1169 int rc;
1170 if (offFile + cbBuf < offFile)
1171 rc = -EINVAL;
1172 else if (offFile >= pFlatImage->Node.cbPrimary)
1173 rc = 0;
1174 else if (!cbBuf)
1175 rc = 0;
1176 else
1177 {
1178 /* Adjust for EOF. */
1179 if (offFile + cbBuf >= pFlatImage->Node.cbPrimary)
1180 cbBuf = pFlatImage->Node.cbPrimary - offFile;
1181
1182 /*
1183 * Aligned read?
1184 */
1185 int rc2;
1186 if ( !(offFile & VBOXFUSE_MIN_SIZE_MASK_OFF)
1187 && !(cbBuf & VBOXFUSE_MIN_SIZE_MASK_OFF))
1188 rc2 = VDRead(pFlatImage->pDisk, offFile, pbBuf, cbBuf);
1189 else
1190 {
1191 /*
1192 * Unaligned read - lots of extra work.
1193 */
1194 uint8_t abBlock[VBOXFUSE_MIN_SIZE];
1195 if (((offFile + cbBuf) & VBOXFUSE_MIN_SIZE_MASK_BLK) == (offFile & VBOXFUSE_MIN_SIZE_MASK_BLK))
1196 {
1197 /* a single partial block. */
1198 rc2 = VDRead(pFlatImage->pDisk, offFile & VBOXFUSE_MIN_SIZE_MASK_BLK, abBlock, VBOXFUSE_MIN_SIZE);
1199 if (RT_SUCCESS(rc2))
1200 memcpy(pbBuf, &abBlock[offFile & VBOXFUSE_MIN_SIZE_MASK_OFF], cbBuf);
1201 }
1202 else
1203 {
1204 /* read unaligned head. */
1205 rc2 = VINF_SUCCESS;
1206 if (offFile & VBOXFUSE_MIN_SIZE_MASK_OFF)
1207 {
1208 rc2 = VDRead(pFlatImage->pDisk, offFile & VBOXFUSE_MIN_SIZE_MASK_BLK, abBlock, VBOXFUSE_MIN_SIZE);
1209 if (RT_SUCCESS(rc2))
1210 {
1211 size_t cbCopy = VBOXFUSE_MIN_SIZE - (offFile & VBOXFUSE_MIN_SIZE_MASK_OFF);
1212 memcpy(pbBuf, &abBlock[offFile & VBOXFUSE_MIN_SIZE_MASK_OFF], cbCopy);
1213 pbBuf += cbCopy;
1214 offFile += cbCopy;
1215 cbBuf -= cbCopy;
1216 }
1217 }
1218
1219 /* read the middle. */
1220 Assert(!(offFile & VBOXFUSE_MIN_SIZE_MASK_OFF));
1221 if (cbBuf >= VBOXFUSE_MIN_SIZE && RT_SUCCESS(rc2))
1222 {
1223 size_t cbRead = cbBuf & VBOXFUSE_MIN_SIZE_MASK_BLK;
1224 rc2 = VDRead(pFlatImage->pDisk, offFile, pbBuf, cbRead);
1225 if (RT_SUCCESS(rc2))
1226 {
1227 pbBuf += cbRead;
1228 offFile += cbRead;
1229 cbBuf -= cbRead;
1230 }
1231 }
1232
1233 /* unaligned tail read. */
1234 Assert(cbBuf < VBOXFUSE_MIN_SIZE);
1235 Assert(!(offFile & VBOXFUSE_MIN_SIZE_MASK_OFF));
1236 if (cbBuf && RT_SUCCESS(rc2))
1237 {
1238 rc2 = VDRead(pFlatImage->pDisk, offFile, abBlock, VBOXFUSE_MIN_SIZE);
1239 if (RT_SUCCESS(rc2))
1240 memcpy(pbBuf, &abBlock[0], cbBuf);
1241 }
1242 }
1243 }
1244
1245 /* convert the return code */
1246 if (RT_SUCCESS(rc2))
1247 rc = cbBuf;
1248 else
1249 rc = -RTErrConvertToErrno(rc2);
1250 }
1251
1252 vboxfuseNodeUnlock(&pFlatImage->Node);
1253 return rc;
1254 }
1255
1256 case VBOXFUSETYPE_CONTROL_PIPE:
1257 return -ENOTSUP;
1258
1259 default:
1260 AssertMsgFailed(("%s\n", pszPath));
1261 return -EDOOFUS;
1262 }
1263}
1264
1265
1266/**
1267 * The FUSE operations.
1268 *
1269 * @remarks We'll initialize this manually since we cannot use C99 style
1270 * initialzer designations in C++ (yet).
1271 */
1272static struct fuse_operations g_vboxfuseOps;
1273
1274
1275
1276int main(int argc, char **argv)
1277{
1278 /*
1279 * Initialize the runtime and VD.
1280 */
1281 int rc = RTR3Init();
1282 if (RT_FAILURE(rc))
1283 {
1284 RTStrmPrintf(g_pStdErr, "VBoxFUSE: RTR3Init failed, rc=%Rrc\n", rc);
1285 return 1;
1286 }
1287 RTPrintf("VBoxFUSE: Hello...\n");
1288 rc = VDInit();
1289 if (RT_FAILURE(rc))
1290 {
1291 RTStrmPrintf(g_pStdErr, "VBoxFUSE: VDInit failed, rc=%Rrc\n", rc);
1292 return 1;
1293 }
1294
1295 /*
1296 * Initializes the globals and populate the file hierarchy.
1297 */
1298 rc = vboxfuseDirCreate("/", NULL);
1299 if (RT_SUCCESS(rc))
1300 rc = vboxfuseDirCreate("/FlattenedImages", NULL);
1301 if (RT_FAILURE(rc))
1302 {
1303 RTStrmPrintf(g_pStdErr, "VBoxFUSE: vboxfuseDirCreate failed, rc=%Rrc\n", rc);
1304 return 1;
1305 }
1306
1307 /*
1308 * Initialize the g_vboxfuseOps. (C++ sucks!)
1309 */
1310 memset(&g_vboxfuseOps, 0, sizeof(g_vboxfuseOps));
1311 g_vboxfuseOps.getattr = vboxfuseOp_getattr;
1312 g_vboxfuseOps.opendir = vboxfuseOp_opendir;
1313 g_vboxfuseOps.readdir = vboxfuseOp_readdir;
1314 g_vboxfuseOps.releasedir = vboxfuseOp_releasedir;
1315 g_vboxfuseOps.symlink = vboxfuseOp_symlink;
1316 g_vboxfuseOps.open = vboxfuseOp_open;
1317 g_vboxfuseOps.read = vboxfuseOp_read;
1318 g_vboxfuseOps.release = vboxfuseOp_release;
1319
1320 /*
1321 * Hand control over to libfuse.
1322 */
1323
1324#if 0
1325 /** @todo multithreaded fun. */
1326#else
1327 rc = fuse_main(argc, argv, &g_vboxfuseOps, NULL);
1328#endif
1329 RTPrintf("VBoxFUSE: fuse_main -> %d\n", rc);
1330 return rc;
1331}
1332
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