I'm using fuse3 to write a file system driver that should parse all the fuse options and then take two arguments, a device path (or file containing a file system image) and a mount-point.
Is there a convenient way to use fuse's command-line parsing to extract the device from the command line? I've taken to manipulating the arguments before I hand them to fuse like this:
struct fuse_args args;
const char *device = NULL;
memset(&args, 0, sizeof(args));
for (int i = 0; i < argc; ++i)
if (i > 0 && i == argc - 2 && *argv[i] != '-')
image = argv[i];
else
fuse_opt_add_arg(&args, argv[i]);
if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
return 1;
if (!device) {
usage(argv[0]);
fuse_opt_add_arg(&args, "--help");
}
fuse_main(args.argc, args.argv, &oper, nullptr);
But this is totally gross and doesn't quite work if the user specified only one argument, unless that argument also happens to be a valid mountpoint, because fuse seems to check the mountpoint viability before printing the help.
Surely this must be a common thing to want to do, so I'm wondering what the correct idiom is for such a file system.
so for this, you will have to restrict the user on the order of the cmd arguments(all fuse/mount options come before the device path and mount point). To make it simple, ensure that the path to the device and mount point are provided last:
So in your main function, this statement will check that there is a correct number of arguments and that the mount point and device do not start with hyphens as with options.
if ((argc < 3) || (argv[argc-2][0] == '-') || (argv[argc-1][0] == '-'))
{
fprintf(stderr, "usage: ./myfuse [FUSE and mount options] devicepath mountPoint\n");
abort();
}
Then extract the device path and store it in a struct:
struct my_state *my_data;
my_data = malloc(sizeof(struct my_state));
if (my_data == NULL) {
perror("main calloc");
abort();
}
my_data->devicepath = realpath(argv[argc - 2], NULL);
argv[argc-2] = argv[argc-1];
argv[argc-1] = NULL;
argc--;
Notice that I remove the device paths from argv and decrement argc by 1 before passing them to the fuse_main function Then while calling the fuse_main function make sure to pass the my_data struct:
fuse_main(argc, argv, &my_fuse_operations, my_data);
Here is the definition of the my_state struct which you can put in a header file:
struct my_state{
char *devicepath;
};
You should also add this definition in the header file below the struct definition:
#define BB_DATA ((struct my_state *) fuse_get_context()->private_data)
And also in your init function call fuse_get_context and return BB_DATA:
void *my_init()
{
fuse_get_context();
return BB_DATA;
}
The return value will be passed in the private_data field of fuse_context to all file operations and as a parameter to the destroy() method. The fuse_context is set up before this function is called, and fuse_get_context()->private_data returns the user_data passed to fuse_main().