X-Git-Url: https://git.rrq.au/?a=blobdiff_plain;f=fusefile.c;h=eeb0e72789f1e322b52419b2fcd04ebf3b995fc1;hb=e46d32a7a6068d96db47e39c959105ee2acda8ba;hp=99424c5fd8ea7f8b1247d404da41cd8be7f06280;hpb=6637283f63bfa51cbf0ae0746a6702cb2b6014e7;p=rrq%2Ffusefile.git diff --git a/fusefile.c b/fusefile.c index 99424c5..eeb0e72 100644 --- a/fusefile.c +++ b/fusefile.c @@ -35,8 +35,8 @@ #include struct Region { - off_t pos; - size_t size; + off_t beg; + off_t end; }; struct Source { @@ -45,6 +45,7 @@ struct Source { ssize_t to; ssize_t start; // starting position in concatenated file int fd; + int dirty; }; static struct { @@ -69,18 +70,20 @@ static struct { 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 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 BEG( m ) < pos? m : -1; + return overlay.table[m].beg <= pos? m : -1; } - if ( BEG( m ) <= pos ) { + if ( overlay.table[m].beg <= pos ) { lo = m; } else { hi = m; @@ -89,6 +92,11 @@ static ssize_t overlay_prior_fragment(off_t pos) { 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 ); @@ -102,10 +110,19 @@ static void overlay_save_count() { 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 *) FRAG(lo); + 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 ); @@ -122,10 +139,19 @@ static void overlay_save_table(size_t lo,size_t hi) { size -= n; p += n; } + if ( overlay.source.dirty++ > 1000 ) { + fsync( overlay.source.fd ); + overlay.source.dirty = 0; + } } -static void overlay_insert(size_t p,off_t pos,size_t size) { +/** + * 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 ); @@ -134,44 +160,120 @@ static void overlay_insert(size_t p,off_t pos,size_t size) { } bytes = ( overlay.count++ - p ) * sizeof( struct Region ); if ( bytes ) { - memmove( FRAG( p+1 ), FRAG( p ), bytes ); + memmove( (char*) &overlay.table[ p+1 ], + (char*) &overlay.table[ p ], + bytes ); } - FRAG( p )->pos = pos; - FRAG( p )->size = size; + 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) { - if ( p < --overlay.count ) { - size_t size = ( overlay.count - p ) * sizeof( struct Region ); - memmove( FRAG(p), FRAG(p+1), size ); + size_t bytes = ( --overlay.count - p ) * sizeof( struct Region ); + if ( bytes ) { + memmove( (char*) &overlay.table[ p ], + (char*) &overlay.table[ p+1 ], + bytes ); } - 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 ); +/** + * 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; } - 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); + // 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 +} + +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 ); + usage(); } } @@ -188,17 +290,6 @@ static int RANGE(int s,int n ) { return ( s == n ) && *(range+c) == 0; } -static void usage(); - -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 ); - usage(); - } -} - static int setup_sources(char **argv,int i,int n) { sources.array = calloc( n, sizeof( struct Source ) ); if ( sources.array == 0 ) { @@ -357,40 +448,34 @@ static int find_source(off_t offset) { return lo; } -static int overlay_merge(char *buf,off_t off,size_t size) { +static int overlay_merge(char *buf,off_t beg,off_t end) { #if DEBUG - fprintf( stderr, "merge %ld %ld\n", off, size ); + fprintf( stderr, "merge %ld %ld\n", beg, end ); #endif - // Find nearest overlay data before or at off - ssize_t p = overlay_prior_fragment( off ); + // Find nearest overlay data before or at beg + ssize_t p = overlay_prior_fragment( beg ); 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); + for ( ; p < overlay.count && overlay.table[p].beg < end; p++ ) { + if ( overlay.table[p].end < beg ) { + continue; } - if ( delta > size ) { - delta = size; + if ( overlay.table[p].beg > beg ) { + size_t delta = overlay.table[p].beg - beg; + buf += delta; + beg += delta; } - lseek( overlay.source.fd, off, SEEK_SET ); - while ( delta > 0 ) { - size_t n = read( overlay.source.fd, buf, delta ); - off += n; + 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; - delta -= n; buf += n; + beg += n; // } } -#if DEBUG - fprintf( stderr, "merged\n" ); -#endif return 0; } @@ -428,6 +513,10 @@ static int fusefile_read(const char *path, char *buf, size_t size, if ( n > size ) { n = size; } + if ( sources.array[i].dirty ) { + fsync( sources.array[i].fd ); + sources.array[i].dirty = 0; + } #if DEBUG fprintf( stderr, " seek fd=%d to %ld\n", sources.array[i].fd, b ); #endif @@ -440,12 +529,6 @@ 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.source.filename ) { - int x = overlay_merge( buf + rr, off + rr, r ); - if ( x ) { - return x; - } - } #if DEBUG fprintf( stderr, " got %ld bytes\n", r ); #endif @@ -456,6 +539,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; @@ -503,9 +596,9 @@ static void overlay_load() { exit( 1 ); } #if DEBUG - fprintf( stderr, "overlay region: %ld %ld\n", f.pos, f.size ); + fprintf( stderr, "overlay region: %ld %ld\n", f.beg, f.end ); #endif - overlay_mark( f.pos, f.size ); + overlay_mark( f.beg, f.end ); } } @@ -516,18 +609,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; - if ( overlay.source.filename ) { - source = &overlay.source; - overlay_mark( off, size ); // Mark region as written - } else { - 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 ) { @@ -545,6 +636,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; } @@ -608,6 +703,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 ); @@ -615,6 +724,7 @@ static int fusefile_flush(const char *path, struct fuse_file_info *info) { if ( strcmp( path, "/" ) != 0 ) { return -ENOENT; } + fsync_all_dirty(); return 0; } @@ -635,6 +745,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; }