completed overlay option
authorRalph Ronnquist <ralph.ronnquist@gmail.com>
Tue, 9 Aug 2022 11:21:11 +0000 (21:21 +1000)
committerRalph Ronnquist <ralph.ronnquist@gmail.com>
Tue, 9 Aug 2022 11:21:11 +0000 (21:21 +1000)
fusefile.8
fusefile.c

index b4bd50d314f7b3504677285e7660e1fbc17da885..183a362bce8c42bff4b27764ba01a4930625b37e 100644 (file)
@@ -4,7 +4,7 @@
 fusefile \- FUSE file mount for combining file fragments
 
 .SH SYNOPSIS
-.B fusefile \fR[fuse options\fR] \fBmountpoint\fR \fIfilename/from-to\fR ...
+.B fusefile \fR[\fIfuse-opts\fR] \fBmountpoint\fR \fR[\fIoverlay\fR] \fIfilename/from-to\fR ...
 
 .SH DESCRIPTION
 
@@ -15,6 +15,13 @@ as a single, contiguous file. It accepts over-writing on the fused
 file which gets distributed accordingly to the fragments, but cannot
 change size.
 
+An optional overlay file is declared with the "-overlay:filename"
+argument between the mount point and the fragments. This file is then
+set up as an overlay for capturing writes to the fused file. The
+overlay file will contain the written fused file regions, followed by
+meta data to distinguish between written content and "holes" (where
+content comes from the fused fragments).
+
 The fragment arguments include the filename of a source file, and
 optionally start and end byte positions. All in all there five
 variations:
@@ -66,6 +73,14 @@ position 2442:
 \fB$ fusefile y x/2442: x/:2442\fR
 .RE
 
+Protect raw disk image file with an overlay:
+.RS
+\fB# fusefile -ononempty disk.raw -overlay:today disk.raw
+.RE
+By this set up, the overlay file, "today", will protect the disk image
+file, "disk.raw" from changes, and also override the pathname
+"disk.raw" to be the fused file.
+
 .SH NOTES
 
 Note that \fBfusefile\fR opens the nominated source file or files
@@ -78,6 +93,10 @@ If a source is reduced in size, access will be inconsistent.
 If the mountpoint file doesn't exist, then \fBfusefile\fR creates it,
 and removes it when unmounted.
 
+Using an overlay file makes the fused file writable regardless of the
+fused fragemnts with the overlay file containing any changes to the
+original. The overlay file is reusable for subsequent fusing of the
+same fragments for reconstructing a prior session.
 
 .SH AUTHOR
 
index 8371d1f6c9448d538daa7b4ba1f2fc89feb89676..99424c5fd8ea7f8b1247d404da41cd8be7f06280 100644 (file)
 #include <time.h>
 #include <errno.h>
 
+struct Region {
+    off_t pos;
+    size_t size;
+};
+
 struct Source {
     char *filename;
     ssize_t from;
@@ -54,7 +59,121 @@ static struct {
     time_t ctime;
 } times;
 
-static struct Source overlay;
+/**
+ * Overlay
+ */
+static struct {
+    struct Source source;
+    struct Region *table;
+    size_t count;
+    size_t limit;
+} overlay;
+
+#define FRAG(m) (overlay.table+m)
+#define BEG(m) (FRAG(m)->pos)
+#define END(m) (FRAG(m)->pos + FRAG(m)->size)
+
+static ssize_t overlay_prior_fragment(off_t pos) {
+    size_t lo = 0, hi = overlay.count;
+    while ( lo < hi ) {
+       size_t m = ( lo + hi ) / 2;
+       if ( m == lo ) {
+           return BEG( m ) < pos? m : -1;
+       }
+       if ( BEG( m ) <= pos ) {
+           lo = m;
+       } else {
+           hi = m;
+       }
+    }
+    return -1;
+}
+
+static void overlay_save_count() {
+    lseek( overlay.source.fd, overlay.source.to, SEEK_SET );
+    size_t size = sizeof( overlay.count );
+    char *p = (char *) &overlay.count ;
+    while ( size > 0 ) {
+       size_t n = write( overlay.source.fd, p, size );
+       if ( n < 0 ) {
+           perror( overlay.source.filename );
+           exit( 1 );
+       }
+       size -= n;
+       p += n;
+    }
+}
+
+static void overlay_save_table(size_t lo,size_t hi) {
+    char *p = (char *) FRAG(lo);
+    size_t pos =  overlay.source.to + sizeof( overlay.count ) +
+       lo * sizeof( struct Region );
+    size_t size = ( hi - lo ) * sizeof( struct Region );
+    if ( pos != lseek( overlay.source.fd, pos, SEEK_SET ) ) {
+       fprintf( stderr, "%s: seek error\n", overlay.source.filename );
+       exit( 1 );
+    }
+    while ( size > 0 ) {
+       size_t n = write( overlay.source.fd, p, size );
+       if ( n < 0 ) {
+           perror( overlay.source.filename );
+           exit( 1 );
+       }
+       size -= n;
+       p += n;
+    }
+}
+
+static void overlay_insert(size_t p,off_t pos,size_t size) {
+    size_t bytes;
+    if ( overlay.count >= overlay.limit ) {
+       overlay.limit = overlay.count + 10;
+       bytes = overlay.limit * sizeof( struct Region );
+       overlay.table = overlay.table?
+           realloc( overlay.table, bytes ) : malloc( bytes );
+    }
+    bytes = ( overlay.count++ - p ) * sizeof( struct Region );
+    if ( bytes ) {
+       memmove( FRAG( p+1 ), FRAG( p ), bytes );
+    }
+    FRAG( p )->pos = pos;
+    FRAG( p )->size = size;
+    overlay_save_count();
+}
+
+static void overlay_delete(size_t p) {
+    if ( p < --overlay.count ) {
+       size_t size = ( overlay.count - p ) * sizeof( struct Region );
+       memmove( FRAG(p), FRAG(p+1), size );
+    }
+    overlay_save_count();
+}
+
+static void overlay_mark(off_t pos,size_t size) {
+    ssize_t p = overlay_prior_fragment( pos );
+    if ( p >= 0 && pos <= END(p) ) {
+       // Merge new marks with fragment p
+       FRAG(p)->size = pos + size - BEG(p);
+       if ( p+1 < overlay.count && BEG(p+1) <= END(p) ) {
+           FRAG(p)->size = END(p+1) - BEG(p);
+           overlay_delete( p+1 );
+           overlay_save_table( p, overlay.count );
+       } else {
+           overlay_save_table( p, p+1 );
+       }
+       return;
+    }
+    p++; // index of subsequent fragment
+    if ( p < overlay.count && BEG(p) < pos + size ) {
+       // Merge new marks with pragment p+1
+       FRAG(p)->size = END(p) - pos;
+       FRAG(p)->pos = pos;
+       overlay_save_table( p, p+1 );
+    } else {
+       overlay_insert( p, pos, size);
+       overlay_save_table( p, overlay.count );
+    }
+}
 
 #if DEBUG
 static void print_source(struct Source *p) {
@@ -72,9 +191,9 @@ static int RANGE(int s,int n ) {
 static void usage();
 
 static void setup_overlay(char *filename) {
-    overlay.filename = filename;
-    overlay.fd = open( filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR );
-    if ( overlay.fd < 0 ) {
+    overlay.source.filename = filename;
+    overlay.source.fd = open( filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR );
+    if ( overlay.source.fd < 0 ) {
        perror( filename );
        usage();
     }
@@ -238,45 +357,35 @@ static int find_source(off_t offset) {
     return lo;
 }
 
-#define OBUFSZ 1048576
 static int overlay_merge(char *buf,off_t off,size_t size) {
-    static char obuf[ OBUFSZ ];
 #if DEBUG
     fprintf( stderr, "merge %ld %ld\n", off, size );
 #endif
-    while ( size > 0 ) {
-       size_t n = size < OBUFSZ? size : OBUFSZ;
-       off_t ox = lseek( overlay.fd, off, SEEK_SET );
-#if DEBUG
-       fprintf( stderr, "  seek %ld %ld %ld\n", off, ox, n );
-#endif
-       if ( ox < 0 ) {
-           perror( overlay.filename );
-           return -ENOENT;
-       }
-       if ( ox < off ) {
-           break;
-       }
-       n = read( overlay.fd, obuf, n );
-#if DEBUG
-       fprintf( stderr, "  got %ld\n", n );
-#endif
-       if ( n < 0 ) {
-           perror( overlay.filename );
-            return -ENOENT;
+    // Find nearest overlay data before or at off
+    ssize_t p = overlay_prior_fragment( off );
+    if ( p < 0 ) {
+       p = 0;
+    }
+    for ( ; p < overlay.count && BEG(p) < off+size; p++ ) {
+       size_t delta = FRAG(p)->size;
+       if ( BEG(p) > off ) {
+           size_t skip = BEG(p) - off;
+           off += skip;
+           size -= skip;
+           buf += skip;
+       } else {
+           delta = off - BEG(p);
        }
-       if ( n == 0 ) {
-           break;
+       if ( delta > size ) {
+           delta = size;
        }
-       char *p = obuf;
-       while ( n-- > 0 ) {
-           if ( *p ) {
-               *buf = *p;
-           }
-           p++;
-           buf++;
-           size--;
-           off++;
+       lseek( overlay.source.fd, off, SEEK_SET );
+       while ( delta > 0 ) {
+           size_t n = read( overlay.source.fd, buf, delta );
+           off += n;
+           size -= n;
+           delta -= n;
+           buf += n;
        }
     }
 #if DEBUG
@@ -331,7 +440,7 @@ static int fusefile_read(const char *path, char *buf, size_t size,
                 n, sources.array[i].fd );
 #endif
        ssize_t r = read( sources.array[i].fd, buf + rr, n );
-       if ( overlay.filename ) {
+       if ( overlay.source.filename ) {
            int x = overlay_merge( buf + rr, off + rr, r );
            if ( x ) {
                return x;
@@ -375,6 +484,30 @@ int fusefile_poll(const char *path, struct fuse_file_info *fi,
     return 0;
 }
 
+static void overlay_load() {
+    lseek( overlay.source.fd, overlay.source.to, SEEK_SET );
+    size_t x = 0;
+    size_t size = sizeof( overlay.count );
+    if ( read( overlay.source.fd, &x, size ) != size ) {
+       return;
+    }
+#if DEBUG
+    fprintf( stderr, "overlay: %s with %ld regions\n",
+            overlay.source.filename, x );
+#endif
+    struct Region f = { 0, 0 };
+    size = sizeof( struct Region );
+    while ( x-- > 0 ) {
+       if ( read( overlay.source.fd, &f, size ) != size ) {
+           fprintf( stderr, "%s: bad meta data\n", overlay.source.filename );
+           exit( 1 );
+       }
+#if DEBUG
+       fprintf( stderr, "overlay region: %ld %ld\n", f.pos, f.size );
+#endif
+       overlay_mark( f.pos, f.size );
+    }
+}
 
 /**
  * Write a full block of data over the sources at the offset
@@ -388,8 +521,13 @@ static int write_block(off_t off,const char *buf,size_t size) {
        if ( index < 0 ) {
            return -EIO; // past EOF
        }
-       struct Source *source =
-           overlay.filename? &overlay : &sources.array[ index ];
+       struct Source *source;
+       if ( overlay.source.filename ) {
+           source = &overlay.source;
+           overlay_mark( off, size ); // Mark region as written
+       } else {
+           source = &sources.array[ index ];
+       }
        off_t from = off - source->start + source->from;
        off_t max = source->to - from;
        if ( lseek( source->fd, from, SEEK_SET ) < 0 ) {
@@ -618,7 +756,10 @@ int main(int argc, char *argv[])
     if ( setup_sources( argv, i, argc-i ) ) {
        return 1;
     }
-    overlay.to = sources.size; // Register total size.
+    if ( overlay.source.filename ) {
+       overlay.source.to = sources.size; // Register total size.
+       overlay_load();
+    }
     if ( stat( mnt, &stbuf ) == -1 ) {
        int fd = open( mnt, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR );
        if ( fd < 0 ) {