bug fixing overlay option
[rrq/fusefile.git] / fusefile.c
1 /***
2     fusefile - overlay a file path with a concatenation of parts of
3     other files, read only.
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
37 struct Region {
38     off_t pos;
39     size_t size;
40 };
41
42 struct Source {
43     char *filename;
44     ssize_t from;
45     ssize_t to;
46     ssize_t start; // starting position in concatenated file
47     int fd;
48     int dirty;
49 };
50
51 static struct {
52     struct Source *array;
53     int count;
54     ssize_t size;
55 } sources;
56
57 static struct {
58     time_t atime;
59     time_t mtime;
60     time_t ctime;
61 } times;
62
63 /**
64  * Overlay
65  */
66 static struct {
67     struct Source source;
68     struct Region *table;
69     size_t count;
70     size_t limit;
71 } overlay;
72
73 static void usage();
74
75 #define FRAG(m) (overlay.table+m)
76 #define BEG(m) (FRAG(m)->pos)
77 #define END(m) (FRAG(m)->pos + FRAG(m)->size)
78
79 static ssize_t overlay_prior_fragment(off_t pos) {
80     size_t lo = 0, hi = overlay.count;
81     while ( lo < hi ) {
82         size_t m = ( lo + hi ) / 2;
83         if ( m == lo ) {
84             return BEG( m ) < pos? m : -1;
85         }
86         if ( BEG( m ) <= pos ) {
87             lo = m;
88         } else {
89             hi = m;
90         }
91     }
92     return -1;
93 }
94
95 static void overlay_save_count() {
96     lseek( overlay.source.fd, overlay.source.to, SEEK_SET );
97     size_t size = sizeof( overlay.count );
98     char *p = (char *) &overlay.count ;
99     while ( size > 0 ) {
100         size_t n = write( overlay.source.fd, p, size );
101         if ( n < 0 ) {
102             perror( overlay.source.filename );
103             exit( 1 );
104         }
105         size -= n;
106         p += n;
107     }
108     if ( overlay.source.dirty++ > 1000 ) {
109         fsync( overlay.source.fd );
110         overlay.source.dirty = 0;
111     }
112 }
113
114 static void overlay_save_table(size_t lo,size_t hi) {
115     char *p = (char *) FRAG(lo);
116     size_t pos =  overlay.source.to + sizeof( overlay.count ) +
117         lo * sizeof( struct Region );
118     size_t size = ( hi - lo ) * sizeof( struct Region );
119     if ( pos != lseek( overlay.source.fd, pos, SEEK_SET ) ) {
120         fprintf( stderr, "%s: seek error\n", overlay.source.filename );
121         exit( 1 );
122     }
123     while ( size > 0 ) {
124         size_t n = write( overlay.source.fd, p, size );
125         if ( n < 0 ) {
126             perror( overlay.source.filename );
127             exit( 1 );
128         }
129         size -= n;
130         p += n;
131     }
132     if ( overlay.source.dirty++ > 1000 ) {
133         fsync( overlay.source.fd );
134         overlay.source.dirty = 0;
135     }
136 }
137
138 static void overlay_insert(size_t p,off_t pos,size_t size) {
139     size_t bytes;
140     if ( overlay.count >= overlay.limit ) {
141         overlay.limit = overlay.count + 10;
142         bytes = overlay.limit * sizeof( struct Region );
143         overlay.table = overlay.table?
144             realloc( overlay.table, bytes ) : malloc( bytes );
145     }
146     bytes = ( overlay.count++ - p ) * sizeof( struct Region );
147     if ( bytes ) {
148         memmove( FRAG( p+1 ), FRAG( p ), bytes );
149     }
150     FRAG( p )->pos = pos;
151     FRAG( p )->size = size;
152     overlay_save_count();
153 }
154
155 static void overlay_delete(size_t p) {
156     if ( p < --overlay.count ) {
157         size_t size = ( overlay.count - p ) * sizeof( struct Region );
158         memmove( FRAG(p), FRAG(p+1), size );
159     }
160     overlay_save_count();
161 }
162
163 static void overlay_mark(off_t pos,size_t size) {
164 #if DEBUG
165     fprintf( stderr, "overlay_mark( %ld, %ld )\n", pos, size );
166 #endif
167     int deleted = 0;
168     ssize_t q;
169     ssize_t p = overlay_prior_fragment( pos );
170     // p is the nearest region below pos (or -1)
171     if ( p >= 0 && pos <= END(p) ) {
172         // p overlaps mark region
173         if ( END(p) >= pos + size ) {
174 #if DEBUG
175         fprintf( stderr, "overlay size 1( %ld )\n", FRAG(p)->size );
176 #endif
177             return; // new mark within existing.
178         }
179         // new mark region extends existing
180         FRAG(p)->size = pos + size - BEG(p);
181         q = p+1;
182         while ( q < overlay.count && BEG(q) <= END(p) ) {
183             if ( END(q) > END(p) ) {
184                 FRAG(p)->size = END(q) - BEG(p);
185             }
186             overlay_delete( q );
187             deleted++;
188         }
189         overlay_save_table( p, deleted? overlay.count : q );
190 #if DEBUG
191         fprintf( stderr, "overlay size 2( %ld ) deleted %d\n",
192                  FRAG(p)->size, deleted );
193 #endif
194         return;
195     }
196     // The region p does not expand into new mark region
197     p++; // subsequent region
198     if ( p >= overlay.count || BEG(p) > pos + size ) {
199         // New mark is separate region at p
200         overlay_insert( p, pos, size);
201 #if DEBUG
202         fprintf( stderr, "overlay size 4( %ld )\n", FRAG(p)->size );
203 #endif
204         overlay_save_table( p, overlay.count );
205         return;
206     }
207     // New marks start before and overlap with the region
208     if ( BEG(p) + FRAG(p)->size < pos + size ) {
209         FRAG(p)->size = size; // new mark covers old region
210     } else {
211         FRAG(p)->size += BEG(p) - pos;
212     }
213     BEG(p) = pos;
214     q = p+1;
215     while ( q < overlay.count && BEG(q) <= END(p) ) {
216         if ( END(q) > END(p) ) {
217             FRAG(p)->size = END(q) - BEG(p);
218         }
219         overlay_delete( q );
220         deleted++;
221     }
222     overlay_save_table( p, deleted? overlay.count : q );
223 #if DEBUG
224     fprintf( stderr, "overlay size 4( %ld ) deleted %d\n",
225              FRAG(p)->size, deleted );
226 #endif
227 }
228
229 static void setup_overlay(char *filename) {
230     overlay.source.filename = filename;
231     overlay.source.fd = open( filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR );
232     if ( overlay.source.fd < 0 ) {
233         perror( filename );
234         usage();
235     }
236 }
237
238 #if DEBUG
239 static void print_source(struct Source *p) {
240     fprintf( stderr, "%p { %s, %ld, %ld, %ld, %d }\n",
241              p, p->filename, p->from, p->to, p->start, p-> fd );
242 }
243 #endif
244
245 static char *range;
246 static unsigned int c;
247 static int RANGE(int s,int n ) {
248     return ( s == n ) && *(range+c) == 0;
249 }
250
251 static int setup_sources(char **argv,int i,int n) {
252     sources.array = calloc( n, sizeof( struct Source ) );
253     if ( sources.array == 0 ) {
254         return 1;
255     }
256     sources.count = n;
257     int j = 0;
258     sources.size = 0;
259     for ( ; j < n; i++, j++ ) {
260         struct stat filestat;
261         struct Source *p = sources.array + j;
262         // Open the fragment file rw if possible, else ro
263         range = strrchr( argv[i], '/' ); // last '/'
264         p->filename = range? strndup( argv[i], range - argv[i] ) : argv[i];
265         p->fd = open( p->filename, O_RDWR );
266         int rdonly = 0;
267         if ( p->fd < 0 ) {
268             rdonly = 1;
269             p->fd = open( p->filename, O_RDONLY );
270         }
271         if ( p->fd < 0 ) {
272             perror( p->filename );
273             return 1; // Error return
274         }
275         if ( stat( p->filename, &filestat ) ) {
276             perror( p->filename );
277             return 1; 
278         }
279         if ( rdonly ) {
280             fprintf( stderr, "** %s opened read-only\n", p->filename );
281         }
282         p->from = 0;
283         p->to = filestat.st_size;
284         // Process any range variation
285         if ( range && *(++range) ) {
286             int a,b;
287             if ( 0 ) {
288             } else if ( RANGE( sscanf( range, "%d:%d%n", &a, &b, &c ), 2 )) {
289                 p->from = ( a < 0 )? ( p->to + a ) : a;
290                 p->to = ( b < 0 )? ( p->to + b ) : b;
291             } else if ( RANGE( sscanf( range, "%d+%d%n", &a, &b, &c ), 2 )) {
292                 p->from = ( a < 0 )? ( p->to + a ) : a;
293                 p->to = ( ( b < 0 )? p->to : p->from ) + b;
294             } else if ( RANGE( sscanf( range, "%d+%n", &a, &c ), 1 )) {
295                 p->from = ( a < 0 )? ( p->to + a ) : a;
296             } else if ( RANGE( sscanf( range, ":%d%n", &b, &c ), 1 )) {
297                 p->to = ( b < 0 )? ( p->to + b ) : b;
298             } else if ( RANGE( sscanf( range, "%d:%n", &a, &c ), 1 )) {
299                 p->from = ( a < 0 )? ( p->to + a ) : a;
300             } else if ( RANGE( sscanf( range, "%d%n", &a, &c ), 1 )) {
301                 if ( a >= 0 ) {
302                     p->from = a;
303                 } else {
304                     p->from = p->to + a;
305                 }
306             } else if ( RANGE( sscanf( range, ":%n", &c), 0 ) ) {
307                 // to end from start
308             } else {
309                 fprintf( stderr, "** BAD RANGE: %s\n", argv[i] );
310                 return 1;
311             }
312         }
313         if ( ( filestat.st_mode &  S_IFMT ) == S_IFCHR ) {
314             filestat.st_size = p->to; // Pretend size of character device
315         }
316         if ( p->from < 0 ) {
317             p->from = 0;
318         }
319         if ( p->to > filestat.st_size ) {
320             p->to = filestat.st_size;
321         }
322         if ( p->from >= p->to || p->from >= filestat.st_size ) {
323             fprintf( stderr, "** BAD RANGE: %s [%ld:%ld]\n",
324                      argv[i], p->from, p->to );
325             return 1;
326         }
327         p->start = sources.size; // the fusefile position of fragment
328         sources.size += p->to - p->from;
329 #if DEBUG
330         print_source( p );
331 #endif
332     }
333     return 0;
334 }
335
336 static int fusefile_getattr(const char *path,struct stat *stbuf) {
337 #if DEBUG
338     fprintf( stderr, "fusefile_getattr( %s )\n", path );
339 #endif
340     if ( strcmp( path, "/" ) != 0 ) {
341         return -ENOENT;
342     }
343 #if DEBUG
344     fprintf( stderr, "getattr %ld\n", sources.size );
345 #endif
346     memset( stbuf, 0, sizeof( struct stat ) );
347     stbuf->st_mode = S_IFREG | 0644; // Hmmm
348     stbuf->st_nlink = 1;
349     stbuf->st_size = sources.size;
350     stbuf->st_atime = times.atime;
351     stbuf->st_mtime = times.mtime;
352     stbuf->st_ctime = times.ctime;
353     stbuf->st_uid = getuid();
354     stbuf->st_gid = getgid();
355     return 0;
356 }
357
358 static int fusefile_chmod(const char *path,mode_t m) {
359 #if DEBUG
360     fprintf( stderr, "fusefile_chmod( %s, %d )\n", path, m );
361 #endif
362     return -1;
363 }
364
365 static int fusefile_open(const char *path,struct fuse_file_info *fi) {
366 #if DEBUG
367     fprintf( stderr, "fusefile_open( %s, %d )\n", path, fi->flags );
368     fprintf( stderr, "fixing( %d )\n", fi->flags | O_CLOEXEC );
369 #endif
370     if ( strcmp( path, "/" ) != 0 ) {
371         return -ENOENT;
372     }
373     // set O-CLOEXEC  for this opening?
374     times.atime = time( 0 );
375     return 0;
376 }
377
378 static int find_source(off_t offset) {
379     int lo = 0;
380     int hi = sources.count;
381     if ( offset >= sources.size ) {
382         return -1;
383     }
384 #if DEBUG
385     fprintf( stderr, "find_source( %ld )\n", offset );
386 #endif
387     while ( lo + 1 < hi ) {
388         int m = ( lo + hi ) / 2;
389         if ( offset < sources.array[ m ].start ) {
390 #if DEBUG
391             fprintf( stderr, "  offset < [%d].start: %ld\n",
392                      m, sources.array[ m ].start );
393 #endif
394             hi = m;
395         } else {
396 #if DEBUG
397             fprintf( stderr, "  offset >= [%d].start: %ld\n",
398                      m, sources.array[ m ].start );
399 #endif
400             lo = m;
401         }
402     }
403 #if DEBUG
404     fprintf( stderr, "found %d\n", lo );
405 #endif
406     return lo;
407 }
408
409 static int overlay_merge(char *buf,off_t off,size_t size) {
410 #if DEBUG
411     fprintf( stderr, "merge %ld %ld\n", off, size );
412 #endif
413     // Find nearest overlay data before or at off
414     ssize_t p = overlay_prior_fragment( off );
415     if ( p < 0 ) {
416         p = 0;
417     }
418     for ( ; p < overlay.count && BEG(p) < off+size; p++ ) {
419         size_t delta = FRAG(p)->size;
420         if ( BEG(p) > off ) {
421             size_t skip = BEG(p) - off;
422             off += skip;
423             size -= skip;
424             buf += skip;
425         } else {
426             delta = off - BEG(p);
427         }
428         if ( delta > size ) {
429             delta = size;
430         }
431         lseek( overlay.source.fd, off, SEEK_SET );
432         while ( delta > 0 ) {
433             size_t n = read( overlay.source.fd, buf, delta );
434             off += n;
435             size -= n;
436             delta -= n;
437             buf += n;
438         }
439     }
440 #if DEBUG
441     fprintf( stderr, "merged\n" );
442 #endif
443     return 0;
444 }
445
446 // Read <size> bytes from <offset> in file
447 static int fusefile_read(const char *path, char *buf, size_t size,
448                          off_t off, struct fuse_file_info *fi)
449 {
450 #if DEBUG
451     fprintf( stderr, "fusefile_read( %s )\n", path );
452 #endif
453     if( strcmp( path, "/" ) != 0 ) {
454         return -ENOENT;
455     }
456 #if DEBUG
457     fprintf( stderr, "read %ld %ld\n", off, size );
458 #endif
459     size_t rr = 0; // total reading
460     while ( size > 0 ) {
461 #if DEBUG
462         fprintf( stderr, "  find_source %ld %ld\n", off, size );
463 #endif
464         int i = find_source( off );
465         if ( i < 0 ) {
466             return ( off == sources.size )? rr : -ENOENT;
467         }
468         if ( sources.array[i].fd < 0 ) {
469             return -ENOENT;
470         }
471 #if DEBUG
472         print_source( &sources.array[i] );
473 #endif
474         times.atime = time( 0 );
475         size_t b = off - sources.array[i].start + sources.array[i].from;
476         size_t n = sources.array[i].to - b;
477         if ( n > size ) {
478             n = size;
479         }
480         if ( sources.array[i].dirty ) {
481             fsync( sources.array[i].fd );
482             sources.array[i].dirty = 0;
483         }
484 #if DEBUG
485         fprintf( stderr, "  seek fd=%d to %ld\n", sources.array[i].fd, b );
486 #endif
487         if ( lseek( sources.array[i].fd, b, SEEK_SET ) < 0 ) {
488             perror( sources.array[i].filename );
489             return -ENOENT;
490         }
491 #if DEBUG
492         fprintf( stderr, "  now read %ld from fd=%d\n",
493                  n, sources.array[i].fd );
494 #endif
495         ssize_t r = read( sources.array[i].fd, buf + rr, n );
496 #if DEBUG
497         fprintf( stderr, "  got %ld bytes\n", r );
498 #endif
499         if ( r < 0 ) {
500             perror( sources.array[i].filename );
501             return -ENOENT;
502         }
503         if ( r == 0 ) {
504             break;
505         }
506         if ( overlay.source.filename ) {
507             if ( overlay.source.dirty ) {
508                 fsync( overlay.source.fd );
509                 overlay.source.dirty = 0;
510             }
511             int x = overlay_merge( buf + rr, off + rr, r );
512             if ( x ) {
513                 return x;
514             }
515         }
516         rr += r;
517         off += r;
518         size -= r;
519     }
520 #if DEBUG
521     fprintf( stderr, "  total reading %ld bytes\n", rr );
522 #endif
523     return rr;
524 }
525
526 /**
527  * Poll for IO readiness.
528  */
529 int fusefile_poll(const char *path, struct fuse_file_info *fi,
530                    struct fuse_pollhandle *ph, unsigned *reventsp )
531 {
532 #if DEBUG
533     fprintf( stderr, "fusefile_poll( %s ) %p %d\n", path, ph, *reventsp );
534 #endif
535     if( strcmp( path, "/" ) != 0 ) {
536         return -ENOENT;
537     }
538     if ( ph ) {
539         return fuse_notify_poll( ph );
540     }
541     return 0;
542 }
543
544 static void overlay_load() {
545     lseek( overlay.source.fd, overlay.source.to, SEEK_SET );
546     size_t x = 0;
547     size_t size = sizeof( overlay.count );
548     if ( read( overlay.source.fd, &x, size ) != size ) {
549         return;
550     }
551 #if DEBUG
552     fprintf( stderr, "overlay: %s with %ld regions\n",
553              overlay.source.filename, x );
554 #endif
555     struct Region f = { 0, 0 };
556     size = sizeof( struct Region );
557     while ( x-- > 0 ) {
558         if ( read( overlay.source.fd, &f, size ) != size ) {
559             fprintf( stderr, "%s: bad meta data\n", overlay.source.filename );
560             exit( 1 );
561         }
562 #if DEBUG
563         fprintf( stderr, "overlay region: %ld %ld\n", f.pos, f.size );
564 #endif
565         overlay_mark( f.pos, f.size );
566     }
567 }
568
569 /**
570  * Write a full block of data over the sources at the offset
571  */
572 static int write_block(off_t off,const char *buf,size_t size) {
573 #if DEBUG
574     fprintf( stderr, "write_block( %ld, ?, %ld )\n", off, size );
575 #endif
576     if ( overlay.source.filename ) {
577         overlay_mark( off, size ); // Mark region as written
578     }
579     while ( size > 0 ) {
580         int index = find_source( off ); // index of source file
581         if ( index < 0 ) {
582             return -EIO; // past EOF
583         }
584         struct Source *source = overlay.source.filename?
585             &overlay.source :  &sources.array[ index ];
586         off_t from = off - source->start + source->from;
587         off_t max = source->to - from;
588         if ( lseek( source->fd, from, SEEK_SET ) < 0 ) {
589             return -EIO;
590         }
591         ssize_t todo = ( size < max )? size : max;
592         while ( todo > 0 ) {
593             times.mtime = time( 0 );
594             ssize_t n = write( source->fd, buf, todo );
595             if ( n <= 0 ) {
596                 return -EIO; // Something wrong
597             }
598             buf += n;
599             todo -= n;
600             size -= n;
601             off += n;
602         }
603         if ( source->dirty++ >= 1000 ) {
604             fsync( source->fd );
605             source->dirty = 0;
606         }
607     }
608     return 0;
609 }
610
611 static int fusefile_write_buf(const char *path, struct fuse_bufvec *buf,
612                               off_t off, struct fuse_file_info *fi) {
613 #if DEBUG
614     fprintf( stderr, "fusefile_write_buf( %s )\n", path );
615 #endif
616     if ( strcmp( path, "/" ) != 0 ) {
617         return -ENOENT;
618     }
619
620     size_t size = 0;
621     int i;
622     for ( i = 0; i < buf->count; i++ ) {
623         struct fuse_buf *p = &buf->buf[i];
624         if ( p->flags & FUSE_BUF_IS_FD ) {
625 #if DEBUG
626             fprintf( stderr, "Content held in a file ... HELP!!\n" );
627 #endif
628             return -EIO;
629         }
630         if ( write_block( off, (char*) p->mem, p->size ) < 0 ) {
631             return -EIO;
632         }
633         size += p->size;
634     }
635 #if DEBUG
636     fprintf( stderr, "fusefile_write_buf written %ld\n", size );
637 #endif
638     return size;
639 }
640
641 /**
642  * Write a fragment at <off>. This overwrites files.
643  */
644 static int fusefile_write(const char *path, const char *buf, size_t size,
645                           off_t off, struct fuse_file_info *fi)
646 {
647 #if DEBUG
648     fprintf( stderr, "fusefile_write( %s %ld )\n", path, size );
649 #endif
650     if ( strcmp( path, "/" ) != 0 ) {
651         return -ENOENT;
652     }
653
654     if ( write_block( off, buf, size ) < 0 ) {
655         return -EIO;
656     }
657     return size;
658 }
659
660 static void fusefile_destroy(void *data) {
661     char *mnt = (char*) data; // As passed to fuse_main
662 #if DEBUG
663     fprintf( stderr, "fusefile_destroy( %s )\n", mnt? mnt : "" );
664 #endif
665     if ( mnt ) {
666         unlink( mnt );
667     }
668 }
669
670 static void fsync_all_dirty() {
671     int i = 0;
672     for ( ; i < sources.count; i++ ) {
673         if ( sources.array[i].dirty ) {
674             fsync( sources.array[i].fd );
675             sources.array[i].dirty = 0;
676         }
677     }
678     if ( overlay.source.filename && overlay.source.dirty ) {
679         fsync( overlay.source.fd );
680         overlay.source.dirty = 0;
681     }
682 }
683
684 static int fusefile_flush(const char *path, struct fuse_file_info *info) {
685 #if DEBUG
686     fprintf( stderr, "fusefile_flush( %s )\n", path );
687 #endif
688     if ( strcmp( path, "/" ) != 0 ) {
689         return -ENOENT;
690     }
691     fsync_all_dirty();
692     return 0;
693 }
694
695 static int fusefile_release(const char *path, struct fuse_file_info *fi) {
696 #if DEBUG
697     fprintf( stderr, "fusefile_release( %s, %d )\n", path, fi->flags );
698 #endif
699     if ( strcmp( path, "/" ) != 0 ) {
700         return -ENOENT;
701     }
702     return 0;
703 }
704
705 static int fusefile_fsync(const char *path, int x, struct fuse_file_info *fi) {
706 #if DEBUG
707     fprintf( stderr, "fusefile_fsync( %s, %d )\n", path, x );
708 #endif
709     if ( strcmp( path, "/" ) != 0 ) {
710         return -ENOENT;
711     }
712     fsync_all_dirty();
713     return 0;
714 }
715
716 /**
717  * 
718  */
719 static int fusefile_truncate(const char *path, off_t len) {
720 #if DEBUG
721     fprintf( stderr, "fusefile_truncate( %s, %ld )\n", path, len );
722 #endif
723     if ( strcmp( path, "/" ) != 0 ) {
724         return -ENOENT;
725     }
726     return -EIO;
727 }
728
729 void *fusefile_init(struct fuse_conn_info *fci) {
730 #if DEBUG
731     fprintf( stderr, "fusefile_init( %d, %d )\n", fci->async_read, fci->want );
732 #endif
733     // Disable asynchronous reading
734     fci->async_read = 0;
735     fci->want &= ~FUSE_CAP_ASYNC_READ;
736 #if DEBUG
737     fprintf( stderr, "fusefile_init( %d, %d )\n", fci->async_read, fci->want );
738 #endif
739     return 0;
740 }
741
742 static struct fuse_operations fusefile_oper = {
743     .getattr = fusefile_getattr,
744     .chmod = fusefile_chmod,
745     .open = fusefile_open,
746     .read = fusefile_read,
747     .poll = fusefile_poll,
748     .write = fusefile_write,
749     .write_buf = fusefile_write_buf,
750     .destroy = fusefile_destroy,
751     .flush = fusefile_flush,
752     .release = fusefile_release,
753     .fsync = fusefile_fsync,
754     .truncate = fusefile_truncate,
755     //.truncate = fusefile_truncate,
756     //.release = fusefile_release,
757     .init = fusefile_init,
758 };
759
760 static void usage() {
761     char *usage =
762 "Usage: fusefile [ <fuse options> ] <mount> <file/from-to> ... \n"
763 "Mounts a virtual, file that is a concatenation of file fragments\n"
764         ;
765     fprintf( stderr, "%s", usage );
766     exit( 1 );
767 }
768
769 /**
770  * Set up the arguments for the fuse_main call, adding our own.
771  * argv[argc] is the mount point argument
772  */
773 static int setup_argv(int argc,char ***argv) {
774     // note: (*argv)[ argc ] is the mount point argument
775     char *OURS[] = {
776         "-odefault_permissions",
777         (*argv)[ argc ]
778     };
779 #define OURSN ( sizeof( OURS ) / sizeof( char* ) )
780     int N = argc + OURSN;
781     // Allocate new arg array plus terminating null pointer
782     char **out = malloc( ( N + 1 ) * sizeof( char* ) ); 
783     int i;
784     for ( i = 0; i < argc; i++ ) {
785         out[ i ] = (*argv)[i];
786         //fprintf( stderr, " %s", out[ i ] );
787     }
788     for ( i = 0; i < OURSN; i++ ) {
789         out[ argc + i ] = OURS[i];
790         //fprintf( stderr, " %s", out[ i ] );
791     }
792     out[ N ] = 0;
793     //fprintf( stderr, "\n" );
794     (*argv) = out;
795     return N; // Don't include the terminating null pointer
796 }
797
798 /**
799  * Mount a concatenation of files,
800  * [ <fuse options> ] <mount> <file/from-to> ...
801  */
802 int main(int argc, char *argv[])
803 {
804     char *mnt;
805     int mt;
806     int fg;
807     int i;
808     int fuseargc;
809     struct stat stbuf;
810     int temporary = 0;
811     // Scan past options
812     for ( i = 1; i < argc; i++ ) {
813         if ( *argv[i] != '-' ) {
814             break;
815         }
816     }
817     if ( i > argc - 2 ) { // At least mount point plus one source
818         usage();
819     }
820     fuseargc = i;
821     mnt = argv[ i++ ]; // First non-option argument is the mount pount
822     char *overlaytag = "-overlay:";
823     int overlaytagsize = strlen( overlaytag );
824     if ( strncmp( argv[i], overlaytag, overlaytagsize ) == 0 ) {
825         // consume "-overlay:filename"
826         setup_overlay( argv[i++] + overlaytagsize ); // Need a writable file
827         if ( i >= argc ) {
828             usage();
829         }
830     }
831     if ( setup_sources( argv, i, argc-i ) ) {
832         return 1;
833     }
834     if ( overlay.source.filename ) {
835         overlay.source.to = sources.size; // Register total size.
836         overlay_load();
837     }
838     if ( stat( mnt, &stbuf ) == -1 ) {
839         int fd = open( mnt, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR );
840         if ( fd < 0 ) {
841             perror( mnt );
842             return 1;
843         }
844         time_t now = time( 0 );
845         times.atime = now;
846         times.mtime = now;
847         times.ctime = now;
848         temporary = 1;
849         close( fd );
850     } else if ( ! S_ISREG( stbuf.st_mode ) ) {
851         fprintf( stderr, "mountpoint is not a regular file\n" );
852         return 1;
853     } else {
854         times.atime = stbuf.st_atime;
855         times.mtime = stbuf.st_mtime;
856         times.ctime = stbuf.st_ctime;
857     }
858
859     {
860         int fd = open( mnt, O_RDWR, S_IRUSR | S_IWUSR );
861         if ( fd < 0 ) {
862             perror( mnt );
863             return 1;
864         }
865         if ( lseek( fd, sources.size, SEEK_SET ) < 0 ) {
866             return -EIO;
867         }
868     }
869     fuseargc = setup_argv( fuseargc, &argv );
870     struct fuse_args args = FUSE_ARGS_INIT( fuseargc, argv );
871     if ( fuse_parse_cmdline( &args, &mnt, &mt, &fg ) ) {
872         return 1;
873     }
874     fuse_opt_free_args( &args );
875     if ( ! mnt ) {
876         fprintf( stderr, "missing mountpoint parameter\n" );
877         return 1;
878     }
879     return fuse_main( fuseargc, argv, &fusefile_oper, temporary? mnt : NULL );
880 }