From 6637283f63bfa51cbf0ae0746a6702cb2b6014e7 Mon Sep 17 00:00:00 2001 From: Ralph Ronnquist Date: Tue, 9 Aug 2022 21:21:11 +1000 Subject: [PATCH] completed overlay option --- fusefile.8 | 21 ++++- fusefile.c | 223 +++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 202 insertions(+), 42 deletions(-) diff --git a/fusefile.8 b/fusefile.8 index b4bd50d..183a362 100644 --- a/fusefile.8 +++ b/fusefile.8 @@ -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 diff --git a/fusefile.c b/fusefile.c index 8371d1f..99424c5 100644 --- a/fusefile.c +++ b/fusefile.c @@ -34,6 +34,11 @@ #include #include +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 ) { -- 2.39.2