30ec7fa284c7ce1f330daaede1de2fb1835ee9b5
[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         if ( END(p) < off ) {
420             continue;
421         }
422         size_t delta = FRAG(p)->size;
423         if ( BEG(p) < off ) {
424             delta -= off - BEG(p);
425         } else {
426             size_t skip = BEG(p) - off;
427             off += skip;
428             size -= skip;
429             buf += skip;
430         }
431         if ( delta > size ) {
432             delta = size;
433         }
434         lseek( overlay.source.fd, off, SEEK_SET );
435         while ( delta > 0 ) {
436             size_t n = read( overlay.source.fd, buf, delta );
437             off += n;
438             size -= n;
439             delta -= n;
440             buf += n;
441         }
442     }
443 #if DEBUG
444     fprintf( stderr, "merged\n" );
445 #endif
446     return 0;
447 }
448
449 // Read <size> bytes from <offset> in file
450 static int fusefile_read(const char *path, char *buf, size_t size,
451                          off_t off, struct fuse_file_info *fi)
452 {
453 #if DEBUG
454     fprintf( stderr, "fusefile_read( %s )\n", path );
455 #endif
456     if( strcmp( path, "/" ) != 0 ) {
457         return -ENOENT;
458     }
459 #if DEBUG
460     fprintf( stderr, "read %ld %ld\n", off, size );
461 #endif
462     size_t rr = 0; // total reading
463     while ( size > 0 ) {
464 #if DEBUG
465         fprintf( stderr, "  find_source %ld %ld\n", off, size );
466 #endif
467         int i = find_source( off );
468         if ( i < 0 ) {
469             return ( off == sources.size )? rr : -ENOENT;
470         }
471         if ( sources.array[i].fd < 0 ) {
472             return -ENOENT;
473         }
474 #if DEBUG
475         print_source( &sources.array[i] );
476 #endif
477         times.atime = time( 0 );
478         size_t b = off - sources.array[i].start + sources.array[i].from;
479         size_t n = sources.array[i].to - b;
480         if ( n > size ) {
481             n = size;
482         }
483         if ( sources.array[i].dirty ) {
484             fsync( sources.array[i].fd );
485             sources.array[i].dirty = 0;
486         }
487 #if DEBUG
488         fprintf( stderr, "  seek fd=%d to %ld\n", sources.array[i].fd, b );
489 #endif
490         if ( lseek( sources.array[i].fd, b, SEEK_SET ) < 0 ) {
491             perror( sources.array[i].filename );
492             return -ENOENT;
493         }
494 #if DEBUG
495         fprintf( stderr, "  now read %ld from fd=%d\n",
496                  n, sources.array[i].fd );
497 #endif
498         ssize_t r = read( sources.array[i].fd, buf + rr, n );
499 #if DEBUG
500         fprintf( stderr, "  got %ld bytes\n", r );
501 #endif
502         if ( r < 0 ) {
503             perror( sources.array[i].filename );
504             return -ENOENT;
505         }
506         if ( r == 0 ) {
507             break;
508         }
509         if ( overlay.source.filename ) {
510             if ( overlay.source.dirty ) {
511                 fsync( overlay.source.fd );
512                 overlay.source.dirty = 0;
513             }
514             int x = overlay_merge( buf + rr, off + rr, r );
515             if ( x ) {
516                 return x;
517             }
518         }
519         rr += r;
520         off += r;
521         size -= r;
522     }
523 #if DEBUG
524     fprintf( stderr, "  total reading %ld bytes\n", rr );
525 #endif
526     return rr;
527 }
528
529 /**
530  * Poll for IO readiness.
531  */
532 int fusefile_poll(const char *path, struct fuse_file_info *fi,
533                    struct fuse_pollhandle *ph, unsigned *reventsp )
534 {
535 #if DEBUG
536     fprintf( stderr, "fusefile_poll( %s ) %p %d\n", path, ph, *reventsp );
537 #endif
538     if( strcmp( path, "/" ) != 0 ) {
539         return -ENOENT;
540     }
541     if ( ph ) {
542         return fuse_notify_poll( ph );
543     }
544     return 0;
545 }
546
547 static void overlay_load() {
548     lseek( overlay.source.fd, overlay.source.to, SEEK_SET );
549     size_t x = 0;
550     size_t size = sizeof( overlay.count );
551     if ( read( overlay.source.fd, &x, size ) != size ) {
552         return;
553     }
554 #if DEBUG
555     fprintf( stderr, "overlay: %s with %ld regions\n",
556              overlay.source.filename, x );
557 #endif
558     struct Region f = { 0, 0 };
559     size = sizeof( struct Region );
560     while ( x-- > 0 ) {
561         if ( read( overlay.source.fd, &f, size ) != size ) {
562             fprintf( stderr, "%s: bad meta data\n", overlay.source.filename );
563             exit( 1 );
564         }
565 #if DEBUG
566         fprintf( stderr, "overlay region: %ld %ld\n", f.pos, f.size );
567 #endif
568         overlay_mark( f.pos, f.size );
569     }
570 }
571
572 /**
573  * Write a full block of data over the sources at the offset
574  */
575 static int write_block(off_t off,const char *buf,size_t size) {
576 #if DEBUG
577     fprintf( stderr, "write_block( %ld, ?, %ld )\n", off, size );
578 #endif
579     if ( overlay.source.filename ) {
580         overlay_mark( off, size ); // Mark region as written
581     }
582     while ( size > 0 ) {
583         int index = find_source( off ); // index of source file
584         if ( index < 0 ) {
585             return -EIO; // past EOF
586         }
587         struct Source *source = overlay.source.filename?
588             &overlay.source :  &sources.array[ index ];
589         off_t from = off - source->start + source->from;
590         off_t max = source->to - from;
591         if ( lseek( source->fd, from, SEEK_SET ) < 0 ) {
592             return -EIO;
593         }
594         ssize_t todo = ( size < max )? size : max;
595         while ( todo > 0 ) {
596             times.mtime = time( 0 );
597             ssize_t n = write( source->fd, buf, todo );
598             if ( n <= 0 ) {
599                 return -EIO; // Something wrong
600             }
601             buf += n;
602             todo -= n;
603             size -= n;
604             off += n;
605         }
606         if ( source->dirty++ >= 1000 ) {
607             fsync( source->fd );
608             source->dirty = 0;
609         }
610     }
611     return 0;
612 }
613
614 static int fusefile_write_buf(const char *path, struct fuse_bufvec *buf,
615                               off_t off, struct fuse_file_info *fi) {
616 #if DEBUG
617     fprintf( stderr, "fusefile_write_buf( %s )\n", path );
618 #endif
619     if ( strcmp( path, "/" ) != 0 ) {
620         return -ENOENT;
621     }
622
623     size_t size = 0;
624     int i;
625     for ( i = 0; i < buf->count; i++ ) {
626         struct fuse_buf *p = &buf->buf[i];
627         if ( p->flags & FUSE_BUF_IS_FD ) {
628 #if DEBUG
629             fprintf( stderr, "Content held in a file ... HELP!!\n" );
630 #endif
631             return -EIO;
632         }
633         if ( write_block( off, (char*) p->mem, p->size ) < 0 ) {
634             return -EIO;
635         }
636         size += p->size;
637     }
638 #if DEBUG
639     fprintf( stderr, "fusefile_write_buf written %ld\n", size );
640 #endif
641     return size;
642 }
643
644 /**
645  * Write a fragment at <off>. This overwrites files.
646  */
647 static int fusefile_write(const char *path, const char *buf, size_t size,
648                           off_t off, struct fuse_file_info *fi)
649 {
650 #if DEBUG
651     fprintf( stderr, "fusefile_write( %s %ld )\n", path, size );
652 #endif
653     if ( strcmp( path, "/" ) != 0 ) {
654         return -ENOENT;
655     }
656
657     if ( write_block( off, buf, size ) < 0 ) {
658         return -EIO;
659     }
660     return size;
661 }
662
663 static void fusefile_destroy(void *data) {
664     char *mnt = (char*) data; // As passed to fuse_main
665 #if DEBUG
666     fprintf( stderr, "fusefile_destroy( %s )\n", mnt? mnt : "" );
667 #endif
668     if ( mnt ) {
669         unlink( mnt );
670     }
671 }
672
673 static void fsync_all_dirty() {
674     int i = 0;
675     for ( ; i < sources.count; i++ ) {
676         if ( sources.array[i].dirty ) {
677             fsync( sources.array[i].fd );
678             sources.array[i].dirty = 0;
679         }
680     }
681     if ( overlay.source.filename && overlay.source.dirty ) {
682         fsync( overlay.source.fd );
683         overlay.source.dirty = 0;
684     }
685 }
686
687 static int fusefile_flush(const char *path, struct fuse_file_info *info) {
688 #if DEBUG
689     fprintf( stderr, "fusefile_flush( %s )\n", path );
690 #endif
691     if ( strcmp( path, "/" ) != 0 ) {
692         return -ENOENT;
693     }
694     fsync_all_dirty();
695     return 0;
696 }
697
698 static int fusefile_release(const char *path, struct fuse_file_info *fi) {
699 #if DEBUG
700     fprintf( stderr, "fusefile_release( %s, %d )\n", path, fi->flags );
701 #endif
702     if ( strcmp( path, "/" ) != 0 ) {
703         return -ENOENT;
704     }
705     return 0;
706 }
707
708 static int fusefile_fsync(const char *path, int x, struct fuse_file_info *fi) {
709 #if DEBUG
710     fprintf( stderr, "fusefile_fsync( %s, %d )\n", path, x );
711 #endif
712     if ( strcmp( path, "/" ) != 0 ) {
713         return -ENOENT;
714     }
715     fsync_all_dirty();
716     return 0;
717 }
718
719 /**
720  * 
721  */
722 static int fusefile_truncate(const char *path, off_t len) {
723 #if DEBUG
724     fprintf( stderr, "fusefile_truncate( %s, %ld )\n", path, len );
725 #endif
726     if ( strcmp( path, "/" ) != 0 ) {
727         return -ENOENT;
728     }
729     return -EIO;
730 }
731
732 void *fusefile_init(struct fuse_conn_info *fci) {
733 #if DEBUG
734     fprintf( stderr, "fusefile_init( %d, %d )\n", fci->async_read, fci->want );
735 #endif
736     // Disable asynchronous reading
737     fci->async_read = 0;
738     fci->want &= ~FUSE_CAP_ASYNC_READ;
739 #if DEBUG
740     fprintf( stderr, "fusefile_init( %d, %d )\n", fci->async_read, fci->want );
741 #endif
742     return 0;
743 }
744
745 static struct fuse_operations fusefile_oper = {
746     .getattr = fusefile_getattr,
747     .chmod = fusefile_chmod,
748     .open = fusefile_open,
749     .read = fusefile_read,
750     .poll = fusefile_poll,
751     .write = fusefile_write,
752     .write_buf = fusefile_write_buf,
753     .destroy = fusefile_destroy,
754     .flush = fusefile_flush,
755     .release = fusefile_release,
756     .fsync = fusefile_fsync,
757     .truncate = fusefile_truncate,
758     //.truncate = fusefile_truncate,
759     //.release = fusefile_release,
760     .init = fusefile_init,
761 };
762
763 static void usage() {
764     char *usage =
765 "Usage: fusefile [ <fuse options> ] <mount> <file/from-to> ... \n"
766 "Mounts a virtual, file that is a concatenation of file fragments\n"
767         ;
768     fprintf( stderr, "%s", usage );
769     exit( 1 );
770 }
771
772 /**
773  * Set up the arguments for the fuse_main call, adding our own.
774  * argv[argc] is the mount point argument
775  */
776 static int setup_argv(int argc,char ***argv) {
777     // note: (*argv)[ argc ] is the mount point argument
778     char *OURS[] = {
779         "-odefault_permissions",
780         (*argv)[ argc ]
781     };
782 #define OURSN ( sizeof( OURS ) / sizeof( char* ) )
783     int N = argc + OURSN;
784     // Allocate new arg array plus terminating null pointer
785     char **out = malloc( ( N + 1 ) * sizeof( char* ) ); 
786     int i;
787     for ( i = 0; i < argc; i++ ) {
788         out[ i ] = (*argv)[i];
789         //fprintf( stderr, " %s", out[ i ] );
790     }
791     for ( i = 0; i < OURSN; i++ ) {
792         out[ argc + i ] = OURS[i];
793         //fprintf( stderr, " %s", out[ i ] );
794     }
795     out[ N ] = 0;
796     //fprintf( stderr, "\n" );
797     (*argv) = out;
798     return N; // Don't include the terminating null pointer
799 }
800
801 /**
802  * Mount a concatenation of files,
803  * [ <fuse options> ] <mount> <file/from-to> ...
804  */
805 int main(int argc, char *argv[])
806 {
807     char *mnt;
808     int mt;
809     int fg;
810     int i;
811     int fuseargc;
812     struct stat stbuf;
813     int temporary = 0;
814     // Scan past options
815     for ( i = 1; i < argc; i++ ) {
816         if ( *argv[i] != '-' ) {
817             break;
818         }
819     }
820     if ( i > argc - 2 ) { // At least mount point plus one source
821         usage();
822     }
823     fuseargc = i;
824     mnt = argv[ i++ ]; // First non-option argument is the mount pount
825     char *overlaytag = "-overlay:";
826     int overlaytagsize = strlen( overlaytag );
827     if ( strncmp( argv[i], overlaytag, overlaytagsize ) == 0 ) {
828         // consume "-overlay:filename"
829         setup_overlay( argv[i++] + overlaytagsize ); // Need a writable file
830         if ( i >= argc ) {
831             usage();
832         }
833     }
834     if ( setup_sources( argv, i, argc-i ) ) {
835         return 1;
836     }
837     if ( overlay.source.filename ) {
838         overlay.source.to = sources.size; // Register total size.
839         overlay_load();
840     }
841     if ( stat( mnt, &stbuf ) == -1 ) {
842         int fd = open( mnt, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR );
843         if ( fd < 0 ) {
844             perror( mnt );
845             return 1;
846         }
847         time_t now = time( 0 );
848         times.atime = now;
849         times.mtime = now;
850         times.ctime = now;
851         temporary = 1;
852         close( fd );
853     } else if ( ! S_ISREG( stbuf.st_mode ) ) {
854         fprintf( stderr, "mountpoint is not a regular file\n" );
855         return 1;
856     } else {
857         times.atime = stbuf.st_atime;
858         times.mtime = stbuf.st_mtime;
859         times.ctime = stbuf.st_ctime;
860     }
861
862     {
863         int fd = open( mnt, O_RDWR, S_IRUSR | S_IWUSR );
864         if ( fd < 0 ) {
865             perror( mnt );
866             return 1;
867         }
868         if ( lseek( fd, sources.size, SEEK_SET ) < 0 ) {
869             return -EIO;
870         }
871     }
872     fuseargc = setup_argv( fuseargc, &argv );
873     struct fuse_args args = FUSE_ARGS_INIT( fuseargc, argv );
874     if ( fuse_parse_cmdline( &args, &mnt, &mt, &fg ) ) {
875         return 1;
876     }
877     fuse_opt_free_args( &args );
878     if ( ! mnt ) {
879         fprintf( stderr, "missing mountpoint parameter\n" );
880         return 1;
881     }
882     return fuse_main( fuseargc, argv, &fusefile_oper, temporary? mnt : NULL );
883 }