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