present the stacked overlay option
[rrq/fusefile.git] / fusefile.c
1 /***
2     fusefile - overlay a file path with a concatenation of parts of
3     other files.
4
5     Copyright (C) 2019-  Ralph Ronnquist
6
7     This program is free software: you can redistribute it and/or
8     modify it under the terms of the GNU General Public License as
9     published by the Free Software Foundation, either version 3 of the
10     License, or (at your option) any later version.
11
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15     General Public License for more details.
16
17     You should have received a copy of the GNU General Public License
18     along with this program. If not, see
19     <http://www.gnu.org/licenses/>.
20
21     This source was inspired by the "null.c" example of the libfuse
22     sources, which is distributed under GPL2, and copyright (C)
23     2001-2007 Miklos Szeredi <miklos@szeredi.hu>.
24 */
25
26 #define FUSE_USE_VERSION 33
27
28 #include <fuse.h>
29 #include <fuse/fuse_lowlevel.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <time.h>
35 #include <errno.h>
36 #include <sys/ioctl.h>
37 #include <linux/fs.h>
38
39 struct Region {
40     off_t beg;
41     off_t end;
42 };
43
44 #define REGIONKEEP(r) ((r,beg == r.end)?: 0 : 1)
45
46 struct Source {
47     char *filename;
48     ssize_t from;
49     ssize_t to;
50     ssize_t start; // starting position in concatenated file
51     int fd;
52     int dirty;
53 };
54
55 #define ENDSOURCE( S ) ( S.start + ( S.to - S.from ) )
56
57 static struct {
58     struct Source *array;
59     int count;
60     ssize_t size;
61 } sources;
62
63 static struct {
64     time_t atime;
65     time_t mtime;
66     time_t ctime;
67 } times;
68
69 /**
70  * Overlay
71  */
72 static struct Overlay {
73     struct Source source;
74     struct Region *table;
75     size_t count;
76     size_t limit;
77 } overlay; // The current overlay.
78
79 static void usage();
80
81 /**
82  * Find the nearest overlay.table region below pos. Returns the index,
83  * or -1 if there is none, i.e. pos < overlay.table[0].
84  */
85 static ssize_t overlay_prior_fragment(off_t pos) {
86     size_t lo = 0, hi = overlay.count;
87     while ( lo < hi ) {
88         size_t m = ( lo + hi ) / 2;
89         if ( m == lo ) {
90             return overlay.table[m].beg <= pos? m : -1;
91         }
92         if ( overlay.table[m].beg <= pos ) {
93             lo = m;
94         } else {
95             hi = m;
96         }
97     }
98     return -1;
99 }
100
101 /**
102  * Save the entry count for overlay.table as 64-bit integer
103  * immediately following the overlay content at the index
104  * corresponding to the fused file size.
105  */
106 static void overlay_save_count() {
107     lseek( overlay.source.fd, overlay.source.to, SEEK_SET );
108     size_t size = sizeof( overlay.count );
109     char *p = (char *) &overlay.count ;
110     while ( size > 0 ) {
111         size_t n = write( overlay.source.fd, p, size );
112         if ( n < 0 ) {
113             perror( overlay.source.filename );
114             exit( 1 );
115         }
116         size -= n;
117         p += n;
118     }
119     if ( overlay.source.dirty++ > 1000 ) {
120         fsync( overlay.source.fd );
121         overlay.source.dirty = 0;
122     }
123 }
124
125 /**
126  * Update the on-disk cache of overlay.table between the given
127  * indexes. The table is laid out immediately following the table
128  * count with each region saved as two 64-bit unsigned integers.
129  */
130 static void overlay_save_table(size_t lo,size_t hi) {
131     char *p = (char *) &overlay.table[ lo ];
132     size_t pos =  overlay.source.to + sizeof( overlay.count ) +
133         lo * sizeof( struct Region );
134     size_t size = ( hi - lo ) * sizeof( struct Region );
135     if ( pos != lseek( overlay.source.fd, pos, SEEK_SET ) ) {
136         fprintf( stderr, "%s: seek error\n", overlay.source.filename );
137         exit( 1 );
138     }
139     while ( size > 0 ) {
140         size_t n = write( overlay.source.fd, p, size );
141         if ( n < 0 ) {
142             perror( overlay.source.filename );
143             exit( 1 );
144         }
145         size -= n;
146         p += n;
147     }
148     if ( overlay.source.dirty++ > 1000 ) {
149         fsync( overlay.source.fd );
150         overlay.source.dirty = 0;
151     }
152 }
153
154 /**
155  * Insert a new region at index p, with previous portion [p,count]
156  * moved up to make space.
157  */
158 static void overlay_insert(size_t p,off_t beg,off_t end) {
159     size_t bytes;
160     // Grow the table if needed
161     if ( overlay.count >= overlay.limit ) {
162         overlay.limit = overlay.count + 10;
163         bytes = overlay.limit * sizeof( struct Region );
164         overlay.table = overlay.table?
165             realloc( overlay.table, bytes ) : malloc( bytes );
166     }
167     bytes = ( overlay.count++ - p ) * sizeof( struct Region );
168     if ( bytes ) {
169         memmove( (char*) &overlay.table[ p+1 ],
170                  (char*) &overlay.table[ p ],
171                  bytes );
172     }
173     overlay.table[ p ].beg = beg;
174     overlay.table[ p ].end = end;
175     overlay_save_count();
176 }
177
178 /**
179  * Delete the region entry at p by moving the portion [p+1,count]
180  * down.
181  */
182 static void overlay_delete(size_t p) {
183     size_t bytes = ( --overlay.count - p ) * sizeof( struct Region );
184     if ( bytes ) {
185         memmove( (char*) &overlay.table[ p ],
186                  (char*) &overlay.table[ p+1 ],
187                  bytes );
188     }
189 }
190
191 /**
192  * Mark the given region as updated, i.e. written to the overlay. The
193  * mark region may attach to prior marked regions or be a new,
194  * separate region. If attaching, it causes the prior regions to
195  * expand and the table adjusted by deleting any regions that become
196  * fully contained in other regions.
197  */
198 static void overlay_mark(off_t beg,off_t end) {
199 #if DEBUG
200     fprintf( stderr, "overlay_mark( %ld, %ld )\n", beg, end );
201 #endif
202     int deleted = 0;
203     ssize_t q;
204     ssize_t p = overlay_prior_fragment( beg );
205     // p is the nearest region below or at beg (or -1)
206     if ( p >= 0 && beg <= overlay.table[p].end ) {
207         // p overlaps mark region
208         if ( end <= overlay.table[p].end ) {
209             // region p covers mark region already
210 #if DEBUG
211             fprintf( stderr, "overlay covering ( %ld %ld )\n",
212                      overlay.table[p].beg, overlay.table[p].end );
213 #endif
214             return;
215         }
216         // the new mark region extends region p
217         overlay.table[p].end = end;
218         q = p+1;
219         while ( q < overlay.count &&
220                 overlay.table[q].beg <= overlay.table[p].end ) {
221             // Extended region merges with subsequent region
222             if ( overlay.table[p].end < overlay.table[q].end ) {
223                 overlay.table[p].end = overlay.table[q].end;
224             }
225             overlay_delete( q );
226             deleted++;
227         }
228         if ( deleted ) {
229             overlay_save_count();
230             q = overlay.count;
231         }
232         overlay_save_table( p, q );
233 #if DEBUG
234         fprintf( stderr, "overlay expand ( %ld %ld ) deleted %d\n",
235                  overlay.table[p].beg, overlay.table[p].end, deleted );
236 #endif
237         return;
238     }
239     // The prior region p does not expand into new mark region
240     p++; // subsequent region 
241     if ( p >= overlay.count || end < overlay.table[p].beg ) {
242         // New mark region is a separate region at p
243         overlay_insert( p, beg, end );
244 #if DEBUG
245         fprintf( stderr, "overlay new ( %ld %ld )\n",
246                  overlay.table[p].beg, overlay.table[p].end );
247 #endif
248         overlay_save_table( p, overlay.count );
249         return;
250     }
251     // New marks start before and overlap with region p => change p
252     // and handle any subsequent regions being covered
253     overlay.table[p].beg = beg;
254     q = p+1;
255     if ( overlay.table[p].end < end ) {
256         overlay.table[p].end = end;
257         while ( q < overlay.count &&
258                 overlay.table[q].beg <= overlay.table[p].end ) {
259             if ( overlay.table[p].end < overlay.table[q].end ) {
260                 overlay.table[p].end = overlay.table[q].end;
261             }
262             overlay_delete( q );
263             deleted++;
264         }
265         if ( deleted ) {
266             overlay_save_count();
267             q = overlay.count;
268         }
269     }
270     overlay_save_table( p, q );
271 #if DEBUG
272     fprintf( stderr, "overlay before ( %ld %ld ) deleted %d\n",
273              overlay.table[p].beg, overlay.table[p].end, deleted );
274 #endif
275 }
276
277 /**
278  * Capture overlay filenames for processing after source fragments.
279  */
280 static void overlay_setup(char *filenames) {
281     overlay.source.filename = filenames;
282 }
283
284 // Duplicate the source record data at the given index.
285 static void dup_source_item(int index) {
286     sources.count++;
287     sources.array = realloc(
288         sources.array, sources.count * sizeof( struct Source ) );
289     if ( sources.array == 0 ) {
290         fprintf( stderr, "** OOM when expanding frament table\n" );
291         usage();
292     }
293     memcpy( &sources.array[ index+1 ], &sources.array[ index ],
294             sizeof( struct Source ) );
295 }
296
297 #if DEBUG
298 static void print_source(struct Source *p) {
299     fprintf( stderr, "%p { %s, %ld, %ld, %ld, %d }\n",
300              p, p->filename, p->from, p->to, p->start, p-> fd );
301 }
302 #endif
303
304 static char *range;
305 static unsigned int c;
306 static int RANGE(int s,int n ) {
307     return ( s == n ) && *(range+c) == 0;
308 }
309
310 static int setup_source(struct Source *p,char *frag) {
311     struct stat filestat;
312     // Open the fragment file rw if possible, else ro
313     // First try the fragment in full, thereafter with range appendix
314     if ( stat( frag, &filestat ) == 0 ) {
315         p->filename = strdup( frag );
316         range = 0;
317     } else {
318         range = strrchr( frag, '/' ); // last '/'
319         p->filename = range? strndup( frag, range - frag ) : frag;
320     }
321     p->fd = open( p->filename, O_RDWR );
322     int rdonly = 0;
323     if ( p->fd < 0 ) {
324         rdonly = 1;
325         p->fd = open( p->filename, O_RDONLY );
326     }
327     if ( p->fd < 0 ) {
328         perror( p->filename );
329         return 1; // Error return
330     }
331     if ( ( range == 0 ) && stat( p->filename, &filestat ) ) {
332         perror( p->filename );
333         return 1; 
334     }
335     if ( rdonly ) {
336         fprintf( stderr, "** %s opened read-only\n", p->filename );
337     }
338     p->from = 0;
339     if ( S_ISBLK( filestat.st_mode ) ) {
340         // Block devices report size differently:
341         if ( ioctl( p->fd, BLKGETSIZE64, &filestat.st_size ) < 0 ) {
342             perror( p->filename );
343         }
344 #if DEBUG
345         fprintf( stderr, "block device size = %ld\n", filestat.st_size );
346 #endif
347     }
348     p->to = filestat.st_size;
349     // Process any range variation
350     if ( range && *(++range) ) {
351         long int a,b;
352         if ( 0 ) {
353         } else if ( RANGE( sscanf( range, "%ld:%ld%n", &a, &b, &c ), 2 )) {
354             p->from = ( a < 0 )? ( p->to + a ) : a;
355             p->to = ( b < 0 )? ( p->to + b ) : b;
356         } else if ( RANGE( sscanf( range, "%ld+%ld%n", &a, &b, &c ), 2 )) {
357             p->from = ( a < 0 )? ( p->to + a ) : a;
358             p->to = ( ( b < 0 )? p->to : p->from ) + b;
359         } else if ( RANGE( sscanf( range, "%ld+%n", &a, &c ), 1 )) {
360             p->from = ( a < 0 )? ( p->to + a ) : a;
361         } else if ( RANGE( sscanf( range, ":%ld%n", &b, &c ), 1 )) {
362             p->to = ( b < 0 )? ( p->to + b ) : b;
363         } else if ( RANGE( sscanf( range, "%ld:%n", &a, &c ), 1 )) {
364             p->from = ( a < 0 )? ( p->to + a ) : a;
365         } else if ( RANGE( sscanf( range, "%ld%n", &a, &c ), 1 )) {
366             if ( a >= 0 ) {
367                 p->from = a;
368             } else {
369                 p->from = p->to + a;
370             }
371         } else if ( RANGE( sscanf( range, ":%n", &c), 0 ) ) {
372             // to end from start
373         } else {
374             fprintf( stderr, "** BAD RANGE: %s\n", frag );
375             return 1;
376         }
377     }
378     if ( ( filestat.st_mode &  S_IFMT ) == S_IFCHR ) {
379         filestat.st_size = p->to; // Pretend size of character device
380     }
381     if ( p->from < 0 ) {
382         p->from = 0;
383     }
384     if ( p->to > filestat.st_size ) {
385         p->to = filestat.st_size;
386     }
387     if ( p->from >= p->to || p->from >= filestat.st_size ) {
388         fprintf( stderr, "** BAD RANGE: %s [%ld:%ld]\n",
389                  frag, p->from, p->to );
390         return 1;
391     }
392     p->start = sources.size; // the fusefile position of fragment
393     sources.size += p->to - p->from;
394     return 0;
395 }
396
397 static int setup_sources(char **argv,int i,int n) {
398     sources.array = calloc( n, sizeof( struct Source ) );
399     if ( sources.array == 0 ) {
400         return 1;
401     }
402     sources.count = n;
403     int j = 0;
404     sources.size = 0;
405     for ( ; j < n; i++, j++ ) {
406         struct Source *p = sources.array + j;
407         if ( setup_source( p, argv[i] ) ) {
408             return 1;
409         }
410 #if DEBUG
411         print_source( p );
412 #endif
413     }
414     return 0;
415 }
416
417 static int fusefile_getattr(const char *path,struct stat *stbuf) {
418 #if DEBUG
419     fprintf( stderr, "fusefile_getattr( %s )\n", path );
420 #endif
421     if ( strcmp( path, "/" ) != 0 ) {
422         return -ENOENT;
423     }
424 #if DEBUG
425     fprintf( stderr, "getattr %ld\n", sources.size );
426 #endif
427     memset( stbuf, 0, sizeof( struct stat ) );
428     stbuf->st_mode = S_IFREG | 0644; // Hmmm
429     stbuf->st_nlink = 1;
430     stbuf->st_size = sources.size;
431     stbuf->st_atime = times.atime;
432     stbuf->st_mtime = times.mtime;
433     stbuf->st_ctime = times.ctime;
434     stbuf->st_uid = getuid();
435     stbuf->st_gid = getgid();
436     return 0;
437 }
438
439 static int fusefile_chmod(const char *path,mode_t m) {
440 #if DEBUG
441     fprintf( stderr, "fusefile_chmod( %s, %d )\n", path, m );
442 #endif
443     return -1;
444 }
445
446 static int fusefile_open(const char *path,struct fuse_file_info *fi) {
447 #if DEBUG
448     fprintf( stderr, "fusefile_open( %s, %d )\n", path, fi->flags );
449     fprintf( stderr, "fixing( %d )\n", fi->flags | O_CLOEXEC );
450 #endif
451     if ( strcmp( path, "/" ) != 0 ) {
452         return -ENOENT;
453     }
454     // set O-CLOEXEC  for this opening?
455     times.atime = time( 0 );
456     return 0;
457 }
458
459 static int find_source(off_t offset) {
460     int lo = 0;
461     int hi = sources.count;
462     if ( offset >= sources.size ) {
463         return -1;
464     }
465 #if DEBUG
466     fprintf( stderr, "find_source( %ld )\n", offset );
467 #endif
468     while ( lo + 1 < hi ) {
469         int m = ( lo + hi ) / 2;
470         if ( offset < sources.array[ m ].start ) {
471 #if DEBUG
472             fprintf( stderr, "  offset < [%d].start: %ld\n",
473                      m, sources.array[ m ].start );
474 #endif
475             hi = m;
476         } else {
477 #if DEBUG
478             fprintf( stderr, "  offset >= [%d].start: %ld\n",
479                      m, sources.array[ m ].start );
480 #endif
481             lo = m;
482         }
483     }
484 #if DEBUG
485     fprintf( stderr, "found %d\n", lo );
486 #endif
487     return lo;
488 }
489
490 static int overlay_merge(char *buf,off_t beg,off_t end) {
491 #if DEBUG
492     fprintf( stderr, "merge %ld %ld\n", beg, end );
493 #endif
494     // Find nearest overlay data before or at beg
495     ssize_t p = overlay_prior_fragment( beg );
496     if ( p < 0 ) {
497         p = 0;
498     }
499     for ( ; p < overlay.count && overlay.table[p].beg < end; p++ ) {
500         if ( overlay.table[p].end < beg ) {
501             continue;
502         }
503         if ( overlay.table[p].beg > beg ) {
504             size_t delta = overlay.table[p].beg - beg;
505             buf += delta;
506             beg += delta;
507         }
508         size_t size = ( overlay.table[p].end <= end )?
509             ( overlay.table[p].end - beg ) : ( end - beg ); 
510         lseek( overlay.source.fd, beg, SEEK_SET );
511         while ( size > 0 ) {
512             size_t n = read( overlay.source.fd, buf, size );
513             size -= n;
514             buf += n;
515             beg += n; //
516         }
517     }
518     return 0;
519 }
520
521 // Read <size> bytes from <offset> in file
522 static int fusefile_read(const char *path, char *buf, size_t size,
523                          off_t off, struct fuse_file_info *fi)
524 {
525 #if DEBUG
526     fprintf( stderr, "fusefile_read( %s )\n", path );
527 #endif
528     if( strcmp( path, "/" ) != 0 ) {
529         return -ENOENT;
530     }
531 #if DEBUG
532     fprintf( stderr, "read %ld %ld\n", off, size );
533 #endif
534     size_t rr = 0; // total reading
535     while ( size > 0 ) {
536 #if DEBUG
537         fprintf( stderr, "  find_source %ld %ld\n", off, size );
538 #endif
539         int i = find_source( off );
540         if ( i < 0 ) {
541             return ( off == sources.size )? rr : -ENOENT;
542         }
543         if ( sources.array[i].fd < 0 ) {
544             return -ENOENT;
545         }
546 #if DEBUG
547         print_source( &sources.array[i] );
548 #endif
549         times.atime = time( 0 );
550         size_t b = off - sources.array[i].start + sources.array[i].from;
551         size_t n = sources.array[i].to - b;
552         if ( n > size ) {
553             n = size;
554         }
555         if ( sources.array[i].dirty ) {
556             fsync( sources.array[i].fd );
557             sources.array[i].dirty = 0;
558         }
559 #if DEBUG
560         fprintf( stderr, "  seek fd=%d to %ld\n", sources.array[i].fd, b );
561 #endif
562         if ( lseek( sources.array[i].fd, b, SEEK_SET ) < 0 ) {
563             perror( sources.array[i].filename );
564             return -ENOENT;
565         }
566 #if DEBUG
567         fprintf( stderr, "  now read %ld from fd=%d\n",
568                  n, sources.array[i].fd );
569 #endif
570         ssize_t r = read( sources.array[i].fd, buf + rr, n );
571 #if DEBUG
572         fprintf( stderr, "  got %ld bytes\n", r );
573 #endif
574         if ( r < 0 ) {
575             perror( sources.array[i].filename );
576             return -ENOENT;
577         }
578         if ( r == 0 ) {
579             break;
580         }
581         if ( overlay.source.filename ) {
582             if ( overlay.source.dirty ) {
583                 fsync( overlay.source.fd );
584                 overlay.source.dirty = 0;
585             }
586             int x = overlay_merge( buf + rr, off + rr, off + rr + r );
587             if ( x ) {
588                 return x;
589             }
590         }
591         rr += r;
592         off += r;
593         size -= r;
594     }
595 #if DEBUG
596     fprintf( stderr, "  total reading %ld bytes\n", rr );
597 #endif
598     return rr;
599 }
600
601 /**
602  * Poll for IO readiness.
603  */
604 int fusefile_poll(const char *path, struct fuse_file_info *fi,
605                    struct fuse_pollhandle *ph, unsigned *reventsp )
606 {
607 #if DEBUG
608     fprintf( stderr, "fusefile_poll( %s ) %p %d\n", path, ph, *reventsp );
609 #endif
610     if( strcmp( path, "/" ) != 0 ) {
611         return -ENOENT;
612     }
613     if ( ph ) {
614         return fuse_notify_poll( ph );
615     }
616     return 0;
617 }
618
619 static void overlay_load() {
620     lseek( overlay.source.fd, overlay.source.to, SEEK_SET );
621     size_t x = 0;
622     size_t size = sizeof( overlay.count );
623     if ( read( overlay.source.fd, &x, size ) != size ) {
624         return;
625     }
626 #if DEBUG
627     fprintf( stderr, "overlay: %s with %ld regions\n",
628              overlay.source.filename, x );
629 #endif
630     struct Region f = { 0, 0 };
631     size = sizeof( struct Region );
632     while ( x-- > 0 ) {
633         if ( read( overlay.source.fd, &f, size ) != size ) {
634             fprintf( stderr, "%s: bad meta data\n", overlay.source.filename );
635             exit( 1 );
636         }
637 #if DEBUG
638         fprintf( stderr, "overlay region: %ld %ld\n", f.beg, f.end );
639 #endif
640         overlay_mark( f.beg, f.end );
641     }
642 }
643
644 /**
645  * Inject an overlay fragment into the source table, end return the
646  * end of the injected fragment.
647  */
648 static off_t overlay_inject_from_region(off_t beg,off_t end) {
649     int index = find_source( beg );
650     if ( index < 0 ) {
651         fprintf( stderr, "** Injecting %s at %ld failed\n",
652                  overlay.source.filename, beg );
653         usage();
654     }
655     if ( end > ENDSOURCE( sources.array[ index ] ) ) {
656         end = ENDSOURCE( sources.array[ index ] );
657     }
658     struct Region frags[3] = {
659         { sources.array[ index ].start, beg },
660         { beg, ENDSOURCE( sources.array[ index ] ) },
661         { ENDSOURCE( sources.array[ index ] ), end } };
662     ssize_t size = frags[0].end - frags[0].beg;
663     if ( size ) {
664         // "Duplicate" the indexed source data, copying the filename
665         dup_source_item( index );
666         sources.array[ index ].to = sources.array[ index ].from + size;
667         index++;
668         sources.array[ index ].start = beg;
669         sources.array[ index ].from = sources.array[ index-1 ].to;
670     }
671     size = frags[2].end - frags[2].beg;
672     if ( size ) {
673         dup_source_item( index );
674         sources.array[ index+1 ].start = frags[2].beg;
675         sources.array[ index+1 ].from = sources.array[ index+1 ].to -size;
676     }
677     // Replace the [index] fragment
678     sources.array[ index ].filename = overlay.source.filename;
679     sources.array[ index ].start = beg;
680     sources.array[ index ].from = beg;
681     sources.array[ index ].to = end;
682     sources.array[ index ].fd = overlay.source.fd; //?
683     return end;
684 }
685
686 /**
687  * Inject the current (unopened) overlay into the source list.
688  */
689 static void overlay_inject() {
690     overlay.source.fd = open( overlay.source.filename, O_RDONLY );
691     if ( overlay.source.fd < 0 ) {
692         perror( overlay.source.filename );
693         usage();
694     }
695     
696     if ( lseek( overlay.source.fd, overlay.source.to, SEEK_SET ) < 0 ) {
697         perror( overlay.source.filename );
698         usage();
699     }
700     size_t count = 0;
701     size_t size = sizeof( overlay.count );
702     if ( read( overlay.source.fd, &count, size ) != size ) {
703         fprintf( stderr, "** error injecting %s\n", overlay.source.filename );
704         usage();
705     }
706     if ( count == 0 ) {
707         close( overlay.source.fd );
708         return;
709     }
710     size = count * sizeof( struct Region );
711     overlay.table = calloc( sizeof( struct Region ), count );
712     if ( read( overlay.source.fd, overlay.table, size ) != size ) {
713         fprintf( stderr, "** error injecting %s\n", overlay.source.filename );
714         usage();
715     }
716     size_t i;
717     for ( i = 0; i < count; i++ ) {
718         off_t beg = overlay.table[i].beg;
719         while ( beg < overlay.table[i].end ) {
720             beg = overlay_inject_from_region( beg, overlay.table[i].end );
721         }
722     }
723     free( overlay.table );
724     overlay.table = 0;
725     close( overlay.source.fd );
726 }
727
728 /**
729  * Each stacked overlay file is nested into to source list
730  */
731 static void overlay_post_setup() {
732     char *end;
733     while ( ( end = strchr( overlay.source.filename, ':' ) ) ) {
734         *end = 0; //  
735         overlay_inject();
736         overlay.source.filename = end + 1;
737     }
738     if ( *overlay.source.filename ) {
739         overlay.source.fd = open( overlay.source.filename,
740                                   O_RDWR | O_CREAT, S_IRUSR | S_IWUSR );
741         if ( overlay.source.fd < 0 ) {
742             perror( overlay.source.filename );
743             usage();
744         }
745         overlay_load();
746     }
747 }
748
749 /**
750  * Write a full block of data over the sources at the offset
751  */
752 static int write_block(off_t off,const char *buf,size_t size) {
753 #if DEBUG
754     fprintf( stderr, "write_block( %ld, ?, %ld )\n", off, size );
755 #endif
756     if ( overlay.source.filename ) {
757         overlay_mark( off, off + size ); // Mark region as written
758     }
759     while ( size > 0 ) {
760         int index = find_source( off ); // index of source file
761         if ( index < 0 ) {
762             return -EIO; // past EOF
763         }
764         struct Source *source = overlay.source.filename?
765             &overlay.source :  &sources.array[ index ];
766         off_t from = off - source->start + source->from;
767         off_t max = source->to - from;
768         if ( lseek( source->fd, from, SEEK_SET ) < 0 ) {
769             return -EIO;
770         }
771         ssize_t todo = ( size < max )? size : max;
772         while ( todo > 0 ) {
773             times.mtime = time( 0 );
774             ssize_t n = write( source->fd, buf, todo );
775             if ( n <= 0 ) {
776                 return -EIO; // Something wrong
777             }
778             buf += n;
779             todo -= n;
780             size -= n;
781             off += n;
782         }
783         if ( source->dirty++ >= 1000 ) {
784             fsync( source->fd );
785             source->dirty = 0;
786         }
787     }
788     return 0;
789 }
790
791 static int fusefile_write_buf(const char *path, struct fuse_bufvec *buf,
792                               off_t off, struct fuse_file_info *fi) {
793 #if DEBUG
794     fprintf( stderr, "fusefile_write_buf( %s )\n", path );
795 #endif
796     if ( strcmp( path, "/" ) != 0 ) {
797         return -ENOENT;
798     }
799
800     size_t size = 0;
801     int i;
802     for ( i = 0; i < buf->count; i++ ) {
803         struct fuse_buf *p = &buf->buf[i];
804         if ( p->flags & FUSE_BUF_IS_FD ) {
805 #if DEBUG
806             fprintf( stderr, "Content held in a file ... HELP!!\n" );
807 #endif
808             return -EIO;
809         }
810         if ( write_block( off, (char*) p->mem, p->size ) < 0 ) {
811             return -EIO;
812         }
813         size += p->size;
814     }
815 #if DEBUG
816     fprintf( stderr, "fusefile_write_buf written %ld\n", size );
817 #endif
818     return size;
819 }
820
821 /**
822  * Write a fragment at <off>. This overwrites files.
823  */
824 static int fusefile_write(const char *path, const char *buf, size_t size,
825                           off_t off, struct fuse_file_info *fi)
826 {
827 #if DEBUG
828     fprintf( stderr, "fusefile_write( %s %ld )\n", path, size );
829 #endif
830     if ( strcmp( path, "/" ) != 0 ) {
831         return -ENOENT;
832     }
833
834     if ( write_block( off, buf, size ) < 0 ) {
835         return -EIO;
836     }
837     return size;
838 }
839
840 static void fusefile_destroy(void *data) {
841     char *mnt = (char*) data; // As passed to fuse_main
842 #if DEBUG
843     fprintf( stderr, "fusefile_destroy( %s )\n", mnt? mnt : "" );
844 #endif
845     if ( mnt ) {
846         unlink( mnt );
847     }
848 }
849
850 static void fsync_all_dirty() {
851     int i = 0;
852     for ( ; i < sources.count; i++ ) {
853         if ( sources.array[i].dirty ) {
854             fsync( sources.array[i].fd );
855             sources.array[i].dirty = 0;
856         }
857     }
858     if ( overlay.source.filename && overlay.source.dirty ) {
859         fsync( overlay.source.fd );
860         overlay.source.dirty = 0;
861     }
862 }
863
864 static int fusefile_flush(const char *path, struct fuse_file_info *info) {
865 #if DEBUG
866     fprintf( stderr, "fusefile_flush( %s )\n", path );
867 #endif
868     if ( strcmp( path, "/" ) != 0 ) {
869         return -ENOENT;
870     }
871     fsync_all_dirty();
872     return 0;
873 }
874
875 static int fusefile_release(const char *path, struct fuse_file_info *fi) {
876 #if DEBUG
877     fprintf( stderr, "fusefile_release( %s, %d )\n", path, fi->flags );
878 #endif
879     if ( strcmp( path, "/" ) != 0 ) {
880         return -ENOENT;
881     }
882     return 0;
883 }
884
885 static int fusefile_fsync(const char *path, int x, struct fuse_file_info *fi) {
886 #if DEBUG
887     fprintf( stderr, "fusefile_fsync( %s, %d )\n", path, x );
888 #endif
889     if ( strcmp( path, "/" ) != 0 ) {
890         return -ENOENT;
891     }
892     fsync_all_dirty();
893     return 0;
894 }
895
896 /**
897  * 
898  */
899 static int fusefile_truncate(const char *path, off_t len) {
900 #if DEBUG
901     fprintf( stderr, "fusefile_truncate( %s, %ld )\n", path, len );
902 #endif
903     if ( strcmp( path, "/" ) != 0 ) {
904         return -ENOENT;
905     }
906     return -EIO;
907 }
908
909 void *fusefile_init(struct fuse_conn_info *fci) {
910 #if DEBUG
911     fprintf( stderr, "fusefile_init( %d, %d )\n", fci->async_read, fci->want );
912 #endif
913     // Disable asynchronous reading
914     fci->async_read = 0;
915     fci->want &= ~FUSE_CAP_ASYNC_READ;
916 #if DEBUG
917     fprintf( stderr, "fusefile_init( %d, %d )\n", fci->async_read, fci->want );
918 #endif
919     return 0;
920 }
921
922 /**
923  * Dump the current fragmentation to stdout.
924  */
925 static int dump_fragments() {
926     int oly = 0;
927     int src = 0;
928     size_t pos = 0;
929     while ( src < sources.count ) {
930         size_t x = ( oly < overlay.count )?
931             overlay.table[ oly ].beg : sources.size;
932         for ( ; src < sources.count && 
933                   ENDSOURCE( sources.array[ src ] ) <= x; src++ ) {
934             // Dump sources.array[src] in full
935             fprintf( stdout, "%s/%ld:%ld\n",
936                      sources.array[ src ].filename,
937                      pos - sources.array[ src ].start,
938                      sources.array[ src ].to );
939             pos = ENDSOURCE( sources.array[ src ] );
940         }
941         if ( ( src < sources.count ) && ( sources.array[ src ].start < x ) ) {
942             // Dump sources.array[src] up to x;
943             fprintf( stdout, "%s/%ld:%ld\n",
944                      sources.array[ src ].filename,
945                      pos - sources.array[ src ].start,
946                      x - sources.array[ src ].start );
947             pos = ENDSOURCE( sources.array[ src ] );
948         }
949         if ( oly < overlay.count ) {
950             fprintf( stdout, "%s/%ld:%ld\n",
951                      overlay.source.filename,
952                      overlay.table[ oly ].beg,
953                      overlay.table[ oly ].end );
954             pos = overlay.table[ oly++ ].end;
955         }
956         for ( ; src < sources.count &&
957                   ENDSOURCE( sources.array[ src ] ) <= pos; src++ ) {
958             // Just skip these fragments.
959         }
960     }
961     return( 0 );
962 }
963
964 static struct fuse_operations fusefile_oper = {
965     .getattr = fusefile_getattr,
966     // NYI .fgetattr = fusefile_fgetattr,
967     .chmod = fusefile_chmod,
968     .open = fusefile_open,
969     .read = fusefile_read,
970     .poll = fusefile_poll,
971     .write = fusefile_write,
972     .write_buf = fusefile_write_buf,
973     .destroy = fusefile_destroy,
974     // NYI .access = fusefile_access,
975     .flush = fusefile_flush,
976     .release = fusefile_release,
977     .fsync = fusefile_fsync,
978     // NYI .ftruncate = fusefile_ftruncate,
979     .truncate = fusefile_truncate,
980     //.truncate = fusefile_truncate,
981     //.release = fusefile_release,
982     .init = fusefile_init,
983 };
984
985 static void usage() {
986     char *usage =
987 "Usage: fusefile [ <fuse options> ] <mount> <file/from-to> ... \n"
988 "Mounts a virtual, file that is a concatenation of file fragments\n"
989         ;
990     fprintf( stderr, "%s", usage );
991     exit( 1 );
992 }
993
994 /**
995  * Set up the arguments for the fuse_main call, adding our own.
996  * argv[argc] is the mount point argument
997  */
998 static int setup_argv(int argc,char ***argv) {
999     // note: (*argv)[ argc ] is the mount point argument
1000     char *OURS[] = {
1001         "-odefault_permissions",
1002         (*argv)[ argc ]
1003     };
1004 #define OURSN ( sizeof( OURS ) / sizeof( char* ) )
1005     int N = argc + OURSN;
1006     // Allocate new arg array plus terminating null pointer
1007     char **out = malloc( ( N + 1 ) * sizeof( char* ) ); 
1008     int i;
1009     for ( i = 0; i < argc; i++ ) {
1010         out[ i ] = (*argv)[i];
1011         //fprintf( stderr, " %s", out[ i ] );
1012     }
1013     for ( i = 0; i < OURSN; i++ ) {
1014         out[ argc + i ] = OURS[i];
1015         //fprintf( stderr, " %s", out[ i ] );
1016     }
1017     out[ N ] = 0;
1018     //fprintf( stderr, "\n" );
1019     (*argv) = out;
1020     return N; // Don't include the terminating null pointer
1021 }
1022
1023 /**
1024  * Mount a concatenation of files,
1025  * [ <fuse options> ] <mount> <file/from-to> ...
1026  */
1027 int main(int argc, char *argv[])
1028 {
1029     char *mnt;
1030     int mt;
1031     int fg;
1032     int i;
1033     int fuseargc;
1034     struct stat stbuf;
1035     int temporary = 0;
1036     // Scan past options
1037     for ( i = 1; i < argc; i++ ) {
1038         if ( *argv[i] != '-' ) {
1039             break;
1040         }
1041     }
1042     if ( i > argc - 2 ) { // At least mount point plus one source
1043         usage();
1044     }
1045     fuseargc = i;
1046     mnt = argv[ i++ ]; // First non-option argument is the mount pount
1047     if ( strcmp( argv[i], "-overlay:" ) == 0 ) {
1048         // consume "-overlay:filename[,filename]*"
1049         // Verify file access; to overlay must be writable.
1050         overlay_setup( argv[i++] + strlen( "-overlay:" ) );
1051         if ( i >= argc ) {
1052             usage();
1053         }
1054     }
1055     if ( setup_sources( argv, i, argc-i ) ) {
1056         return 1;
1057     }
1058     if ( overlay.source.filename ) {
1059         overlay.source.to = sources.size; // Register total size.
1060         overlay_post_setup();
1061     }
1062     if ( stat( mnt, &stbuf ) == -1 ) {
1063         int fd = open( mnt, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR );
1064         if ( fd < 0 ) {
1065             perror( mnt );
1066             return 1;
1067         }
1068         time_t now = time( 0 );
1069         times.atime = now;
1070         times.mtime = now;
1071         times.ctime = now;
1072         temporary = 1;
1073         close( fd );
1074     } else if ( ! S_ISREG( stbuf.st_mode ) ) {
1075         fprintf( stderr, "mountpoint is not a regular file\n" );
1076         return 1;
1077     } else {
1078         times.atime = stbuf.st_atime;
1079         times.mtime = stbuf.st_mtime;
1080         times.ctime = stbuf.st_ctime;
1081     }
1082
1083     {
1084         int fd = open( mnt, O_RDWR, S_IRUSR | S_IWUSR );
1085         if ( fd < 0 ) {
1086             perror( mnt );
1087             return 1;
1088         }
1089         if ( lseek( fd, sources.size, SEEK_SET ) < 0 ) {
1090             return -EIO;
1091         }
1092     }
1093     fuseargc = setup_argv( fuseargc, &argv );
1094     if ( strcmp( "-dump", argv[ 1 ] ) == 0 ) {
1095         return dump_fragments();
1096     }
1097     struct fuse_args args = FUSE_ARGS_INIT( fuseargc, argv );
1098     if ( fuse_parse_cmdline( &args, &mnt, &mt, &fg ) ) {
1099         return 1;
1100     }
1101     fuse_opt_free_args( &args );
1102     if ( ! mnt ) {
1103         fprintf( stderr, "missing mountpoint parameter\n" );
1104         return 1;
1105     }
1106     return fuse_main( fuseargc, argv, &fusefile_oper, temporary? mnt : NULL );
1107 }