X-Git-Url: https://git.rrq.au/?a=blobdiff_plain;f=fusefile.c;h=30ec7fa284c7ce1f330daaede1de2fb1835ee9b5;hb=6070beaa128d62e299db8b5d4e94f91e244bad4c;hp=0936da92219a0e5877679c1bdbae2fc91d1680e6;hpb=bcaeffd9fc73b359636ff32629fc07374c50cf16;p=rrq%2Ffusefile.git diff --git a/fusefile.c b/fusefile.c index 0936da9..30ec7fa 100644 --- a/fusefile.c +++ b/fusefile.c @@ -34,12 +34,18 @@ #include #include +struct Region { + off_t pos; + size_t size; +}; + struct Source { char *filename; ssize_t from; ssize_t to; ssize_t start; // starting position in concatenated file int fd; + int dirty; }; static struct { @@ -53,7 +59,182 @@ static struct { time_t mtime; time_t ctime; } times; - + +/** + * Overlay + */ +static struct { + struct Source source; + struct Region *table; + size_t count; + size_t limit; +} overlay; + +static void usage(); + +#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; + } + if ( overlay.source.dirty++ > 1000 ) { + fsync( overlay.source.fd ); + overlay.source.dirty = 0; + } +} + +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; + } + 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) { + 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) { +#if DEBUG + fprintf( stderr, "overlay_mark( %ld, %ld )\n", pos, size ); +#endif + int deleted = 0; + ssize_t q; + ssize_t p = overlay_prior_fragment( pos ); + // p is the nearest region below pos (or -1) + if ( p >= 0 && pos <= END(p) ) { + // p overlaps mark region + if ( END(p) >= pos + size ) { +#if DEBUG + fprintf( stderr, "overlay size 1( %ld )\n", FRAG(p)->size ); +#endif + return; // new mark within existing. + } + // new mark region extends existing + FRAG(p)->size = pos + size - BEG(p); + q = p+1; + while ( q < overlay.count && BEG(q) <= END(p) ) { + if ( END(q) > END(p) ) { + FRAG(p)->size = END(q) - BEG(p); + } + overlay_delete( q ); + deleted++; + } + overlay_save_table( p, deleted? overlay.count : q ); +#if DEBUG + fprintf( stderr, "overlay size 2( %ld ) deleted %d\n", + FRAG(p)->size, deleted ); +#endif + return; + } + // The region p does not expand into new mark region + p++; // subsequent region + if ( p >= overlay.count || BEG(p) > pos + size ) { + // New mark is separate region at p + overlay_insert( p, pos, size); +#if DEBUG + fprintf( stderr, "overlay size 4( %ld )\n", FRAG(p)->size ); +#endif + overlay_save_table( p, overlay.count ); + return; + } + // New marks start before and overlap with the region + if ( BEG(p) + FRAG(p)->size < pos + size ) { + FRAG(p)->size = size; // new mark covers old region + } else { + FRAG(p)->size += BEG(p) - pos; + } + BEG(p) = pos; + q = p+1; + while ( q < overlay.count && BEG(q) <= END(p) ) { + if ( END(q) > END(p) ) { + FRAG(p)->size = END(q) - BEG(p); + } + overlay_delete( q ); + deleted++; + } + overlay_save_table( p, deleted? overlay.count : q ); +#if DEBUG + fprintf( stderr, "overlay size 4( %ld ) deleted %d\n", + FRAG(p)->size, 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(); + } +} + #if DEBUG static void print_source(struct Source *p) { fprintf( stderr, "%p { %s, %ld, %ld, %ld, %d }\n", @@ -200,17 +381,71 @@ 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; } } +#if DEBUG + fprintf( stderr, "found %d\n", lo ); +#endif return lo; } +static int overlay_merge(char *buf,off_t off,size_t size) { +#if DEBUG + fprintf( stderr, "merge %ld %ld\n", off, size ); +#endif + // 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++ ) { + if ( END(p) < off ) { + continue; + } + size_t delta = FRAG(p)->size; + if ( BEG(p) < off ) { + delta -= off - BEG(p); + } else { + size_t skip = BEG(p) - off; + off += skip; + size -= skip; + buf += skip; + } + if ( delta > size ) { + delta = size; + } + 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 + fprintf( stderr, "merged\n" ); +#endif + 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) @@ -224,10 +459,10 @@ static int fusefile_read(const char *path, char *buf, size_t size, #if DEBUG fprintf( stderr, "read %ld %ld\n", off, size ); #endif - size_t rr = 0; + size_t rr = 0; // total reading while ( size > 0 ) { #if DEBUG - fprintf( stderr, "find_source %ld %ld\n", off, size ); + fprintf( stderr, " find_source %ld %ld\n", off, size ); #endif int i = find_source( off ); if ( i < 0 ) { @@ -245,16 +480,24 @@ 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 if ( lseek( sources.array[i].fd, b, SEEK_SET ) < 0 ) { perror( sources.array[i].filename ); return -ENOENT; } #if DEBUG - fprintf( stderr, "get %ld bytes at %ld\n", n, rr ); + 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\n", r ); #endif if ( r < 0 ) { perror( sources.array[i].filename ); @@ -263,10 +506,23 @@ 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, r ); + if ( x ) { + return x; + } + } rr += r; off += r; size -= r; } +#if DEBUG + fprintf( stderr, " total reading %ld bytes\n", rr ); +#endif return rr; } @@ -288,6 +544,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 @@ -296,12 +576,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, 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 ) { @@ -319,6 +603,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; } @@ -382,6 +670,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 ); @@ -389,6 +691,7 @@ static int fusefile_flush(const char *path, struct fuse_file_info *info) { if ( strcmp( path, "/" ) != 0 ) { return -ENOENT; } + fsync_all_dirty(); return 0; } @@ -409,6 +712,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,7 +763,7 @@ static struct fuse_operations fusefile_oper = { static void usage() { char *usage = "Usage: fusefile [ ] ... \n" -"Mounts a virtual, read-only file that is a concatenation of file fragments\n" +"Mounts a virtual, file that is a concatenation of file fragments\n" ; fprintf( stderr, "%s", usage ); exit( 1 ); @@ -518,9 +822,22 @@ 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 ( 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_load(); + } if ( stat( mnt, &stbuf ) == -1 ) { int fd = open( mnt, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR ); if ( fd < 0 ) { @@ -542,6 +859,16 @@ int main(int argc, char *argv[]) times.ctime = stbuf.st_ctime; } + { + int fd = open( mnt, O_RDWR, S_IRUSR | S_IWUSR ); + if ( fd < 0 ) { + perror( mnt ); + return 1; + } + if ( lseek( fd, sources.size, SEEK_SET ) < 0 ) { + return -EIO; + } + } fuseargc = setup_argv( fuseargc, &argv ); struct fuse_args args = FUSE_ARGS_INIT( fuseargc, argv ); if ( fuse_parse_cmdline( &args, &mnt, &mt, &fg ) ) {