Merge branch 'wip/stacked-overlay' into preview
authorRalph Ronnquist <rrq@rrq.au>
Sat, 12 Aug 2023 03:44:10 +0000 (13:44 +1000)
committerRalph Ronnquist <rrq@rrq.au>
Sat, 12 Aug 2023 03:44:10 +0000 (13:44 +1000)
1  2 
fusefile.8
fusefile.c

diff --combined fusefile.8
index 194f4aa83558057c4c62d859dbab9a2219445940,da2b645b7825d2b7cad2a9999682ff37331708a4..b952f5574075e2ae9b0df699ead855873618a772
@@@ -8,8 -8,6 +8,8 @@@ fusefile, fusedisk \- FUSE file mount f
  .br
  .B fusefile \fB-dump\fR \fR[\fIfuse-opts\fR] \fBmountpoint\fR \fR[\fIoverlay\fR] \fIfilename/from-to\fR ...
  .br
 +.B fusefile \fB-push\fR \fR[\fIfuse-opts\fR] \fBmountpoint\fR \fR[\fIoverlay\fR] \fIfilename/from-to\fR ...
 +.br
  .B fusedisk \fR[\fIfuse-opts\fR] \fBmountpoint\fR \fR[\fIoverlay\fR] \fIfilename/from-to\fR ...
  
  .SH DESCRIPTION
@@@ -33,20 -31,19 +33,25 @@@ any new written fused file regions foll
  distinguish between new, written content and old content that comes
  from the fragments.
  
+ By instead using the \fB-overlay:\fIlist\fR argument where \fIlist\fR
+ is a colon-separated list of filenames, \fBfusefile\fR will use those
+ as an ordered stack of overlays and "inject" them as fragments on top
+ of each other.
  The option \fB-dump\fR as first argument together with a fusefile
  setup will print the setup to standard output rather than establishing
 -a fusefile mount. This is of most use with a prior overlay setup,
 -where then the printout includes the portions of updates that have
 -been captured in the overlay. The printout is the series of fusefile
 +a fusefile mount. This is of most use with a prior overlay setup where
 +then the printout includes the portions of updates that have been
 +captured in the overlay. The printout is the series of fusefile
  fragment argments to give in order to intersperse the captured overlay
  portions according to the overlay table.
  
 +The option \fB-push\fR as first argument together with a fusefile
 +setup will push the overlay into the sources (except for
 +write-protected fragments). This is only of use with a prior overlay
 +setup where then the updates that have been captured in the overlay
 +get pushed into the fragments.
 +
  \fBfusedisk\fR is a helper script to set up a \fBfusefile\fR as a
  block device (via \fIfuseblk\fR) by using the device mapper
  (\fBdmsetup\fR) to manage an empty block device mapping where content
diff --combined fusefile.c
index 69750fad1507d9db3626b9927bac2bb642235ab2,e05b90ebdf4c8a3de0c45d56a562dc71f4031fc4..62431d28a6894ba2973388ee7999971bb0bb2e2e
@@@ -41,6 -41,8 +41,8 @@@ struct Region 
      off_t end;
  };
  
+ #define REGIONKEEP(r) ((r,beg == r.end)?: 0 : 1)
  struct Source {
      char *filename;
      ssize_t from;
@@@ -50,6 -52,8 +52,8 @@@
      int dirty;
  };
  
+ #define ENDSOURCE( S ) ( S.start + ( S.to - S.from ) )
  static struct {
      struct Source *array;
      int count;
@@@ -65,12 -69,12 +69,12 @@@ static struct 
  /**
   * Overlay
   */
- static struct {
+ static struct Overlay {
      struct Source source;
      struct Region *table;
      size_t count;
      size_t limit;
- } overlay;
+ } overlay; // The current overlay.
  
  static void usage();
  
@@@ -270,13 -274,24 +274,24 @@@ static void overlay_mark(off_t beg,off_
  #endif
  }
  
- static void setup_overlay(char *filename) {
-     overlay.source.filename = filename;
-     overlay.source.fd = open( filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR );
-     if ( overlay.source.fd < 0 ) {
-       perror( filename );
+ /**
+  * Capture overlay filenames for processing after source fragments.
+  */
+ static void overlay_setup(char *filenames) {
+     overlay.source.filename = filenames;
+ }
+ // Duplicate the source record data at the given index.
+ static void dup_source_item(int index) {
+     sources.count++;
+     sources.array = realloc(
+       sources.array, sources.count * sizeof( struct Source ) );
+     if ( sources.array == 0 ) {
+       fprintf( stderr, "** OOM when expanding frament table\n" );
        usage();
      }
+     memcpy( &sources.array[ index+1 ], &sources.array[ index ],
+           sizeof( struct Source ) );
  }
  
  #if DEBUG
@@@ -626,6 -641,111 +641,111 @@@ static void overlay_load() 
      }
  }
  
+ /**
+  * Inject an overlay fragment into the source table, end return the
+  * end of the injected fragment.
+  */
+ static off_t overlay_inject_from_region(off_t beg,off_t end) {
+     int index = find_source( beg );
+     if ( index < 0 ) {
+       fprintf( stderr, "** Injecting %s at %ld failed\n",
+                overlay.source.filename, beg );
+       usage();
+     }
+     if ( end > ENDSOURCE( sources.array[ index ] ) ) {
+       end = ENDSOURCE( sources.array[ index ] );
+     }
+     struct Region frags[3] = {
+       { sources.array[ index ].start, beg },
+       { beg, ENDSOURCE( sources.array[ index ] ) },
+       { ENDSOURCE( sources.array[ index ] ), end } };
+     ssize_t size = frags[0].end - frags[0].beg;
+     if ( size ) {
+       // "Duplicate" the indexed source data, copying the filename
+       dup_source_item( index );
+       sources.array[ index ].to = sources.array[ index ].from + size;
+       index++;
+       sources.array[ index ].start = beg;
+       sources.array[ index ].from = sources.array[ index-1 ].to;
+     }
+     size = frags[2].end       - frags[2].beg;
+     if ( size ) {
+       dup_source_item( index );
+       sources.array[ index+1 ].start = frags[2].beg;
+       sources.array[ index+1 ].from = sources.array[ index+1 ].to -size;
+     }
+     // Replace the [index] fragment
+     sources.array[ index ].filename = overlay.source.filename;
+     sources.array[ index ].start = beg;
+     sources.array[ index ].from = beg;
+     sources.array[ index ].to = end;
+     sources.array[ index ].fd = overlay.source.fd; //?
+     return end;
+ }
+ /**
+  * Inject the current (unopened) overlay into the source list.
+  */
+ static void overlay_inject() {
+     overlay.source.fd = open( overlay.source.filename, O_RDONLY );
+     if ( overlay.source.fd < 0 ) {
+       perror( overlay.source.filename );
+       usage();
+     }
+     
+     if ( lseek( overlay.source.fd, overlay.source.to, SEEK_SET ) < 0 ) {
+       perror( overlay.source.filename );
+       usage();
+     }
+     size_t count = 0;
+     size_t size = sizeof( overlay.count );
+     if ( read( overlay.source.fd, &count, size ) != size ) {
+       fprintf( stderr, "** error injecting %s\n", overlay.source.filename );
+       usage();
+     }
+     if ( count == 0 ) {
+       close( overlay.source.fd );
+       return;
+     }
+     size = count * sizeof( struct Region );
+     overlay.table = calloc( sizeof( struct Region ), count );
+     if ( read( overlay.source.fd, overlay.table, size ) != size ) {
+       fprintf( stderr, "** error injecting %s\n", overlay.source.filename );
+       usage();
+     }
+     size_t i;
+     for ( i = 0; i < count; i++ ) {
+       off_t beg = overlay.table[i].beg;
+       while ( beg < overlay.table[i].end ) {
+           beg = overlay_inject_from_region( beg, overlay.table[i].end );
+       }
+     }
+     free( overlay.table );
+     overlay.table = 0;
+     close( overlay.source.fd );
+ }
+ /**
+  * Each stacked overlay file is nested into to source list
+  */
+ static void overlay_post_setup() {
+     char *end;
+     while ( ( end = strchr( overlay.source.filename, ':' ) ) ) {
+       *end = 0; //  
+       overlay_inject();
+       overlay.source.filename = end + 1;
+     }
+     if ( *overlay.source.filename ) {
+       overlay.source.fd = open( overlay.source.filename,
+                                 O_RDWR | O_CREAT, S_IRUSR | S_IWUSR );
+       if ( overlay.source.fd < 0 ) {
+           perror( overlay.source.filename );
+           usage();
+       }
+       overlay_load();
+     }
+ }
  /**
   * Write a full block of data over the sources at the offset
   */
@@@ -717,40 -837,6 +837,40 @@@ static int fusefile_write(const char *p
      return size;
  }
  
 +#define PUSHBUF 104857600
 +/**
 + * Write data from overlay to source.
 + */
 +static void push_oly(off_t beg, off_t end) {
 +    static char * buffer = 0;
 +    // Pretend that there isn't an overlay
 +    char *filename = overlay.source.filename;
 +    if ( buffer == 0 ) {
 +      buffer = malloc( PUSHBUF );
 +      if ( buffer == 0 ) {
 +          fprintf( stderr, "** OOM!!\n" );
 +          exit( 1 );
 +      }
 +    }
 +    overlay.source.filename = 0;
 +    while ( beg < end ) {
 +      off_t size = end - beg;
 +      if ( size > PUSHBUF ) {
 +          size = PUSHBUF;
 +      }
 +      if ( lseek( overlay.source.fd, beg, SEEK_SET ) < 0 ) {
 +          fprintf( stderr, "** Cannot seek overlay at %ld\n", beg );
 +          break;
 +      }
 +      size = read( overlay.source.fd, buffer, size );
 +      if ( write_block( beg, buffer, size ) < 0 ) {
 +          fprintf( stderr, "** Cannot push %ld bytes at %ld\n", size, beg );
 +      }
 +      beg += size;
 +    }
 +    overlay.source.filename = filename;
 +}
 +
  static void fusefile_destroy(void *data) {
      char *mnt = (char*) data; // As passed to fuse_main
  #if DEBUG
@@@ -833,12 -919,10 +953,10 @@@ void *fusefile_init(struct fuse_conn_in
      return 0;
  }
  
- #define ENDSOURCE( S ) ( S.start + ( S.to - S.from ) )
  /**
   * Dump the current fragmentation to stdout.
   */
 -static int dump_fragments() {
 +static int dump_fragments(int push) {
      int oly = 0;
      int src = 0;
      size_t pos = 0;
        for ( ; src < sources.count && 
                  ENDSOURCE( sources.array[ src ] ) <= x; src++ ) {
            // Dump sources.array[src] in full
 -          fprintf( stdout, "%s/%ld:%ld\n",
 -                   sources.array[ src ].filename,
 -                   pos - sources.array[ src ].start,
 -                   sources.array[ src ].to );
 +          if ( !push ) {
 +              fprintf( stdout, "%s/%ld:%ld\n",
 +                       sources.array[ src ].filename,
 +                       pos - sources.array[ src ].start,
 +                       sources.array[ src ].to );
 +          }
            pos = ENDSOURCE( sources.array[ src ] );
        }
        if ( ( src < sources.count ) && ( sources.array[ src ].start < x ) ) {
            // Dump sources.array[src] up to x;
 -          fprintf( stdout, "%s/%ld:%ld\n",
 -                   sources.array[ src ].filename,
 -                   pos - sources.array[ src ].start,
 -                   x - sources.array[ src ].start );
 +          if ( !push ) {
 +              fprintf( stdout, "%s/%ld:%ld\n",
 +                       sources.array[ src ].filename,
 +                       pos - sources.array[ src ].start,
 +                       x - sources.array[ src ].start );
 +          }
            pos = ENDSOURCE( sources.array[ src ] );
        }
        if ( oly < overlay.count ) {
 -          fprintf( stdout, "%s/%ld:%ld\n",
 -                   overlay.source.filename,
 -                   overlay.table[ oly ].beg,
 -                   overlay.table[ oly ].end );
 +          if ( !push ) {
 +              fprintf( stdout, "%s/%ld:%ld\n",
 +                       overlay.source.filename,
 +                       overlay.table[ oly ].beg,
 +                       overlay.table[ oly ].end );
 +          } else {
 +              push_oly( overlay.table[ oly ].beg, overlay.table[ oly ].end );
 +          }
            pos = overlay.table[ oly++ ].end;
        }
        for ( ; src < sources.count &&
@@@ -968,11 -1044,10 +1086,10 @@@ int main(int argc, char *argv[]
      }
      fuseargc = i;
      mnt = argv[ i++ ]; // First non-option argument is the mount pount
-     char *overlaytag = "-overlay:";
-     int overlaytagsize = strlen( overlaytag );
-     if ( strncmp( argv[i], overlaytag, overlaytagsize ) == 0 ) {
-       // consume "-overlay:filename"
-       setup_overlay( argv[i++] + overlaytagsize ); // Need a writable file
+     if ( strcmp( argv[i], "-overlay:" ) == 0 ) {
+       // consume "-overlay:filename[,filename]*"
+       // Verify file access; to overlay must be writable.
+       overlay_setup( argv[i++] + strlen( "-overlay:" ) );
        if ( i >= argc ) {
            usage();
        }
      }
      if ( overlay.source.filename ) {
        overlay.source.to = sources.size; // Register total size.
-       overlay_load();
+       overlay_post_setup();
      }
      if ( stat( mnt, &stbuf ) == -1 ) {
        int fd = open( mnt, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR );
      }
      fuseargc = setup_argv( fuseargc, &argv );
      if ( strcmp( "-dump", argv[ 1 ] ) == 0 ) {
 -      return dump_fragments();
 +      return dump_fragments( 0 );
 +    }
 +    if ( strcmp( "-push", argv[ 1 ] ) == 0 ) {
 +      return dump_fragments( 1 );
      }
      struct fuse_args args = FUSE_ARGS_INIT( fuseargc, argv );
      if ( fuse_parse_cmdline( &args, &mnt, &mt, &fg ) ) {