2001-2007 Miklos Szeredi <miklos@szeredi.hu>.
*/
-#define FUSE_USE_VERSION 31
+#define FUSE_USE_VERSION 33
-#include <fuse/fuse.h>
+#include <fuse.h>
#include <fuse/fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
static struct {
struct Source *array;
int count;
+ int limit;
ssize_t size;
} sources;
+#define SOURCEARRAYP(i) ((void*)&sources.array[ i ])
+
+/**
+ * Holds info about scratch pad file for 'write' events.
+ */
+static struct {
+ char *filename;
+ int fd;
+} pad;
+
#if DEBUG
static void print_source(struct Source *p) {
fprintf( stderr, "%p { %s, %ld, %ld, %ld, %d }\n",
- p, p->filename, p->from, p->to, p->start, p-> fd );
+ p, p->filename, p->from, p->to, p->start, p->fd );
}
#endif
m = i;
}
}
+#if DEBUG
fprintf( stderr, "m=%d s=%d\n", m, s );
+#endif
// Copy the filename, and set from and to
p->filename = strndup( in, ( s < 0 )? e : s );
struct stat buf;
if ( p->from < 0 ) {
p->from = 0;
}
+#if DEBUG
fprintf( stderr, "p->from=%ld\n", p->from );
+#endif
p->to = ( m < 0 )? buf.st_size : atol( in+m+1 );
if ( p->from > p->to || p->to > buf.st_size ) {
return 1;
return 1;
}
sources.count = n;
+ sources.limit = n;
int j = 0;
sources.size = 0;
for ( ; j < n; i++, j++ ) {
return 0;
}
-static int fusefile_getattr(const char *path, struct stat *stbuf )
-{
+static int fusefile_getattr(const char *path,struct stat *stbuf) {
+#if DEBUG
+ fprintf( stderr, "fusefile_getattr( %s )\n", path );
+#endif
if ( strcmp( path, "/" ) != 0 ) {
return -ENOENT;
}
#endif
memset( stbuf, 0, sizeof( struct stat ) );
stbuf->st_mode = S_IFREG | 0444; // Hmmm
+ if ( pad.filename ) {
+ stbuf->st_mode |= 0200;
+ }
stbuf->st_nlink = 1;
stbuf->st_size = sources.size;
time_t now = time( 0 );
return 0;
}
-static int fusefile_open(const char *path, struct fuse_file_info *fi)
-{
+static int fusefile_open(const char *path,struct fuse_file_info *fi) {
+#if DEBUG
+ fprintf( stderr, "fusefile_open( %s, %d )\n", path, fi->flags );
+ fprintf( stderr, "fixing( %d )\n", fi->flags | O_CLOEXEC );
+#endif
if ( strcmp( path, "/" ) != 0 ) {
return -ENOENT;
}
+ // set O-CLOEXEC for this opening?
return 0;
}
return lo;
}
-// Read <size> bytes from <offset> in file
+/**
+ * Insert a source fragment description into the table at <off>.
+ */
+static int insert_source(struct Source *source,size_t off) {
+ int index = find_source( off );
+ // Ensure at least 5 "free" Sources in <source.array>
+ // and allocate space for 20 new otherwise.
+ if ( sources.count + 5 > sources.limit ) {
+ size_t size = sources.limit + 20;
+ struct Source *new = realloc(
+ sources.array, size * sizeof( struct Source ) );
+ if ( new == 0 ) {
+ return -1;
+ }
+ sources.array = new;
+ sources.limit = size;
+ }
+ if ( index < sources.count ) {
+ ssize_t b = ( sources.count - index ) * sizeof(struct Source);
+ if ( sources.array[ index ].start < off ) {
+ // Split the <index> record at <off>
+ // and adjust index
+ b *= 2;
+ memcpy( SOURCEARRAYP( index+2 ), SOURCEARRAYP( index ), b );
+ sources.count += 2;
+ b = off - sources.array[ index ].start;
+ sources.array[ index + 2 ].from += b; // adjust tail fragment
+ sources.array[ index++ ].to = b; // adjust head fragment
+ } else {
+ // Insert the new source at <index>
+ memcpy( SOURCEARRAYP( index+1 ), SOURCEARRAYP( index ), b );
+ sources.count += 1;
+ }
+ } else {
+ // Append the new source to <sources> (at <index>)
+ sources.count += 1;
+ }
+ sources.array[ index ].filename = source->filename;
+ sources.array[ index ].fd = source->fd;
+ sources.array[ index ].from = source->from;
+ sources.array[ index ].to = source->to;
+ for ( ; index < sources.count; index++ ) {
+ sources.array[ index ].start = off;
+ off += sources.array[ index ].to - sources.array[ index ].from;
+ }
+ sources.size = off;
+ return index;
+}
+
+// Read <size> bytes from <off> in file
static int fusefile_read(const char *path, char *buf, size_t size,
- off_t offset, struct fuse_file_info *fi)
+ off_t off, struct fuse_file_info *fi)
{
- if( strcmp( path, "/" ) != 0 ) {
+#if DEBUG
+ fprintf( stderr, "fusefile_read( %s )\n", path );
+#endif
+ if ( strcmp( path, "/" ) != 0 ) {
return -ENOENT;
}
#if DEBUG
- fprintf( stderr, "read %ld %ld\n", offset, size );
+ fprintf( stderr, "read %ld %ld\n", off, size );
#endif
size_t rr = 0;
while ( size > 0 ) {
#if DEBUG
- fprintf( stderr, "find_source %ld %ld\n", offset, size );
+ fprintf( stderr, "find_source %ld %ld\n", off, size );
#endif
- int i = find_source( offset );
+ int i = find_source( off );
if ( i < 0 ) {
return -ENOENT;
}
#if DEBUG
print_source( &sources.array[i] );
#endif
- size_t b = offset - sources.array[i].start + sources.array[i].from;
+ size_t b = off - sources.array[i].start + sources.array[i].from;
size_t n = sources.array[i].to - b;
if ( n > size ) {
n = size;
break;
}
rr += r;
- offset += r;
+ off += r;
size -= r;
}
return rr;
}
+/**
+ * Write a full block of data.
+ */
+static int write_block(int fd,const char *buf,size_t size) {
+ size_t orig = size;
+ while ( size > 0 ) {
+ ssize_t n = write( fd, buf, size );
+ if ( n <= 0 ) {
+ return n;
+ }
+ }
+ return orig;
+}
+
+/**
+ * Insert a fragment at <off>. The data is appended to the pad file,
+ * and a descriptor is inserted; the fragment containing <off> is
+ * first split, unless <off> is at its start, and then new fragment
+ * descriptor is inserted.
+ */
+static int fusefile_write(const char *path, const char *buf, size_t size,
+ off_t off, struct fuse_file_info *fi)
+{
+#if DEBUG
+ fprintf( stderr, "fusefile_write( %s )\n", path );
+#endif
+ if ( strcmp( path, "/" ) != 0 ) {
+ return -ENOENT;
+ }
+#if DEBUG
+ fprintf( stderr, "write %ld %ld\n", off, size );
+#endif
+ // Ensure a pad was nominated
+ if ( pad.filename == 0 ) {
+ return 1;
+ }
+ static char meta[ 100 ];
+ sprintf( meta, "%ld\n%ld\n", off, size );
+ if ( write_block( pad.fd, meta, strlen( meta ) ) <= 0 ) {
+ perror( pad.filename );
+ return -EIO;
+ }
+ struct Source source = {
+ .filename = pad.filename,
+ .fd = pad.fd,
+ .from = lseek( pad.fd, 0, SEEK_END ),
+ .to = 0,
+ .start = 0
+ };
+ ssize_t n = write_block( pad.fd, buf, size );
+ if ( n != size ) {
+ return n;
+ }
+ source.to = source.from + size;
+ insert_source( &source, off );
+ return size;
+}
+
static void fusefile_destroy(void *data) {
- char *mnt = (char*) data;
+ char *mnt = (char*) data; // As passed to fuse_main
+#if DEBUG
+ fprintf( stderr, "fusefile_destroy( %s )\n", mnt? mnt : "" );
+#endif
if ( mnt ) {
unlink( mnt );
}
}
+static int fusefile_flush(const char *path, struct fuse_file_info *info) {
+#if DEBUG
+ fprintf( stderr, "fusefile_flush( %s )\n", path );
+#endif
+ if ( strcmp( path, "/" ) != 0 ) {
+ return -ENOENT;
+ }
+ return 0;
+}
+
+static int fusefile_release(const char *path, struct fuse_file_info *fi) {
+#if DEBUG
+ fprintf( stderr, "fusefile_release( %s, %d )\n", path, fi->flags );
+#endif
+ if ( strcmp( path, "/" ) != 0 ) {
+ return -ENOENT;
+ }
+ return 0;
+}
+
+static int fusefile_fsync(const char *path, int x, struct fuse_file_info *fi) {
+#if DEBUG
+ fprintf( stderr, "fusefile_fsync( %s, %d )\n", path, x );
+#endif
+ if ( strcmp( path, "/" ) != 0 ) {
+ return -ENOENT;
+ }
+ return 0;
+}
+
static struct fuse_operations fusefile_oper = {
.getattr = fusefile_getattr,
.open = fusefile_open,
.read = fusefile_read,
+ .write = fusefile_write,
.destroy = fusefile_destroy,
+ .flush = fusefile_flush,
+ .release = fusefile_release,
+ .fsync = fusefile_fsync,
+ //void *(*init) (struct fuse_conn_info *conn);
+ //int (*write_buf) (const char *, struct fuse_bufvec *buf, off_t off,
+ // struct fuse_file_info *);
+ //.truncate = fusefile_truncate,
+ //.flush = fusefile_flush,
+ //.release = fusefile_release,
};
static void usage() {
exit( 1 );
}
+/**
+ * Set up the arguments for the fuse_main call, adding our own.
+ */
+static int setup_argv(int argc,char ***argv) {
+ int n = argc + 1;
+ char **out = calloc( n--, sizeof( char* ) );
+ memcpy( (void*) out, (void*) (*argv), argc-- * sizeof( char* ) );
+ out[ n++ ] = out[ argc ]; // mount point
+ out[ argc++ ] = "-odefault_permissions";
+ (*argv) = out;
+ return n;
+}
+
/**
* Mount a concatenation of files,
* [ <fuse options> ] <mount> <file/from-to> ...
int mt;
int fg;
int i;
+ int fuseargc;
struct stat stbuf;
int temporary = 0;
// Scan past options
break;
}
}
- if ( i > argc - 2 ) { // At least one source
+ if ( i > argc - 2 ) { // At least mount point plus one source
usage();
}
- i++;
+ mnt = argv[ i++ ]; // First non-option argument is the mount pount
+ fuseargc = i;
+ if ( strncmp( argv[ i ], "pad=", 4 ) == 0 ) {
+ // First argument is the pad, if any, signaled with "pad=" prefix
+ pad.filename = argv[ i++ ] + 4; // (also move arg index)
+#if DEBUG
+ fprintf( stderr, "scratch pad=%s\n", pad.filename );
+#endif
+ pad.fd = open( pad.filename, O_RDWR | O_CREAT, 0600 );
+ if ( pad.fd < 0 ) {
+ perror( pad.filename );
+ exit( errno );
+ }
+ lseek( pad.fd, 0, SEEK_END ); // initial seek
+ }
if ( setup_sources( argv, i, argc-i ) ) {
return 1;
}
- mnt = argv[i-1];
if ( stat( mnt, &stbuf ) == -1 ) {
int fd = open( mnt, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR );
if ( fd < 0 ) {
fprintf( stderr, "mountpoint is not a regular file\n" );
return 1;
}
- struct fuse_args args = FUSE_ARGS_INIT( i, argv );
+ fuseargc = setup_argv( fuseargc, &argv );
+ struct fuse_args args = FUSE_ARGS_INIT( fuseargc, argv );
if ( fuse_parse_cmdline( &args, &mnt, &mt, &fg ) ) {
return 1;
}
fprintf( stderr, "missing mountpoint parameter\n" );
return 1;
}
- return fuse_main( i, argv, &fusefile_oper, temporary? mnt : NULL );
+ return fuse_main( fuseargc, argv, &fusefile_oper, temporary? mnt : NULL );
}