X-Git-Url: https://git.rrq.au/?a=blobdiff_plain;f=fusefile.c;h=a1bf2bd926792844ecee3c989dd131da641d69e2;hb=HEAD;hp=59dcaa5fb746b13f90098c69c7e278b21b73490e;hpb=f2a64b0bd6a5624ec37ec31a07747bfb063a7bb6;p=rrq%2Ffusefile.git diff --git a/fusefile.c b/fusefile.c index 59dcaa5..a1bf2bd 100644 --- a/fusefile.c +++ b/fusefile.c @@ -1,8 +1,8 @@ /*** fusefile - overlay a file path with a concatenation of parts of - other files, read only. + other files. - Copyright (C) 2019 Ralph Ronnquist + Copyright (C) 2019- Ralph Ronnquist This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -33,6 +33,15 @@ #include #include #include +#include +#include + +struct Region { + off_t beg; + off_t end; +}; + +#define REGIONKEEP(r) ((r,beg == r.end)?: 0 : 1) struct Source { char *filename; @@ -40,8 +49,11 @@ struct Source { ssize_t to; ssize_t start; // starting position in concatenated file int fd; + int dirty; }; +#define ENDSOURCE( S ) ( S.start + ( S.to - S.from ) ) + static struct { struct Source *array; int count; @@ -53,7 +65,236 @@ static struct { time_t mtime; time_t ctime; } times; - + +/** + * Overlay + */ +static struct Overlay { + struct Source source; + struct Region *table; + size_t count; + size_t limit; +} overlay; // The current overlay. + +static void usage(); + +/** + * Find the nearest overlay.table region below pos. Returns the index, + * or -1 if there is none, i.e. pos < overlay.table[0]. + */ +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 overlay.table[m].beg <= pos? m : -1; + } + if ( overlay.table[m].beg <= pos ) { + lo = m; + } else { + hi = m; + } + } + return -1; +} + +/** + * Save the entry count for overlay.table as 64-bit integer + * immediately following the overlay content at the index + * corresponding to the fused file size. + */ +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; + } + if ( overlay.source.dirty++ > 1000 ) { + fsync( overlay.source.fd ); + overlay.source.dirty = 0; + } +} + +/** + * Update the on-disk cache of overlay.table between the given + * indexes. The table is laid out immediately following the table + * count with each region saved as two 64-bit unsigned integers. + */ +static void overlay_save_table(size_t lo,size_t hi) { + char *p = (char *) &overlay.table[ 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; + } + if ( overlay.source.dirty++ > 1000 ) { + fsync( overlay.source.fd ); + overlay.source.dirty = 0; + } +} + +/** + * Insert a new region at index p, with previous portion [p,count] + * moved up to make space. + */ +static void overlay_insert(size_t p,off_t beg,off_t end) { + size_t bytes; + // Grow the table if needed + 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( (char*) &overlay.table[ p+1 ], + (char*) &overlay.table[ p ], + bytes ); + } + overlay.table[ p ].beg = beg; + overlay.table[ p ].end = end; + overlay_save_count(); +} + +/** + * Delete the region entry at p by moving the portion [p+1,count] + * down. + */ +static void overlay_delete(size_t p) { + size_t bytes = ( --overlay.count - p ) * sizeof( struct Region ); + if ( bytes ) { + memmove( (char*) &overlay.table[ p ], + (char*) &overlay.table[ p+1 ], + bytes ); + } +} + +/** + * Mark the given region as updated, i.e. written to the overlay. The + * mark region may attach to prior marked regions or be a new, + * separate region. If attaching, it causes the prior regions to + * expand and the table adjusted by deleting any regions that become + * fully contained in other regions. + */ +static void overlay_mark(off_t beg,off_t end) { +#if DEBUG + fprintf( stderr, "overlay_mark( %ld, %ld )\n", beg, end ); +#endif + int deleted = 0; + ssize_t q; + ssize_t p = overlay_prior_fragment( beg ); + // p is the nearest region below or at beg (or -1) + if ( p >= 0 && beg <= overlay.table[p].end ) { + // p overlaps mark region + if ( end <= overlay.table[p].end ) { + // region p covers mark region already +#if DEBUG + fprintf( stderr, "overlay covering ( %ld %ld )\n", + overlay.table[p].beg, overlay.table[p].end ); +#endif + return; + } + // the new mark region extends region p + overlay.table[p].end = end; + q = p+1; + while ( q < overlay.count && + overlay.table[q].beg <= overlay.table[p].end ) { + // Extended region merges with subsequent region + if ( overlay.table[p].end < overlay.table[q].end ) { + overlay.table[p].end = overlay.table[q].end; + } + overlay_delete( q ); + deleted++; + } + if ( deleted ) { + overlay_save_count(); + q = overlay.count; + } + overlay_save_table( p, q ); +#if DEBUG + fprintf( stderr, "overlay expand ( %ld %ld ) deleted %d\n", + overlay.table[p].beg, overlay.table[p].end, deleted ); +#endif + return; + } + // The prior region p does not expand into new mark region + p++; // subsequent region + if ( p >= overlay.count || end < overlay.table[p].beg ) { + // New mark region is a separate region at p + overlay_insert( p, beg, end ); +#if DEBUG + fprintf( stderr, "overlay new ( %ld %ld )\n", + overlay.table[p].beg, overlay.table[p].end ); +#endif + overlay_save_table( p, overlay.count ); + return; + } + // New marks start before and overlap with region p => change p + // and handle any subsequent regions being covered + overlay.table[p].beg = beg; + q = p+1; + if ( overlay.table[p].end < end ) { + overlay.table[p].end = end; + while ( q < overlay.count && + overlay.table[q].beg <= overlay.table[p].end ) { + if ( overlay.table[p].end < overlay.table[q].end ) { + overlay.table[p].end = overlay.table[q].end; + } + overlay_delete( q ); + deleted++; + } + if ( deleted ) { + overlay_save_count(); + q = overlay.count; + } + } + overlay_save_table( p, q ); +#if DEBUG + fprintf( stderr, "overlay before ( %ld %ld ) deleted %d\n", + overlay.table[p].beg, overlay.table[p].end, deleted ); +#endif +} + +/** + * 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(); + } + // copy elements from [index] to [count-1] one element down + size_t size = ( sources.count - index - 1 ) * sizeof( struct Source ); + memmove( &sources.array[ index+1 ], &sources.array[ index ], size ); +} + #if DEBUG static void print_source(struct Source *p) { fprintf( stderr, "%p { %s, %ld, %ld, %ld, %d }\n", @@ -67,6 +308,93 @@ static int RANGE(int s,int n ) { return ( s == n ) && *(range+c) == 0; } +static int setup_source(struct Source *p,char *frag) { + struct stat filestat; + // Open the fragment file rw if possible, else ro + // First try the fragment in full, thereafter with range appendix + if ( stat( frag, &filestat ) == 0 ) { + p->filename = strdup( frag ); + range = 0; + } else { + range = strrchr( frag, '/' ); // last '/' + p->filename = range? strndup( frag, range - frag ) : frag; + } + p->fd = open( p->filename, O_RDWR ); + int rdonly = 0; + if ( p->fd < 0 ) { + rdonly = 1; + p->fd = open( p->filename, O_RDONLY ); + } + if ( p->fd < 0 ) { + perror( p->filename ); + return 1; // Error return + } + if ( ( range != 0 ) && stat( p->filename, &filestat ) ) { + perror( p->filename ); + return 1; + } + if ( rdonly ) { + fprintf( stderr, "** %s opened read-only\n", p->filename ); + } + p->from = 0; + if ( S_ISBLK( filestat.st_mode ) ) { + // Block devices report size differently: + if ( ioctl( p->fd, BLKGETSIZE64, &filestat.st_size ) < 0 ) { + perror( p->filename ); + } +#if DEBUG + fprintf( stderr, "block device size = %ld\n", filestat.st_size ); +#endif + } + p->to = filestat.st_size; + // Process any range variation + if ( range && *(++range) ) { + long int a,b; + if ( 0 ) { + } else if ( RANGE( sscanf( range, "%ld:%ld%n", &a, &b, &c ), 2 )) { + p->from = ( a < 0 )? ( p->to + a ) : a; + p->to = ( b < 0 )? ( p->to + b ) : b; + } else if ( RANGE( sscanf( range, "%ld+%ld%n", &a, &b, &c ), 2 )) { + p->from = ( a < 0 )? ( p->to + a ) : a; + p->to = ( ( b < 0 )? p->to : p->from ) + b; + } else if ( RANGE( sscanf( range, "%ld+%n", &a, &c ), 1 )) { + p->from = ( a < 0 )? ( p->to + a ) : a; + } else if ( RANGE( sscanf( range, ":%ld%n", &b, &c ), 1 )) { + p->to = ( b < 0 )? ( p->to + b ) : b; + } else if ( RANGE( sscanf( range, "%ld:%n", &a, &c ), 1 )) { + p->from = ( a < 0 )? ( p->to + a ) : a; + } else if ( RANGE( sscanf( range, "%ld%n", &a, &c ), 1 )) { + if ( a >= 0 ) { + p->from = a; + } else { + p->from = p->to + a; + } + } else if ( RANGE( sscanf( range, ":%n", &c), 0 ) ) { + // to end from start + } else { + fprintf( stderr, "** BAD RANGE: %s\n", frag ); + return 1; + } + } + if ( ( filestat.st_mode & S_IFMT ) == S_IFCHR ) { + filestat.st_size = p->to; // Pretend size of character device + } + if ( p->from < 0 ) { + p->from = 0; + } + if ( p->to > filestat.st_size ) { + p->to = filestat.st_size; + } + if ( p->from >= p->to || p->from >= filestat.st_size ) { + fprintf( stderr, "** BAD RANGE: %s [%ld:%ld]\n", + frag, p->from, p->to ); + return 1; + } + p->start = sources.size; // the fusefile position of fragment + sources.size += p->to - p->from; + return 0; +} + static int setup_sources(char **argv,int i,int n) { sources.array = calloc( n, sizeof( struct Source ) ); if ( sources.array == 0 ) { @@ -76,75 +404,10 @@ static int setup_sources(char **argv,int i,int n) { int j = 0; sources.size = 0; for ( ; j < n; i++, j++ ) { - struct stat filestat; struct Source *p = sources.array + j; - // Open the fragment file rw if possible, else ro - range = strrchr( argv[i], '/' ); // last '/' - p->filename = range? strndup( argv[i], range - argv[i] ) : argv[i]; - p->fd = open( p->filename, O_RDWR ); - int rdonly = 0; - if ( p->fd < 0 ) { - rdonly = 1; - p->fd = open( p->filename, O_RDONLY ); - } - if ( p->fd < 0 ) { - perror( p->filename ); - return 1; // Error return - } - if ( stat( p->filename, &filestat ) ) { - perror( p->filename ); - return 1; - } - if ( rdonly ) { - fprintf( stderr, "** %s opened read-only\n", p->filename ); - } - p->from = 0; - p->to = filestat.st_size; - // Process any range variation - if ( range && *(++range) ) { - int a,b; - if ( 0 ) { - } else if ( RANGE( sscanf( range, "%d:%d%n", &a, &b, &c ), 2 )) { - p->from = ( a < 0 )? ( p->to + a ) : a; - p->to = ( b < 0 )? ( p->to + b ) : b; - } else if ( RANGE( sscanf( range, "%d+%d%n", &a, &b, &c ), 2 )) { - p->from = ( a < 0 )? ( p->to + a ) : a; - p->to = ( ( b < 0 )? p->to : p->from ) + b; - } else if ( RANGE( sscanf( range, "%d+%n", &a, &c ), 1 )) { - p->from = ( a < 0 )? ( p->to + a ) : a; - } else if ( RANGE( sscanf( range, ":%d%n", &b, &c ), 1 )) { - p->to = ( b < 0 )? ( p->to + b ) : b; - } else if ( RANGE( sscanf( range, "%d:%n", &a, &c ), 1 )) { - p->from = ( a < 0 )? ( p->to + a ) : a; - } else if ( RANGE( sscanf( range, "%d%n", &a, &c ), 1 )) { - if ( a >= 0 ) { - p->from = a; - } else { - p->from = p->to + a; - } - } else if ( RANGE( sscanf( range, ":%n", &c), 0 ) ) { - // to end from start - } else { - fprintf( stderr, "** BAD RANGE: %s\n", argv[i] ); - return 1; - } - } - if ( ( filestat.st_mode & S_IFMT ) == S_IFCHR ) { - filestat.st_size = p->to; // Pretend size of character device - } - if ( p->from < 0 ) { - p->from = 0; - } - if ( p->to > filestat.st_size ) { - p->to = filestat.st_size; - } - if ( p->from >= p->to || p->from >= filestat.st_size ) { - fprintf( stderr, "** BAD RANGE: %s [%ld:%ld]\n", - argv[i], p->from, p->to ); + if ( setup_source( p, argv[i] ) ) { return 1; } - p->start = sources.size; // the fusefile position of fragment - sources.size += p->to - p->from; #if DEBUG print_source( p ); #endif @@ -200,79 +463,88 @@ static int find_source(off_t offset) { if ( offset >= sources.size ) { return -1; } -#if DEBUG - fprintf( stderr, "find_source( %ld )\n", offset ); -#endif while ( lo + 1 < hi ) { int m = ( lo + hi ) / 2; if ( offset < sources.array[ m ].start ) { -#if DEBUG - fprintf( stderr, " offset < [%d].start: %ld\n", - m, sources.array[ m ].start ); -#endif hi = m; } else { -#if DEBUG - fprintf( stderr, " offset >= [%d].start: %ld\n", - m, sources.array[ m ].start ); -#endif lo = m; } } + return lo; +} + +static int overlay_merge(char *buf,off_t beg,off_t end) { #if DEBUG - fprintf( stderr, "found %d\n", lo ); + fprintf( stderr, "merge %ld %ld\n", beg, end ); #endif - return lo; + // Find nearest overlay data before or at beg + ssize_t p = overlay_prior_fragment( beg ); + if ( p < 0 ) { + p = 0; + } + for ( ; p < overlay.count && overlay.table[p].beg < end; p++ ) { + if ( overlay.table[p].end < beg ) { + continue; + } + if ( overlay.table[p].beg > beg ) { + size_t delta = overlay.table[p].beg - beg; + buf += delta; + beg += delta; + } + size_t size = ( overlay.table[p].end <= end )? + ( overlay.table[p].end - beg ) : ( end - beg ); + lseek( overlay.source.fd, beg, SEEK_SET ); + while ( size > 0 ) { + size_t n = read( overlay.source.fd, buf, size ); + size -= n; + buf += n; + beg += n; // + } + } + return 0; } // Read bytes from in file static int fusefile_read(const char *path, char *buf, size_t size, off_t off, struct fuse_file_info *fi) { -#if DEBUG - fprintf( stderr, "fusefile_read( %s )\n", path ); -#endif if( strcmp( path, "/" ) != 0 ) { return -ENOENT; } -#if DEBUG - fprintf( stderr, "read %ld %ld\n", off, size ); -#endif size_t rr = 0; // total reading - while ( size > 0 ) { #if DEBUG - fprintf( stderr, " find_source %ld %ld\n", off, size ); + fprintf( stderr, "fusefile_read %ld + %ld\n", off, size ); #endif + while ( size > 0 ) { int i = find_source( off ); if ( i < 0 ) { return ( off == sources.size )? rr : -ENOENT; } +#if DEBUG + fprintf( stderr, " item: %d ", i ); + print_source(& sources.array[i] ); +#endif if ( sources.array[i].fd < 0 ) { return -ENOENT; } -#if DEBUG - print_source( &sources.array[i] ); -#endif times.atime = time( 0 ); size_t b = off - sources.array[i].start + sources.array[i].from; size_t n = sources.array[i].to - b; if ( n > size ) { n = size; } -#if DEBUG - fprintf( stderr, " seek fd=%d to %ld\n", sources.array[i].fd, b ); -#endif + if ( sources.array[i].dirty ) { + fsync( sources.array[i].fd ); + sources.array[i].dirty = 0; + } if ( lseek( sources.array[i].fd, b, SEEK_SET ) < 0 ) { perror( sources.array[i].filename ); return -ENOENT; } -#if DEBUG - fprintf( stderr, " now read %ld from fd=%d\n", - n, sources.array[i].fd ); -#endif ssize_t r = read( sources.array[i].fd, buf + rr, n ); #if DEBUG - fprintf( stderr, " got %ld bytes\n", r ); + fprintf( stderr, " got: %ld bytes of %ld at %ld\n", r, n, rr ); #endif if ( r < 0 ) { perror( sources.array[i].filename ); @@ -281,6 +553,16 @@ static int fusefile_read(const char *path, char *buf, size_t size, if ( r == 0 ) { break; } + if ( overlay.source.filename ) { + if ( overlay.source.dirty ) { + fsync( overlay.source.fd ); + overlay.source.dirty = 0; + } + int x = overlay_merge( buf + rr, off + rr, off + rr + r ); + if ( x ) { + return x; + } + } rr += r; off += r; size -= r; @@ -309,6 +591,169 @@ 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.beg, f.end ); +#endif + overlay_mark( f.beg, f.end ); + } +} + +/** + * 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, end }, + { end, ENDSOURCE( sources.array[ index ] ) } }; +#if DEBUG + int i; + for ( i = 0; i < 3; i++ ) { + fprintf( stderr, "frags[%d] = (%ld, %ld)\n", + i, frags[i].beg, frags[i].end ); + } +#endif + ssize_t size = frags[0].end - frags[0].beg; + if ( size ) { + // Handle any portion before injection point. + dup_source_item( index ); + off_t point = sources.array[ index ].from + size; + sources.array[ index ].to = point; +#if DEBUG + fprintf( stderr, "item %d ", index ); + print_source( &sources.array[ index ] ); +#endif + // Adjust item after injection point + index++; + sources.array[ index ].start = beg; + sources.array[ index ].from = point; +#if DEBUG + fprintf( stderr, "item %d adjust ", index ); + print_source( &sources.array[ index ] ); +#endif + } + size = frags[2].end - frags[2].beg; + if ( size ) { + // Handle any remaining portion following injection fragment + dup_source_item( index ); + sources.array[ index+1 ].start = frags[2].beg; + sources.array[ index+1 ].from += frags[1].end - frags[1].beg; +#if DEBUG + fprintf( stderr, "item %d ", index+1 ); + print_source( &sources.array[ index+1 ] ); +#endif + } + // Set up the injection fragment + sources.array[ index ].filename = overlay.source.filename; + sources.array[ index ].from = beg; + sources.array[ index ].to = end; + sources.array[ index ].fd = overlay.source.fd; +#if DEBUG + fprintf( stderr, "item %d ", index ); + print_source( &sources.array[ index ] ); +#endif + 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 ); + size_t n; + if ( ( n = read( overlay.source.fd, &count, size ) ) != size ) { + if ( n != 0 ) { + fprintf( stderr, "** error injecting %s\n", + overlay.source.filename ); + usage(); + } + fprintf( stderr, "** ignoring empty %s\n", overlay.source.filename ); + } + 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 ) { +#if DEBUG + fprintf( stderr, "inject [%ld,%ld] ", beg, overlay.table[i].end ); + print_source( &overlay.source ); +#endif + + beg = overlay_inject_from_region( beg, overlay.table[i].end ); + } + } + free( overlay.table ); + overlay.table = 0; +} + +/** + * 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 @@ -317,12 +762,16 @@ static int write_block(off_t off,const char *buf,size_t size) { #if DEBUG fprintf( stderr, "write_block( %ld, ?, %ld )\n", off, size ); #endif + if ( overlay.source.filename ) { + overlay_mark( off, off + size ); // Mark region as written + } while ( size > 0 ) { int index = find_source( off ); // index of source file if ( index < 0 ) { return -EIO; // past EOF } - struct Source *source = &sources.array[ index ]; + struct Source *source = overlay.source.filename? + &overlay.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 ) { @@ -340,6 +789,10 @@ static int write_block(off_t off,const char *buf,size_t size) { size -= n; off += n; } + if ( source->dirty++ >= 1000 ) { + fsync( source->fd ); + source->dirty = 0; + } } return 0; } @@ -352,7 +805,6 @@ static int fusefile_write_buf(const char *path, struct fuse_bufvec *buf, if ( strcmp( path, "/" ) != 0 ) { return -ENOENT; } - size_t size = 0; int i; for ( i = 0; i < buf->count; i++ ) { @@ -393,6 +845,40 @@ static int fusefile_write(const char *path, const char *buf, size_t size, 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 @@ -403,6 +889,20 @@ static void fusefile_destroy(void *data) { } } +static void fsync_all_dirty() { + int i = 0; + for ( ; i < sources.count; i++ ) { + if ( sources.array[i].dirty ) { + fsync( sources.array[i].fd ); + sources.array[i].dirty = 0; + } + } + if ( overlay.source.filename && overlay.source.dirty ) { + fsync( overlay.source.fd ); + overlay.source.dirty = 0; + } +} + static int fusefile_flush(const char *path, struct fuse_file_info *info) { #if DEBUG fprintf( stderr, "fusefile_flush( %s )\n", path ); @@ -410,6 +910,7 @@ static int fusefile_flush(const char *path, struct fuse_file_info *info) { if ( strcmp( path, "/" ) != 0 ) { return -ENOENT; } + fsync_all_dirty(); return 0; } @@ -430,6 +931,7 @@ static int fusefile_fsync(const char *path, int x, struct fuse_file_info *fi) { if ( strcmp( path, "/" ) != 0 ) { return -ENOENT; } + fsync_all_dirty(); return 0; } @@ -459,8 +961,63 @@ void *fusefile_init(struct fuse_conn_info *fci) { return 0; } +/** + * Dump the current fragmentation to stdout. + */ +static int dump_fragments(int push) { + int oly = 0; + int src = 0; + size_t pos = 0; + while ( src < sources.count ) { + size_t x = ( oly < overlay.count )? + overlay.table[ oly ].beg : sources.size; + for ( ; src < sources.count && + ENDSOURCE( sources.array[ src ] ) <= x; src++ ) { + // Dump sources.array[src] in full + if ( !push ) { + fprintf( stdout, "%s/%ld:%ld\n", + sources.array[ src ].filename, + pos - sources.array[ src ].start + + sources.array[ src ].from, + 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; + if ( !push ) { + fprintf( stdout, "%s/%ld:%ld\n", + sources.array[ src ].filename, + sources.array[ src ].from + + pos - sources.array[ src ].start, + x - sources.array[ src ].start + + sources.array[ src ].from + ); + } + pos = ENDSOURCE( sources.array[ src ] ); + } + if ( oly < overlay.count ) { + 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 && + ENDSOURCE( sources.array[ src ] ) <= pos; src++ ) { + // Just skip these fragments. + } + } + return( 0 ); +} + static struct fuse_operations fusefile_oper = { .getattr = fusefile_getattr, + // NYI .fgetattr = fusefile_fgetattr, .chmod = fusefile_chmod, .open = fusefile_open, .read = fusefile_read, @@ -468,9 +1025,11 @@ static struct fuse_operations fusefile_oper = { .write = fusefile_write, .write_buf = fusefile_write_buf, .destroy = fusefile_destroy, + // NYI .access = fusefile_access, .flush = fusefile_flush, .release = fusefile_release, .fsync = fusefile_fsync, + // NYI .ftruncate = fusefile_ftruncate, .truncate = fusefile_truncate, //.truncate = fusefile_truncate, //.release = fusefile_release, @@ -494,6 +1053,7 @@ static int setup_argv(int argc,char ***argv) { // note: (*argv)[ argc ] is the mount point argument char *OURS[] = { "-odefault_permissions", + //"-s", // Forced single-threading (*argv)[ argc ] }; #define OURSN ( sizeof( OURS ) / sizeof( char* ) ) @@ -539,9 +1099,21 @@ int main(int argc, char *argv[]) } fuseargc = i; mnt = argv[ i++ ]; // First non-option argument is the mount pount + #define OVERLAYTAG "-overlay:" + if ( strncmp( argv[i], OVERLAYTAG, strlen( OVERLAYTAG ) ) == 0 ) { + // consume "-overlay:filename[,filename]*" + overlay_setup( argv[i++] + strlen( OVERLAYTAG ) ); + if ( i >= argc ) { + usage(); + } + } if ( setup_sources( argv, i, argc-i ) ) { return 1; } + if ( overlay.source.filename ) { + overlay.source.to = sources.size; // Register total size. + overlay_post_setup(); + } if ( stat( mnt, &stbuf ) == -1 ) { int fd = open( mnt, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR ); if ( fd < 0 ) { @@ -574,6 +1146,12 @@ int main(int argc, char *argv[]) } } fuseargc = setup_argv( fuseargc, &argv ); + if ( strcmp( "-dump", argv[ 1 ] ) == 0 ) { + 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 ) ) { return 1;