Captured fusescript sub project as wip/fusescript branch wip/fusescript
authorRalph Ronnquist <rrq@rrq.au>
Mon, 7 Oct 2024 22:16:57 +0000 (09:16 +1100)
committerRalph Ronnquist <rrq@rrq.au>
Mon, 7 Oct 2024 22:16:57 +0000 (09:16 +1100)
Makefile
fusescript.c [new file with mode: 0644]

index 9f02adc50fd480aeecf75e2a9783c8b3a9c32fb1..1f5d030f26eeb5a55e2e8325cf5b3cba225476d3 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-BINS = fusefile
+BINS = fusefile fusescript
 
 default: $(BINS)
 
@@ -7,12 +7,14 @@ ${BINS}: CFLAGS += -DDEBUG=1 -g
 endif
 
 ${BINS}: CFLAGS += -Wall -D_FILE_OFFSET_BITS=64
-fusefile: LDFLAGS = -lfuse -pthread
+fusefile fusescript: LDFLAGS = -lfuse -pthread
 
-.INTERMEDIATE: fusefile.o
+.INTERMEDIATE: fusefile.o fusescript.o
 fusefile.o: fusefile.c
 
-fusefile: fusefile.o
+fusescript.o: fusescript.c
+
+fusefile fusescript: %: %.o
        $(CC) $(CFLAGS) $(CPPFLAGS) $? $(LDFLAGS) $(TARGET_ARCH) -o $@
 
 clean:
diff --git a/fusescript.c b/fusescript.c
new file mode 100644 (file)
index 0000000..98ef012
--- /dev/null
@@ -0,0 +1,343 @@
+/***
+    fusescript - fuse file mount that executes a script upon open and
+    return it stdout as data.
+    Copyright (C) 2024 Ralph Ronnquist
+    This program is free software: you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program. If not, see
+    <http://www.gnu.org/licenses/>.
+
+    This source was inspired by the "null.c" example of the libfuse
+    sources, which is distributed under GPL2, and copyright (C)
+    2001-2007 Miklos Szeredi <miklos@szeredi.hu>.
+*/
+
+#define FUSE_USE_VERSION 33
+
+#include <fuse.h>
+#include <fuse/fuse_lowlevel.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+
+static struct {
+    time_t atime;
+    time_t mtime;
+    time_t ctime;
+} times;
+
+static void usage();
+
+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;
+    }
+    memset( stbuf, 0, sizeof( struct stat ) );
+    stbuf->st_mode = S_IFREG | 0644; // Hmmm
+    stbuf->st_nlink = 1;
+    stbuf->st_size = 10000000;
+    stbuf->st_atime = times.atime;
+    stbuf->st_mtime = times.mtime;
+    stbuf->st_ctime = times.ctime;
+    stbuf->st_uid = getuid();
+    stbuf->st_gid = getgid();
+    return 0;
+}
+
+#if 0
+static int fusefile_chmod(const char *path,mode_t m) {
+#if DEBUG
+    fprintf( stderr, "fusefile_chmod( %s, %d )\n", path, m );
+#endif
+    return -1;
+}
+#endif
+
+static char *command = "echo hello";
+
+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?
+    times.atime = time( 0 );
+    char filename[100];
+    strcpy( filename, "/tmp/fusescript.XXXXXX" );
+    int temp = mkstemp( filename );
+    if ( temp < 0 || unlink( filename ) ) {
+       perror( "fusescript tempfile" );
+       return -ENOENT;
+    }
+    // Execute command to create the file
+    dup2( temp, 1 ); // Setup temp file as stdout
+    system( command );
+    fi->fh = temp;
+    return 0;
+}
+
+// Read <size> bytes from <offset> in file
+static int fusefile_read(const char *path, char *buf, size_t size,
+                        off_t off, struct fuse_file_info *fi)
+{
+    if( strcmp( path, "/" ) != 0 ) {
+       return -ENOENT;
+    }
+#if DEBUG
+    fprintf( stderr, "fusefile_read %ld + %ld\n", off, size );
+#endif
+    if ( lseek( fi->fh, off, SEEK_SET ) < 0 ) {
+       perror( "fusescript seek" );
+    }
+    char *p = buf;
+    while ( size > 0 ) {
+       ssize_t r = read( fi->fh, p, size );
+       if ( r < 0 ) {
+           perror( "fusescript read" );
+           return -ENOENT;
+       }
+       if ( r == 0 ) {
+           break; // EOF
+       }
+       p += r;
+       size -= r;
+    }
+    return p - buf;
+}
+
+/**
+ * Poll for IO readiness.
+ */
+int fusefile_poll(const char *path, struct fuse_file_info *fi,
+                  struct fuse_pollhandle *ph, unsigned *reventsp )
+{
+#if DEBUG
+    fprintf( stderr, "fusefile_poll( %s ) %p %d\n", path, ph, *reventsp );
+#endif
+    if( strcmp( path, "/" ) != 0 ) {
+       return -ENOENT;
+    }
+    if ( ph ) {
+       return fuse_notify_poll( ph );
+    }
+    return 0;
+}
+
+#if 0
+
+static int fusefile_write_buf(const char *path, struct fuse_bufvec *buf,
+                             off_t off, struct fuse_file_info *fi) {
+#if DEBUG
+    fprintf( stderr, "fusefile_write_buf( %s )\n", path );
+#endif
+    if ( strcmp( path, "/" ) != 0 ) {
+       return -ENOENT;
+    }
+    return -ENOENT;
+}
+
+/**
+ * Write a fragment at <off>. This overwrites files.
+ */
+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 %ld )\n", path, size );
+#endif
+    if ( strcmp( path, "/" ) != 0 ) {
+       return -ENOENT;
+    }
+    return -ENOENT;
+}
+
+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 -ENOENT;
+}
+
+#endif
+
+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;
+    }
+    close( fi->fh );
+    fi->fh = 0;
+    return 0;
+}
+
+#if 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 -ENOENT;
+}
+
+/**
+ * 
+ */
+static int fusefile_truncate(const char *path, off_t len) {
+#if DEBUG
+    fprintf( stderr, "fusefile_truncate( %s, %ld )\n", path, len );
+#endif
+    if ( strcmp( path, "/" ) != 0 ) {
+       return -ENOENT;
+    }
+    return -EIO;
+}
+#endif
+
+void *fusefile_init(struct fuse_conn_info *fci) {
+#if DEBUG
+    fprintf( stderr, "fusefile_init( %d, %d )\n", fci->async_read, fci->want );
+#endif
+    // Disable asynchronous reading
+    fci->async_read = 0;
+    fci->want &= ~FUSE_CAP_ASYNC_READ;
+#if DEBUG
+    fprintf( stderr, "fusefile_init( %d, %d )\n", fci->async_read, fci->want );
+#endif
+    return 0;
+}
+
+static struct fuse_operations fusefile_oper = {
+    .getattr = fusefile_getattr,
+    // NYI .fgetattr = fusefile_fgetattr,
+    //.chmod = fusefile_chmod,
+    .open = fusefile_open,
+    .read = fusefile_read,
+    .poll = fusefile_poll,
+    //.write = fusefile_write,
+    //.write_buf = fusefile_write_buf,
+    //.destroy = fusefile_destroy,
+    // NYI .access = fusefile_access,
+    //.flush = fusefile_flush,
+    .release = fusefile_release,
+    //.fsync = fusefile_fsync,
+    // NYI .ftruncate = fusefile_ftruncate,
+    //.truncate = fusefile_truncate,
+    //.truncate = fusefile_truncate,
+    //.release = fusefile_release,
+    .init = fusefile_init,
+};
+
+static void usage() {
+    char *usage =
+"Usage: fusescript [ <fuse options> ] <mount> <command>\n"
+"Mounts a virtual, file that exectes a command for data to read\n"
+       ;
+    fprintf( stderr, "%s", usage );
+    exit( 1 );
+}
+
+/**
+ * Set up the arguments for the fuse_main call, adding our own.
+ * argv[argc] is the mount point argument
+ */
+static int setup_argv(int argc,char ***argv) {
+    // note: (*argv)[ argc ] is the mount point argument
+    char *OURS[] = {
+       "-odefault_permissions",
+       //"-s", // Forced single-threading
+       (*argv)[ argc ]
+    };
+#define OURSN ( sizeof( OURS ) / sizeof( char* ) )
+    int N = argc + OURSN;
+    // Allocate new arg array plus terminating null pointer
+    char **out = malloc( ( N + 1 ) * sizeof( char* ) ); 
+    int i;
+    for ( i = 0; i < argc; i++ ) {
+       out[ i ] = (*argv)[i];
+       //fprintf( stderr, " %s", out[ i ] );
+    }
+    for ( i = 0; i < OURSN; i++ ) {
+       out[ argc + i ] = OURS[i];
+       //fprintf( stderr, " %s", out[ i ] );
+    }
+    out[ N ] = 0;
+    //fprintf( stderr, "\n" );
+    (*argv) = out;
+    return N; // Don't include the terminating null pointer
+}
+
+/**
+ * Set up the command string.
+ */
+static int setup_command(char **argv,int i,int n) {
+    command = argv[i];
+    return 0;
+}
+
+/**
+ * Mount a concatenation of files,
+ * [ <fuse options> ] <mount> <file/from-to> ...
+ */
+int main(int argc, char *argv[])
+{
+    char *mnt;
+    int mt;
+    int fg;
+    int i;
+    int fuseargc;
+    int temporary = 0;
+    // Scan past options
+    for ( i = 1; i < argc; i++ ) {
+       if ( *argv[i] != '-' ) {
+           break;
+       }
+    }
+    if ( i > argc - 2 ) { // At least mount point plus one command token
+       usage();
+    }
+    fuseargc = i;
+    mnt = argv[ i++ ]; // First non-option argument is the mount pount
+    if ( setup_command( argv, i, argc-i ) ) {
+       return 1;
+    }
+    fuseargc = setup_argv( fuseargc, &argv );
+    struct fuse_args args = FUSE_ARGS_INIT( fuseargc, argv );
+    if ( fuse_parse_cmdline( &args, &mnt, &mt, &fg ) ) {
+       return 1;
+    }
+    fuse_opt_free_args( &args );
+    if ( ! mnt ) {
+       fprintf( stderr, "missing mountpoint parameter\n" );
+       return 1;
+    }
+    return fuse_main( fuseargc, argv, &fusefile_oper, temporary? mnt : NULL );
+}