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
 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
 
 
 .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.
 
 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:
 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
 
 \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
 .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.
 
 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
 
 
 .SH AUTHOR
 
index 8371d1f6c9448d538daa7b4ba1f2fc89feb89676..99424c5fd8ea7f8b1247d404da41cd8be7f06280 100644 (file)
 #include <time.h>
 #include <errno.h>
 
 #include <time.h>
 #include <errno.h>
 
+struct Region {
+    off_t pos;
+    size_t size;
+};
+
 struct Source {
     char *filename;
     ssize_t from;
 struct Source {
     char *filename;
     ssize_t from;
@@ -54,7 +59,121 @@ static struct {
     time_t ctime;
 } times;
 
     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) {
 
 #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) {
 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();
     }
        perror( filename );
        usage();
     }
@@ -238,45 +357,35 @@ static int find_source(off_t offset) {
     return lo;
 }
 
     return lo;
 }
 
-#define OBUFSZ 1048576
 static int overlay_merge(char *buf,off_t off,size_t size) {
 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
 #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
        }
     }
 #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 );
                 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;
            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;
 }
 
     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
 
 /**
  * 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
        }
        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 ) {
        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;
     }
     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 ) {
     if ( stat( mnt, &stbuf ) == -1 ) {
        int fd = open( mnt, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR );
        if ( fd < 0 ) {