From b1715a5a62d2747a410409926c4b2d13ae16255d Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Sat, 8 Mar 2025 23:12:12 +0100 Subject: [PATCH 1/4] Remove fuse_opt compatibility code The fuse_opt compatibility code is no longer needed on macOS. Remove it since it is not included on any other platform. --- compat/fuse_opt.c | 364 ---------------------------------------------- compat/fuse_opt.h | 258 -------------------------------- meson.build | 2 +- 3 files changed, 1 insertion(+), 623 deletions(-) delete mode 100644 compat/fuse_opt.c delete mode 100644 compat/fuse_opt.h diff --git a/compat/fuse_opt.c b/compat/fuse_opt.c deleted file mode 100644 index 582c6ad3..00000000 --- a/compat/fuse_opt.c +++ /dev/null @@ -1,364 +0,0 @@ -/* - FUSE: Filesystem in Userspace - Copyright (C) 2001-2006 Miklos Szeredi - - This program can be distributed under the terms of the GNU LGPL. - See the file COPYING.LIB -*/ - -#include "fuse_opt.h" - -#include -#include -#include -#include - -struct fuse_opt_context { - void *data; - const struct fuse_opt *opt; - fuse_opt_proc_t proc; - int argctr; - int argc; - char **argv; - struct fuse_args outargs; - char *opts; - int nonopt; -}; - -void fuse_opt_free_args(struct fuse_args *args) -{ - if (args && args->argv && args->allocated) { - int i; - for (i = 0; i < args->argc; i++) - free(args->argv[i]); - free(args->argv); - args->argv = NULL; - args->allocated = 0; - } -} - -static int alloc_failed(void) -{ - fprintf(stderr, "fuse: memory allocation failed\n"); - return -1; -} - -int fuse_opt_add_arg(struct fuse_args *args, const char *arg) -{ - char **newargv; - char *newarg; - - assert(!args->argv || args->allocated); - - newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *)); - newarg = newargv ? strdup(arg) : NULL; - if (!newargv || !newarg) - return alloc_failed(); - - args->argv = newargv; - args->allocated = 1; - args->argv[args->argc++] = newarg; - args->argv[args->argc] = NULL; - return 0; -} - -int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg) -{ - assert(pos <= args->argc); - if (fuse_opt_add_arg(args, arg) == -1) - return -1; - - if (pos != args->argc - 1) { - char *newarg = args->argv[args->argc - 1]; - memmove(&args->argv[pos + 1], &args->argv[pos], - sizeof(char *) * (args->argc - pos - 1)); - args->argv[pos] = newarg; - } - return 0; -} - -static int next_arg(struct fuse_opt_context *ctx, const char *opt) -{ - if (ctx->argctr + 1 >= ctx->argc) { - fprintf(stderr, "fuse: missing argument after `%s'\n", opt); - return -1; - } - ctx->argctr++; - return 0; -} - -static int add_arg(struct fuse_opt_context *ctx, const char *arg) -{ - return fuse_opt_add_arg(&ctx->outargs, arg); -} - -int fuse_opt_add_opt(char **opts, const char *opt) -{ - char *newopts; - if (!*opts) - newopts = strdup(opt); - else { - unsigned oldlen = strlen(*opts); - newopts = realloc(*opts, oldlen + 1 + strlen(opt) + 1); - if (newopts) { - newopts[oldlen] = ','; - strcpy(newopts + oldlen + 1, opt); - } - } - if (!newopts) - return alloc_failed(); - - *opts = newopts; - return 0; -} - -static int add_opt(struct fuse_opt_context *ctx, const char *opt) -{ - return fuse_opt_add_opt(&ctx->opts, opt); -} - -static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key, - int iso) -{ - if (key == FUSE_OPT_KEY_DISCARD) - return 0; - - if (key != FUSE_OPT_KEY_KEEP && ctx->proc) { - int res = ctx->proc(ctx->data, arg, key, &ctx->outargs); - if (res == -1 || !res) - return res; - } - if (iso) - return add_opt(ctx, arg); - else - return add_arg(ctx, arg); -} - -static int match_template(const char *t, const char *arg, unsigned *sepp) -{ - int arglen = strlen(arg); - const char *sep = strchr(t, '='); - sep = sep ? sep : strchr(t, ' '); - if (sep && (!sep[1] || sep[1] == '%')) { - int tlen = sep - t; - if (sep[0] == '=') - tlen ++; - if (arglen >= tlen && strncmp(arg, t, tlen) == 0) { - *sepp = sep - t; - return 1; - } - } - if (strcmp(t, arg) == 0) { - *sepp = 0; - return 1; - } - return 0; -} - -static const struct fuse_opt *find_opt(const struct fuse_opt *opt, - const char *arg, unsigned *sepp) -{ - for (; opt && opt->templ; opt++) - if (match_template(opt->templ, arg, sepp)) - return opt; - return NULL; -} - -int fuse_opt_match(const struct fuse_opt *opts, const char *opt) -{ - unsigned dummy; - return find_opt(opts, opt, &dummy) ? 1 : 0; -} - -static int process_opt_param(void *var, const char *format, const char *param, - const char *arg) -{ - assert(format[0] == '%'); - if (format[1] == 's') { - char *copy = strdup(param); - if (!copy) - return alloc_failed(); - - *(char **) var = copy; - } else { - if (sscanf(param, format, var) != 1) { - fprintf(stderr, "fuse: invalid parameter in option `%s'\n", arg); - return -1; - } - } - return 0; -} - -static int process_opt(struct fuse_opt_context *ctx, - const struct fuse_opt *opt, unsigned sep, - const char *arg, int iso) -{ - if (opt->offset == -1U) { - if (call_proc(ctx, arg, opt->value, iso) == -1) - return -1; - } else { - void *var = ctx->data + opt->offset; - if (sep && opt->templ[sep + 1]) { - const char *param = arg + sep; - if (opt->templ[sep] == '=') - param ++; - if (process_opt_param(var, opt->templ + sep + 1, - param, arg) == -1) - return -1; - } else - *(int *)var = opt->value; - } - return 0; -} - -static int process_opt_sep_arg(struct fuse_opt_context *ctx, - const struct fuse_opt *opt, unsigned sep, - const char *arg, int iso) -{ - int res; - char *newarg; - char *param; - - if (next_arg(ctx, arg) == -1) - return -1; - - param = ctx->argv[ctx->argctr]; - newarg = malloc(sep + strlen(param) + 1); - if (!newarg) - return alloc_failed(); - - memcpy(newarg, arg, sep); - strcpy(newarg + sep, param); - res = process_opt(ctx, opt, sep, newarg, iso); - free(newarg); - - return res; -} - -static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso) -{ - unsigned sep; - const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep); - if (opt) { - for (; opt; opt = find_opt(opt + 1, arg, &sep)) { - int res; - if (sep && opt->templ[sep] == ' ' && !arg[sep]) - res = process_opt_sep_arg(ctx, opt, sep, arg, iso); - else - res = process_opt(ctx, opt, sep, arg, iso); - if (res == -1) - return -1; - } - return 0; - } else - return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso); -} - -static int process_real_option_group(struct fuse_opt_context *ctx, char *opts) -{ - char *sep; - - do { - int res; - sep = strchr(opts, ','); - if (sep) - *sep = '\0'; - res = process_gopt(ctx, opts, 1); - if (res == -1) - return -1; - opts = sep + 1; - } while (sep); - - return 0; -} - -static int process_option_group(struct fuse_opt_context *ctx, const char *opts) -{ - int res; - char *copy; - const char *sep = strchr(opts, ','); - if (!sep) - return process_gopt(ctx, opts, 1); - - copy = strdup(opts); - if (!copy) { - fprintf(stderr, "fuse: memory allocation failed\n"); - return -1; - } - res = process_real_option_group(ctx, copy); - free(copy); - return res; -} - -static int process_one(struct fuse_opt_context *ctx, const char *arg) -{ - if (ctx->nonopt || arg[0] != '-') - return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0); - else if (arg[1] == 'o') { - if (arg[2]) - return process_option_group(ctx, arg + 2); - else { - if (next_arg(ctx, arg) == -1) - return -1; - - return process_option_group(ctx, ctx->argv[ctx->argctr]); - } - } else if (arg[1] == '-' && !arg[2]) { - if (add_arg(ctx, arg) == -1) - return -1; - ctx->nonopt = ctx->outargs.argc; - return 0; - } else - return process_gopt(ctx, arg, 0); -} - -static int opt_parse(struct fuse_opt_context *ctx) -{ - if (ctx->argc) { - if (add_arg(ctx, ctx->argv[0]) == -1) - return -1; - } - - for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++) - if (process_one(ctx, ctx->argv[ctx->argctr]) == -1) - return -1; - - if (ctx->opts) { - if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 || - fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1) - return -1; - } - if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc) { - free(ctx->outargs.argv[ctx->outargs.argc - 1]); - ctx->outargs.argv[--ctx->outargs.argc] = NULL; - } - - return 0; -} - -int fuse_opt_parse(struct fuse_args *args, void *data, - const struct fuse_opt opts[], fuse_opt_proc_t proc) -{ - int res; - struct fuse_opt_context ctx = { - .data = data, - .opt = opts, - .proc = proc, - }; - - if (!args || !args->argv || !args->argc) - return 0; - - ctx.argc = args->argc; - ctx.argv = args->argv; - - res = opt_parse(&ctx); - if (res != -1) { - struct fuse_args tmp = *args; - *args = ctx.outargs; - ctx.outargs = tmp; - } - free(ctx.opts); - fuse_opt_free_args(&ctx.outargs); - return res; -} diff --git a/compat/fuse_opt.h b/compat/fuse_opt.h deleted file mode 100644 index de38451b..00000000 --- a/compat/fuse_opt.h +++ /dev/null @@ -1,258 +0,0 @@ -/* - FUSE: Filesystem in Userspace - Copyright (C) 2001-2006 Miklos Szeredi - - This program can be distributed under the terms of the GNU GPL. - See the file COPYING. -*/ - -#ifndef _FUSE_OPT_H_ -#define _FUSE_OPT_H_ - -/* This file defines the option parsing interface of FUSE */ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Option description - * - * This structure describes a single option, and an action associated - * with it, in case it matches. - * - * More than one such match may occur, in which case the action for - * each match is executed. - * - * There are three possible actions in case of a match: - * - * i) An integer (int or unsigned) variable determined by 'offset' is - * set to 'value' - * - * ii) The processing function is called, with 'value' as the key - * - * iii) An integer (any) or string (char *) variable determined by - * 'offset' is set to the value of an option parameter - * - * 'offset' should normally be either set to - * - * - 'offsetof(struct foo, member)' actions i) and iii) - * - * - -1 action ii) - * - * The 'offsetof()' macro is defined in the header. - * - * The template determines which options match, and also have an - * effect on the action. Normally the action is either i) or ii), but - * if a format is present in the template, then action iii) is - * performed. - * - * The types of templates are: - * - * 1) "-x", "-foo", "--foo", "--foo-bar", etc. These match only - * themselves. Invalid values are "--" and anything beginning - * with "-o" - * - * 2) "foo", "foo-bar", etc. These match "-ofoo", "-ofoo-bar" or - * the relevant option in a comma separated option list - * - * 3) "bar=", "--foo=", etc. These are variations of 1) and 2) - * which have a parameter - * - * 4) "bar=%s", "--foo=%lu", etc. Same matching as above but perform - * action iii). - * - * 5) "-x ", etc. Matches either "-xparam" or "-x param" as - * two separate arguments - * - * 6) "-x %s", etc. Combination of 4) and 5) - * - * If the format is "%s", memory is allocated for the string unlike - * with scanf(). - */ -struct fuse_opt { - /** Matching template and optional parameter formatting */ - const char *templ; - - /** - * Offset of variable within 'data' parameter of fuse_opt_parse() - * or -1 - */ - unsigned long offset; - - /** - * Value to set the variable to, or to be passed as 'key' to the - * processing function. Ignored if template has a format - */ - int value; -}; - -/** - * Key option. In case of a match, the processing function will be - * called with the specified key. - */ -#define FUSE_OPT_KEY(templ, key) { templ, -1U, key } - -/** - * Last option. An array of 'struct fuse_opt' must end with a NULL - * template value - */ -#define FUSE_OPT_END { .templ = NULL } - -/** - * Argument list - */ -struct fuse_args { - /** Argument count */ - int argc; - - /** Argument vector. NULL terminated */ - char **argv; - - /** Is 'argv' allocated? */ - int allocated; -}; - -/** - * Initializer for 'struct fuse_args' - */ -#define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 } - -/** - * Key value passed to the processing function if an option did not - * match any template - */ -#define FUSE_OPT_KEY_OPT -1 - -/** - * Key value passed to the processing function for all non-options - * - * Non-options are the arguments beginning with a charater other than - * '-' or all arguments after the special '--' option - */ -#define FUSE_OPT_KEY_NONOPT -2 - -/** - * Special key value for options to keep - * - * Argument is not passed to processing function, but behave as if the - * processing function returned 1 - */ -#define FUSE_OPT_KEY_KEEP -3 - -/** - * Special key value for options to discard - * - * Argument is not passed to processing function, but behave as if the - * processing function returned zero - */ -#define FUSE_OPT_KEY_DISCARD -4 - -/** - * Processing function - * - * This function is called if - * - option did not match any 'struct fuse_opt' - * - argument is a non-option - * - option did match and offset was set to -1 - * - * The 'arg' parameter will always contain the whole argument or - * option including the parameter if exists. A two-argument option - * ("-x foo") is always converted to single arguemnt option of the - * form "-xfoo" before this function is called. - * - * Options of the form '-ofoo' are passed to this function without the - * '-o' prefix. - * - * The return value of this function determines whether this argument - * is to be inserted into the output argument vector, or discarded. - * - * @param data is the user data passed to the fuse_opt_parse() function - * @param arg is the whole argument or option - * @param key determines why the processing function was called - * @param outargs the current output argument list - * @return -1 on error, 0 if arg is to be discarded, 1 if arg should be kept - */ -typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key, - struct fuse_args *outargs); - -/** - * Option parsing function - * - * If 'args' was returned from a previous call to fuse_opt_parse() or - * it was constructed from - * - * A NULL 'args' is equivalent to an empty argument vector - * - * A NULL 'opts' is equivalent to an 'opts' array containing a single - * end marker - * - * A NULL 'proc' is equivalent to a processing function always - * returning '1' - * - * @param args is the input and output argument list - * @param data is the user data - * @param opts is the option description array - * @param proc is the processing function - * @return -1 on error, 0 on success - */ -int fuse_opt_parse(struct fuse_args *args, void *data, - const struct fuse_opt opts[], fuse_opt_proc_t proc); - -/** - * Add an option to a comma separated option list - * - * @param opts is a pointer to an option list, may point to a NULL value - * @param opt is the option to add - * @return -1 on allocation error, 0 on success - */ -int fuse_opt_add_opt(char **opts, const char *opt); - -/** - * Add an argument to a NULL terminated argument vector - * - * @param args is the structure containing the current argument list - * @param arg is the new argument to add - * @return -1 on allocation error, 0 on success - */ -int fuse_opt_add_arg(struct fuse_args *args, const char *arg); - -/** - * Add an argument at the specified position in a NULL terminated - * argument vector - * - * Adds the argument to the N-th position. This is useful for adding - * options at the beggining of the array which must not come after the - * special '--' option. - * - * @param args is the structure containing the current argument list - * @param pos is the position at which to add the argument - * @param arg is the new argument to add - * @return -1 on allocation error, 0 on success - */ -int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg); - -/** - * Free the contents of argument list - * - * The structure itself is not freed - * - * @param args is the structure containing the argument list - */ -void fuse_opt_free_args(struct fuse_args *args); - - -/** - * Check if an option matches - * - * @param opts is the option description array - * @param opt is the option to match - * @return 1 if a match is found, 0 if not - */ -int fuse_opt_match(const struct fuse_opt opts[], const char *opt); - -#ifdef __cplusplus -} -#endif - -#endif /* _FUSE_OPT_H_ */ diff --git a/meson.build b/meson.build index f990ecbf..a408661c 100644 --- a/meson.build +++ b/meson.build @@ -35,7 +35,7 @@ include_dirs = [ include_directories('.') ] sshfs_sources = ['sshfs.c', 'cache.c'] if target_machine.system() == 'darwin' cfg.set_quoted('IDMAP_DEFAULT', 'user') - sshfs_sources += [ 'compat/fuse_opt.c', 'compat/darwin_compat.c' ] + sshfs_sources += [ 'compat/darwin_compat.c' ] include_dirs += [ include_directories('compat') ] else cfg.set_quoted('IDMAP_DEFAULT', 'none') From a9eb71cb1cfd1ae2bb9bd58e2405fde9bb9f5e75 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Sat, 8 Mar 2025 23:16:27 +0100 Subject: [PATCH 2/4] Remove conditional fuse_darwin.h include The header file is no longer available in macFUSE 4. --- sshfs.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/sshfs.c b/sshfs.c index 809568ad..ad8192cd 100644 --- a/sshfs.c +++ b/sshfs.c @@ -14,9 +14,6 @@ #if !defined(__CYGWIN__) # include #endif -#ifdef __APPLE__ -# include -#endif #include #include #include From ed0825440c48895b7e20cc1440bbafd8d9c88eb8 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Sat, 8 Mar 2025 23:26:32 +0100 Subject: [PATCH 3/4] Mountpoint does not have to exist on macOS macFUSE will create the mounpoint (in case it does not exist) before mounting the volume. This allows unprivileged users to mount volumes under /Volumes. --- sshfs.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sshfs.c b/sshfs.c index ad8192cd..676fd586 100644 --- a/sshfs.c +++ b/sshfs.c @@ -3798,6 +3798,16 @@ static int sshfs_opt_proc(void *data, const char *arg, int key, sshfs.mountpoint = strdup(arg); } else { sshfs.mountpoint = realpath(arg, NULL); +#ifdef __APPLE__ + if (!sshfs.mountpoint) { + /* + * The mountpoint does not exist, yet. + * macFUSE will try to create it before + * mounting the volume. + */ + sshfs.mountpoint = strdup(arg); + } +#endif } #endif if (!sshfs.mountpoint) { From ccb6821019c19600110af6750e0d2395a9401617 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Sat, 8 Mar 2025 23:36:06 +0100 Subject: [PATCH 4/4] Disable macOS specific FUSE API extensions Some macOS specific features require FUSE API modifications and extensions that break compatibility with the vanilla FUSE API. Setting the compile-time flag FUSE_DARWIN_ENABLE_EXTENSIONS to 0, when building a file system, disables those API extensions. By default, the macOS specific API modifications and extensions are enabled. --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index a408661c..383ca53b 100644 --- a/meson.build +++ b/meson.build @@ -34,6 +34,7 @@ cfg.set_quoted('PACKAGE_VERSION', meson.project_version()) include_dirs = [ include_directories('.') ] sshfs_sources = ['sshfs.c', 'cache.c'] if target_machine.system() == 'darwin' + add_global_arguments('-DFUSE_DARWIN_ENABLE_EXTENSIONS=0', language: 'c') cfg.set_quoted('IDMAP_DEFAULT', 'user') sshfs_sources += [ 'compat/darwin_compat.c' ] include_dirs += [ include_directories('compat') ]