~ubuntu-branches/ubuntu/natty/virtualbox-ose/natty-updates

« back to all changes in this revision

Viewing changes to debian/vdfuse/vdfuse.c

  • Committer: Bazaar Package Importer
  • Author(s): Felix Geyer
  • Date: 2010-03-11 17:16:37 UTC
  • mfrom: (0.3.4 upstream) (0.4.8 sid)
  • Revision ID: james.westby@ubuntu.com-20100311171637-43z64ia3ccpj8vqn
Tags: 3.1.4-dfsg-2ubuntu1
* Merge from Debian unstable (LP: #528561), remaining changes:
  - VirtualBox should go in Accessories, not in System tools (LP: #288590)
    - debian/virtualbox-ose-qt.files/virtualbox-ose.desktop
  - Add Apport hook
    - debian/virtualbox-ose.files/source_virtualbox-ose.py
    - debian/virtualbox-ose.install
  - Add Launchpad integration
    - debian/control
    - debian/lpi-bug.xpm
    - debian/patches/u02-lp-integration.dpatch
  - Replace *-source packages with transitional packages for *-dkms
* Fix crash in vboxvideo_drm with kernel 2.6.33 / backported drm code
  (LP: #535297)
* Add a list of linux-headers packages to the apport hook
* Update debian/patches/u02-lp-integration.dpatch with a
  DEP-3 compliant header
* Add ${misc:Depends} to virtualbox-ose-source and virtualbox-ose-guest-source
  Depends

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Original author: h2o on forums.virtualbox.org                        *
 
2
 * http://forums.virtualbox.org/viewtopic.php?p=59275                   *
 
3
 * Reworked with Terry Ellison                                          *
 
4
 * vdfuse.c - tool for mounting VDI/VMDK/VHD files                      *
 
5
 *                                                                      *                                                                      *
 
6
 * This program is free software: you can redistribute it and/or modify *
 
7
 * it under the terms of the GNU General Public License as published by *
 
8
 * the Free Software Foundation, either version 2 of the License, or    *
 
9
 * (at your option) any later version.                                  *
 
10
 *                                                                      *
 
11
 * This program is distributed in the hope that it will be useful,      *
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of       *
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
 
14
 * GNU General Public License for more details.                         *
 
15
 *                                                                      *
 
16
 * You should have received a copy of the GNU General Public License    *
 
17
 * along with this program. If not, see <http://www.gnu.org/licenses/>. */
 
18
 
 
19
/* VERSION 01  TE  14 Feb 09  Initial release
 
20
 *         02  TE  15 Feb 09  Use the stat on the container file as the basis for any getattr on
 
21
 *                            the partiton pseudo-files
 
22
 *         03  TE  16 Feb 09  Bug corrections from Gavin, plus EntireDisc/Partition interlock.
 
23
 *         04  TE  23 Mar 09  Another Bug correction from Gavin, plus addition of printPartition.
 
24
 *         05      09 May 09  Change sizes from size_t to uint64_t (size_t is 32-bit on 32-bit systems)
 
25
 *                            Fix _FILE_OFFSET_BITS
 
26
 *                            Option for older VBox
 
27
 *
 
28
 *
 
29
 * DESCRIPTION
 
30
 *
 
31
 * This Fuse module uses the VBox access library to open a VBox supported VD image file and mount
 
32
 * it as a Fuse file system.  The mount point contains a flat directory with the following files
 
33
 *  *  EntireDisk
 
34
 *  *  PartitionN
 
35
 *
 
36
 * Note that each file should only be opened once and opening EntireDisk should locks out
 
37
 * the other files. However, since file close isn't passed to the fuse utilities, I can only 
 
38
 * enforce this in a brute fashion:  If you open EntireDisk then all further I/O to 
 
39
 * the PartitionN files will fail.  If open any PartitionN file then all further I/O to EntireDisk
 
40
 * will fail.  Hence in practice you can only access on or the other within a single mount. 
 
41
 *
 
42
 * This code is structured in the following sections:
 
43
 *  *  The main(argc, argv) routine including validation of arguments and call to fuse_main
 
44
 *  *  MBR and EBR parsing routines
 
45
 *  *  The Fuse callback routines for destroy ,flush ,getattr ,open, read, readdir, write
 
46
 *
 
47
 * For further details on how this all works see http://fuse.sourceforge.net/
 
48
 *
 
49
 * VirtualBox provided an API to enable you to manipulate VD image files programmatically.
 
50
 * This is documented in the embedded source comments.  See for further details
 
51
 *    http://www.virtualbox.org/svn/vbox/trunk/include/VBox/VBoxHDD-new.h
 
52
 *
 
53
 * To compile this you need to pull (wget) the VBox OSE source from http://www.virtualbox.org/downloads.
 
54
 * Set the environment variable VBOX_INCLUDE to the include directory within this tree
 
55
 *
 
56
 * gcc vdfuse.c -o vdfuse `pkg-config --cflags --libs fuse` \
 
57
 *     -l:/usr/lib/virtualbox/VBoxDD.so -Wl,-rpath,/usr/lib/virtualbox \
 
58
 *     -pthread -I$VBOX_INCLUDE
 
59
 *
 
60
 */
 
61
#define FUSE_USE_VERSION 26
 
62
#define _FILE_OFFSET_BITS 64
 
63
#include <limits.h>
 
64
#include <fuse.h>
 
65
#include <errno.h>
 
66
#include <ctype.h>
 
67
#include <stdio.h>
 
68
#include <stdlib.h>
 
69
#include <stdarg.h>
 
70
#include <string.h>
 
71
#include <unistd.h>
 
72
#include <pthread.h>
 
73
 
 
74
#ifdef __GNUC__
 
75
#define UNUSED __attribute__ ((unused))
 
76
#else
 
77
#define UNUSED
 
78
#endif
 
79
 
 
80
#define IN_RING3
 
81
#define BLOCKSIZE 512
 
82
#define UNALLOCATED -1
 
83
#define GETOPT_ARGS "rgvawt:f:dh?"
 
84
#define HOSTPARTITION_MAX 100
 
85
#define PNAMESIZE 15
 
86
#define MBR_START 446
 
87
#define EBR_START 446
 
88
#define PARTTYPE_IS_EXTENDED(x) ((x) == 0x05 || (x) == 0x0f || (x) == 0x85)
 
89
 
 
90
 
 
91
void usageAndExit (char *optFormat, ...);
 
92
void vbprintf (const char *format, ...);
 
93
void vdErrorCallback(void *pvUser, int rc, const char *file, unsigned iLine, const char *function, const char *format, va_list va);
 
94
void initialisePartitionTable(void);
 
95
int  findPartition (const char* filename);
 
96
int  detectDiskType (char **disktype, char *filename);
 
97
static int VD_open (const char *c, struct fuse_file_info *i);
 
98
static int VD_release (const char *name, struct fuse_file_info *fi);
 
99
static int VD_read (const char *c, char *out, size_t len, off_t offset, struct fuse_file_info *i UNUSED);
 
100
static int VD_write (const char *c, const char *in, size_t len, off_t offset, struct fuse_file_info *i UNUSED);
 
101
static int VD_flush (const char *p, struct fuse_file_info *i UNUSED);
 
102
static int VD_readdir (const char *p, void *buf, fuse_fill_dir_t filler, off_t offset UNUSED, struct fuse_file_info *i UNUSED);
 
103
static int VD_getattr (const char *p, struct stat *stbuf);
 
104
void       VD_destroy (void *u);
 
105
 
 
106
// Note that this abstraction layer was initially here to allow a version of this to be compiled with
 
107
// Version 1.x using the VBox/VBoxHDD.h interface, but since this has been withdrawn for 2.x and I can
 
108
// no longer test this, I've decided to remove this old API code, but I have left the abstraction in
 
109
// I also used the ...testcase/tstVD.cpp as a coding template to work out how to call the VD routines.
 
110
 
 
111
// if you don't have VBoxHDD.h, run 'svn co http://www.virtualbox.org/svn/vbox/trunk/include'
 
112
// if your VirtualBox version is older than 2.1, add -DOLDVBOXHDD to your gcc flags
 
113
#ifdef OLDVBOXHDD // < v2.1
 
114
#include <VBox/VBoxHDD-new.h>
 
115
#else // >= v2.1
 
116
#include <VBox/VBoxHDD.h>
 
117
#endif
 
118
#define DISKread(o,b,s) VDRead (hdDisk,o,b,s);
 
119
#define DISKwrite(o,b,s) VDWrite (hdDisk,o,b,s);
 
120
#define DISKclose VDClose(hdDisk, 0)
 
121
#define DISKsize VDGetSize(hdDisk, 0)
 
122
#define DISKflush VDFlush(hdDisk)
 
123
#define DISKopen(t,i) if (RT_FAILURE(VDInterfaceAdd(&vdError, "VD Error", VDINTERFACETYPE_ERROR, \
 
124
                            &vdErrorCallbacks, NULL, &pVDifs))) \
 
125
                            usageAndExit("invalid initialisation of VD interface"); \
 
126
   if (RT_FAILURE(VDCreate(&vdError, &hdDisk))) usageAndExit("invalid initialisation of VD interface"); \
 
127
   if (RT_FAILURE(VDOpen(hdDisk,t , i, readonly ? VD_OPEN_FLAGS_READONLY : VD_OPEN_FLAGS_NORMAL, NULL))) \
 
128
      usageAndExit("opening vbox image failed"); \
 
129
 
 
130
PVBOXHDD         hdDisk;
 
131
PVDINTERFACE     pVDifs = NULL;
 
132
VDINTERFACE      vdError;
 
133
VDINTERFACEERROR vdErrorCallbacks = {
 
134
   .cbSize = sizeof(VDINTERFACEERROR),
 
135
   .enmInterface = VDINTERFACETYPE_ERROR,
 
136
   .pfnError = vdErrorCallback };
 
137
 
 
138
// Partition table information
 
139
 
 
140
typedef struct {                 // See http://en.wikipedia.org/wiki/Master_boot_record
 
141
   uint8_t status;               // status[7] (0x80 = bootable, 0x00 = non-bootable,other = invalid[8])
 
142
                                 // ** CHS address of first block **
 
143
   uint8_t shead;                // first head
 
144
   uint8_t ssector;              // first sector is in bits 5-0; bits 9-8 of cylinder are in bits 7-6
 
145
   uint8_t sbits;                // first bits 7-0 of cylinder
 
146
   uint8_t type;                 // partition type
 
147
                                 // ** CHS address of last block **
 
148
   uint8_t ehead;                // last head
 
149
   uint8_t esector;              // last sector is in bits 5-0; bits 9-8 of cylinder are in bits 7-6
 
150
   uint8_t ebits;                // last bits 7-0 of cylinder
 
151
   uint32_t offset;              // LBA of first sector in the partition
 
152
   uint32_t size;                // number of blocks in partition, in little-endian format
 
153
} MBRentry;
 
154
 
 
155
typedef struct {
 
156
   char        name[PNAMESIZE+1];// name of partition
 
157
   off_t       offset;           // offset into disk in bytes
 
158
   uint64_t    size;             // size of partiton in bytes
 
159
   int         no;               // partition number
 
160
   MBRentry    descriptor;       // copy of MBR / EBR descriptor that defines the partion
 
161
} Partition;
 
162
 
 
163
typedef struct {                 // See http://en.wikipedia.org/wiki/Extended_boot_record for details
 
164
  MBRentry     descriptor;
 
165
  MBRentry     chain;
 
166
  MBRentry     fill1, fill2;
 
167
  uint16_t     signature;
 
168
} EBRentry;
 
169
 
 
170
Partition partitionTable[HOSTPARTITION_MAX+1]; // Note the partitionTable[0] is reserved for the EntireDisk descriptor
 
171
static int lastPartition = 0;
 
172
 
 
173
static struct fuse_operations fuseOperations = {
 
174
   .readdir = VD_readdir,
 
175
   .getattr = VD_getattr,
 
176
   .open    = VD_open,
 
177
   .release = VD_release,
 
178
   .read    = VD_read,
 
179
   .write   = VD_write,
 
180
   .flush   = VD_flush,
 
181
   .destroy = VD_destroy
 
182
};
 
183
 
 
184
static struct fuse_args fuseArgs = FUSE_ARGS_INIT (0, NULL);
 
185
 
 
186
static struct stat VDfile_stat;
 
187
 
 
188
static int   verbose   = 0;
 
189
static int   readonly  = 0;
 
190
static int   allowall  = 0; // allow all users to read from disk
 
191
static int   allowallw = 0; // allow all users to write to disk
 
192
static uid_t myuid     = 0;
 
193
static gid_t mygid     = 0;
 
194
static char *processName;
 
195
static int   entireDiskOpened = 0;
 
196
static int   partitionOpened  = 0;
 
197
static int   opened    = 0; // how many opened instances are there
 
198
 
 
199
//
 
200
//====================================================================================================
 
201
//                                Main routine including validation
 
202
//====================================================================================================
 
203
 
 
204
int main (int argc, char **argv) {
 
205
   char        *diskType      = "auto";
 
206
   char        *imagefilename = NULL;
 
207
   char        *mountpoint    = NULL;
 
208
   int          debug         = 0;
 
209
   int          foreground    = 0;
 
210
   char         c;
 
211
 
 
212
   extern char *optarg;
 
213
   extern int   optind, optopt;
 
214
 
 
215
   //
 
216
   // *** Parse the command line options ***
 
217
   //
 
218
   processName = argv[0];
 
219
 
 
220
   while ((c = getopt(argc, argv, GETOPT_ARGS)) != -1) {
 
221
      switch(c) {
 
222
      case 'r' : readonly = 1;                    break;
 
223
      case 'g' : foreground = 1;                  break;
 
224
      case 'v' : verbose = 1;                     break;
 
225
      case 'a' : allowall = 1;                    break;
 
226
      case 'w' : allowall = 1; allowallw = 1;     break;
 
227
      case 't' : diskType = (char *) optarg;      break;           // ignored if OLDAPI
 
228
      case 'f' : imagefilename =  (char *)optarg; break;
 
229
      case 'd' : foreground = 1; debug = 1;       break;
 
230
      case 'h' : usageAndExit(NULL);
 
231
      case '?' : usageAndExit("Unknown option");
 
232
      }
 
233
   }
 
234
   //
 
235
   // *** Validate the command line ***
 
236
   //
 
237
   if (argc != optind+1) usageAndExit("a single mountpoint must be specified");
 
238
   mountpoint = argv[optind];
 
239
   if (!mountpoint)    usageAndExit("no mountpoint specified");
 
240
   if (!imagefilename) usageAndExit("no image chosen");
 
241
   if (stat(imagefilename, &VDfile_stat)<0) usageAndExit("cannot access imagefile");
 
242
   if (access (imagefilename, F_OK | R_OK | ((!readonly) ? W_OK : 0))
 
243
       > 0) usageAndExit("cannot access imagefile");
 
244
 
 
245
#define IS_TYPE(s) (strcmp (s, diskType) == 0)
 
246
   if ( !(IS_TYPE("auto") || IS_TYPE("VDI" ) || IS_TYPE("VMDK") || IS_TYPE("VHD" ) ||
 
247
          IS_TYPE("auto")) ) usageAndExit("invalid disk type specified");
 
248
   if (strcmp ("auto", diskType) == 0 && detectDiskType (&diskType, imagefilename) < 0) return 1;
 
249
   
 
250
   //
 
251
   // *** Open the VDI, parse the MBR + EBRs and connect to the fuse service ***
 
252
   //
 
253
   DISKopen(diskType, imagefilename);
 
254
 
 
255
   initialisePartitionTable();
 
256
 
 
257
   myuid = geteuid ();
 
258
   mygid = getegid ();
 
259
 
 
260
   fuse_opt_add_arg (&fuseArgs, "vdfuse");
 
261
        
 
262
   {
 
263
      char fsname[strlen(imagefilename) + 12];
 
264
      strcpy (fsname, "-ofsname=\0");
 
265
      strcat (fsname, imagefilename);
 
266
      fuse_opt_add_arg (&fuseArgs, fsname);
 
267
   }
 
268
        
 
269
   fuse_opt_add_arg (&fuseArgs, "-osubtype=vdfuse");
 
270
   fuse_opt_add_arg (&fuseArgs, "-o");
 
271
   fuse_opt_add_arg (&fuseArgs, (allowall) ? "allow_other" : "allow_root");
 
272
   if (foreground) fuse_opt_add_arg (&fuseArgs, "-f");
 
273
   if (debug)      fuse_opt_add_arg (&fuseArgs, "-d");
 
274
   fuse_opt_add_arg (&fuseArgs, mountpoint);
 
275
   
 
276
   return fuse_main (fuseArgs.argc, fuseArgs.argv, &fuseOperations
 
277
#if FUSE_USE_VERSION >= 26
 
278
                     , NULL
 
279
#endif
 
280
                    );
 
281
}
 
282
//====================================================================================================
 
283
//                                  Miscellaneous output utilities
 
284
//====================================================================================================
 
285
 
 
286
void usageAndExit(char *optFormat, ... ) {
 
287
   va_list ap;
 
288
   if (optFormat !=NULL) {
 
289
      fputs ("\nERROR: ", stderr);
 
290
      va_start (ap, optFormat); vfprintf (stderr, optFormat, ap); va_end (ap);
 
291
      fputs ("\n\n", stderr);
 
292
   }
 
293
//              ---------!---------!---------!---------!---------!---------!---------!---------!
 
294
   fprintf (stderr,
 
295
           "DESCRIPTION: This Fuse module uses the VirtualBox access library to open a \n"
 
296
           "VirtualBox supported VD image file and mount it as a Fuse file system.  The\n"
 
297
           "mount point contains a flat directory containing the files EntireDisk,\n"
 
298
           "Partition1 .. PartitionN.  These can then be loop mounted to access the\n"
 
299
           "underlying file systems\n\n"
 
300
           "USAGE: %s [options] -f image-file mountpoint\n"
 
301
          "\t-h\thelp\n"
 
302
          "\t-r\treadonly\n"
 
303
#ifndef OLDAPI
 
304
          "\t-t\tspecify type (VDI, VMDK, VHD, or raw; default: auto)\n"
 
305
#endif
 
306
          "\t-f\tVDimage file\n"
 
307
          "\t-a\tallow all users to read disk\n"
 
308
          "\t-w\tallow all users to read and write to disk\n"
 
309
          "\t-g\trun in foreground\n"
 
310
          "\t-v\tverbose\n"
 
311
          "\t-d\tdebug\n\n"
 
312
          "NOTE: you must add the line \"user_allow_other\" (without quotes)\n"
 
313
          "to /etc/fuse.confand set proper permissions on /etc/fuse.conf\n"
 
314
          "for this to work.\n", processName);
 
315
   exit(1);
 
316
}
 
317
 
 
318
void vbprintf (const char *format, ...) {
 
319
   va_list ap;
 
320
   if (!verbose) return;
 
321
   va_start (ap, format); vprintf (format, ap); va_end (ap);
 
322
   fputs ("\n", stdout);
 
323
   fflush (stdout);
 
324
}
 
325
 
 
326
void vdErrorCallback(void *pvUser UNUSED, int rc, const char *file, unsigned iLine, const char *function, const char *format, va_list va) {
 
327
    fprintf(stderr, "\nVD CallbackError rc %d at %s:%u (%s): ", rc, file, iLine, function);
 
328
    vfprintf(stderr, format, va);
 
329
    fputs("\n", stderr);
 
330
}
 
331
 
 
332
 
 
333
//====================================================================================================
 
334
//                                        MBR + EBR parsing routine
 
335
//====================================================================================================
 
336
//
 
337
// This code is algorithmically based on partRead in VBoxInternalManage.cpp plus the Wikipedia articles
 
338
// on MBR and EBR. As in partRead, a statically allocated partition list is used same  to keep things
 
339
// simple (but up to a max 100 partitions :lol:).  Note than unlike partRead, this doesn't resort the
 
340
// partitions.
 
341
//
 
342
void initialisePartitionTable(void) {
 
343
   uint16_t MBRsignature;
 
344
   int      entendedFlag = UNALLOCATED;
 
345
   int      i;
 
346
 
 
347
   memset(partitionTable, 0, sizeof(partitionTable));
 
348
   for (i=0; i <= (signed) (sizeof(partitionTable)/sizeof(Partition)) ; i++) partitionTable[i].no = UNALLOCATED;
 
349
 
 
350
   partitionTable[0].no     = 0;
 
351
   partitionTable[0].offset = 0;
 
352
   partitionTable[0].size   = DISKsize;
 
353
   strcpy(partitionTable[0].name, "EntireDisk");
 
354
   //
 
355
   // Check that this is unformated or a DOS partitioned disk.  Sorry but other formats not supported.
 
356
   //
 
357
   DISKread(MBR_START + 4 * sizeof(MBRentry), &MBRsignature, sizeof (MBRsignature));
 
358
   if (MBRsignature == 0x0000) return;  // an unformated disk is allowed but only EntireDisk is defined
 
359
   if (MBRsignature != 0xaa55) usageAndExit("Invalid MBR found on image with signature 0x%04hX", MBRsignature);
 
360
 
 
361
   //
 
362
   // Process the four physical partition entires in the MBR
 
363
   //
 
364
   for (i = 1; i <= 4; i++) {
 
365
      Partition *p = partitionTable + i;
 
366
//    MBRentry  *m = &(p->descriptor);
 
367
      DISKread (MBR_START + (i-1) * sizeof(MBRentry), &(p->descriptor), sizeof(MBRentry));
 
368
      if ((p->descriptor).type == 0) continue;
 
369
      if (PARTTYPE_IS_EXTENDED((p->descriptor).type)) {
 
370
         if (entendedFlag != UNALLOCATED) usageAndExit("More than one extended partition in MBR");
 
371
         entendedFlag = i;
 
372
      } else {
 
373
         lastPartition = i;
 
374
         p->no         = i;
 
375
         p->offset     = (off_t)((p->descriptor).offset) * BLOCKSIZE;
 
376
         p->size       = (off_t)((p->descriptor).size)   * BLOCKSIZE;
 
377
      }
 
378
   }
 
379
   //
 
380
   // Now chain down any EBRs to process the logical partition entries
 
381
   //
 
382
   if (entendedFlag != UNALLOCATED) {
 
383
      EBRentry ebr;
 
384
      off_t uStart  = (off_t)((partitionTable[entendedFlag].descriptor).offset) * BLOCKSIZE;
 
385
      off_t uOffset = 0;
 
386
 
 
387
      if (!uStart) usageAndExit("Inconsistency for logical partition start. Aborting\n");
 
388
 
 
389
      for (i = 5; i <= HOSTPARTITION_MAX; i++)  {
 
390
         lastPartition++;
 
391
         Partition *p = partitionTable + i;
 
392
 
 
393
         DISKread (uStart + uOffset + EBR_START, &ebr, sizeof(ebr));
 
394
 
 
395
         if (ebr.signature != 0xaa55)    usageAndExit("Invalid EBR signature found on image");
 
396
         if ((ebr.descriptor).type == 0) usageAndExit("Logical partition with type 0 encountered");
 
397
         if (!((ebr.descriptor).offset)) usageAndExit("Logical partition invalid partition start offset encountered");
 
398
 
 
399
         p->descriptor = ebr.descriptor;
 
400
         p->no         = i;
 
401
         lastPartition = i;
 
402
         p->offset     = uStart + uOffset + (off_t)((ebr.descriptor).offset) * BLOCKSIZE;
 
403
         p->size       = (off_t)((ebr.descriptor).size)   * BLOCKSIZE;
 
404
 
 
405
         if (ebr.chain.type == 0) break;
 
406
         if (!PARTTYPE_IS_EXTENDED(ebr.chain.type)) usageAndExit("Logical partition chain broken");
 
407
         uOffset = (ebr.chain).offset;
 
408
      }
 
409
   }
 
410
   //
 
411
   // Now print out the partition table
 
412
   //
 
413
   vbprintf( "Partition       Size           Offset\n"
 
414
             "=========       ====           ======\n");
 
415
   for (i = 1; i <= lastPartition; i++)  {
 
416
      Partition *p = partitionTable + i;
 
417
      if (p->no != UNALLOCATED) {
 
418
         sprintf(p->name, "Partition%d", i);
 
419
         vbprintf ( "%-14s  %-13lld  %-13lld", p->name, p->offset, p->size );
 
420
      }
 
421
   }
 
422
   vbprintf( "\n" );
 
423
}
 
424
 
 
425
int findPartition (const char* filename) {
 
426
   // Use a dumb serial search since there are typically less than 3 entries
 
427
   int i;
 
428
   register Partition *p = partitionTable;
 
429
   for (i = 0; i <= lastPartition; i++, p++) {
 
430
      if (p->no != UNALLOCATED && strcmp(filename+1, p->name) == 0) return i;
 
431
   }
 
432
   return -1;
 
433
}
 
434
 
 
435
// detects type of virtual image
 
436
int detectDiskType (char **disktype, char *filename) {
 
437
   char buf[8];
 
438
   int fd = open (filename, O_RDONLY);
 
439
   read (fd, buf, sizeof (buf));
 
440
 
 
441
   if (strncmp (buf, "connectix", 8) == 0)  *disktype = "VHD";
 
442
   else if (strncmp (buf, "VMDK", 4) == 0)  *disktype = "VMDK";
 
443
   else if (strncmp (buf, "KDMV", 4) == 0)  *disktype = "VMDK";
 
444
   else if (strncmp (buf, "<<<",  3) == 0)  *disktype = "VDI";
 
445
   else usageAndExit("cannot autodetect disk type");
 
446
 
 
447
   vbprintf ("disktype is %s", *disktype);
 
448
   return 0;
 
449
   close(fd);
 
450
}
 
451
 
 
452
//====================================================================================================
 
453
//                                         Fuse Callback Routines
 
454
//====================================================================================================
 
455
//
 
456
// in alphetic order to help find them: destroy ,flush ,getattr ,open, read, readdir, write
 
457
 
 
458
pthread_mutex_t disk_mutex = PTHREAD_MUTEX_INITIALIZER;
 
459
pthread_mutex_t part_mutex = PTHREAD_MUTEX_INITIALIZER;
 
460
 
 
461
void VD_destroy (void *u UNUSED) {
 
462
// called when the fuse filesystem is umounted
 
463
   vbprintf ("destroy");
 
464
   DISKclose;
 
465
}
 
466
 
 
467
int VD_flush(const char *p, struct fuse_file_info *i UNUSED) {
 
468
   vbprintf ("flush: %s", p);
 
469
   DISKflush;
 
470
   return 0;
 
471
}
 
472
 
 
473
static int VD_getattr (const char *p, struct stat *stbuf) {
 
474
   vbprintf ("getattr: %s", p);
 
475
   int isFileRoot = (strcmp ("/", p) == 0);
 
476
   int n = findPartition(p);
 
477
 
 
478
   if (!isFileRoot && n == -1) return -ENOENT;
 
479
 
 
480
   // Use the container file's stat return as the basis. However since partitions cannot
 
481
   // be created by creating files, there is no write access to the directory.  I also
 
482
   // treat group access the same as other.
 
483
 
 
484
   memcpy (stbuf, &VDfile_stat, sizeof (struct stat));
 
485
 
 
486
   if (isFileRoot) {
 
487
      stbuf->st_mode = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP;
 
488
      if (allowall)  stbuf->st_mode |=  S_IROTH;
 
489
      stbuf->st_size   = 0;
 
490
      stbuf->st_blocks = 2;
 
491
   } else {
 
492
      stbuf->st_mode = S_IFREG | S_IRUSR | S_IWUSR;
 
493
      if (allowall)  stbuf->st_mode |=  S_IRGRP | S_IROTH;
 
494
      if (allowallw) stbuf->st_mode |=  S_IWGRP | S_IWOTH;
 
495
      stbuf->st_size   = partitionTable[n].size;
 
496
      stbuf->st_blocks = (stbuf->st_size + BLOCKSIZE - 1) / BLOCKSIZE;
 
497
   }
 
498
   if (readonly) {
 
499
      stbuf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
 
500
   }
 
501
   
 
502
   stbuf->st_nlink = 1;
 
503
 
 
504
   return 0;
 
505
}
 
506
 
 
507
static int VD_open(const char *cName, struct fuse_file_info *i) {
 
508
   vbprintf ("open: %s, %lld, 0X%08lX ", cName, i->fh, i->flags );
 
509
   int n = findPartition(cName);
 
510
   if ( (n == -1)                   || 
 
511
        (entireDiskOpened && n > 0) || 
 
512
        (partitionOpened && n == 0)   ) return -ENOENT;
 
513
   if ( readonly &&
 
514
       ((i->flags & (O_WRONLY | O_RDWR)) != 0)) return -EROFS;
 
515
   
 
516
   if (n == 0) entireDiskOpened = 1;
 
517
   else        partitionOpened  = 1;
 
518
   
 
519
   pthread_mutex_lock (&part_mutex);
 
520
   opened++;
 
521
   pthread_mutex_unlock (&part_mutex);
 
522
   
 
523
   return 0;
 
524
}
 
525
 
 
526
static int VD_release (const char *name, struct fuse_file_info *fi) {
 
527
   (void) fi;
 
528
   vbprintf ("release: %s", name);
 
529
   
 
530
   pthread_mutex_lock (&part_mutex);
 
531
   opened--;
 
532
   if (opened == 0) {
 
533
      initialisePartitionTable ();
 
534
      entireDiskOpened = 0;
 
535
      partitionOpened = 0;
 
536
   }
 
537
   pthread_mutex_unlock (&part_mutex);
 
538
   
 
539
   return 0;
 
540
}
 
541
 
 
542
static int VD_read (const char *c, char *out, size_t len, off_t offset, struct fuse_file_info *i UNUSED) {
 
543
   vbprintf ("read: %s, offset=%lld, length=%d", c, offset, len);
 
544
   int n = findPartition(c);
 
545
   if (n<0) return -ENOENT;
 
546
   if ((n == 0) ? partitionOpened : entireDiskOpened) return -EIO;
 
547
   
 
548
   Partition *p = &(partitionTable[n]);
 
549
//   if (offset >= p->size) return 0;
 
550
//   if (offset + len> p->size) len = p->size - offset;
 
551
   if ((uint64_t)offset >= p->size) return 0;
 
552
   if ((uint64_t)(offset + len) > p->size) len = p->size - offset;
 
553
 
 
554
   pthread_mutex_lock (&disk_mutex);
 
555
   int ret = DISKread(offset + p->offset, out, len);
 
556
   pthread_mutex_unlock (&disk_mutex);
 
557
 
 
558
   return VBOX_SUCCESS(ret) ? (signed) len : -EIO;
 
559
}
 
560
 
 
561
static int VD_readdir (const char *p, void *buf, fuse_fill_dir_t filler, off_t offset UNUSED, struct fuse_file_info *i UNUSED){
 
562
   int n;
 
563
   vbprintf ("readdir");
 
564
   if (strcmp ("/", p) != 0) return -ENOENT;
 
565
   filler (buf, ".", NULL, 0);
 
566
   filler (buf, "..", NULL, 0);
 
567
   for (n = 0; n <= lastPartition; n++) {
 
568
      Partition *p = partitionTable + n;
 
569
      if (p->no != UNALLOCATED) filler(buf, p->name, NULL, 0);
 
570
   }
 
571
   return 0;
 
572
}
 
573
 
 
574
static int VD_write (const char *c, const char *in, size_t len, off_t offset, struct fuse_file_info *i UNUSED) {
 
575
   vbprintf ("write: %s, offset=%lld, length=%d", c, offset, len);
 
576
   int n = findPartition(c);
 
577
   if (n<0) return -ENOENT;
 
578
   if ((n == 0) ? partitionOpened : entireDiskOpened) return -EIO;
 
579
   Partition *p = &(partitionTable[n]);
 
580
   if ((uint64_t)offset >= p->size) return 0;
 
581
   if ((uint64_t)(offset + len) > p->size) len = p->size - offset;
 
582
 
 
583
   pthread_mutex_lock (&disk_mutex);
 
584
   int ret = DISKwrite(offset + p->offset, in, len);
 
585
   pthread_mutex_unlock (&disk_mutex);
 
586
 
 
587
   return VBOX_SUCCESS(ret) ? (signed) len : -EIO;
 
588
}