aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/fuse_opt.c100
-rw-r--r--lib/mount.c347
2 files changed, 414 insertions, 33 deletions
diff --git a/lib/fuse_opt.c b/lib/fuse_opt.c
index 3bacf74..a2999f3 100644
--- a/lib/fuse_opt.c
+++ b/lib/fuse_opt.c
@@ -19,6 +19,7 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
+#include <stdbool.h>
struct fuse_opt_context {
void *data;
@@ -292,46 +293,81 @@ static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso)
return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso);
}
-static int process_real_option_group(struct fuse_opt_context *ctx, char *opts)
+/* ---- Neue robuste -o-Aufspaltung mit Escape/Quotes -------------------- */
+
+static void unescape_commas(char *s)
{
- char *s = opts;
- char *d = s;
- int end = 0;
-
- while (!end) {
- if (*s == '\0')
- end = 1;
- if (*s == ',' || end) {
- int res;
+ char *w = s, *r = s;
+ while (*r) {
+ if (r[0] == '\\' && r[1] == ',') { *w++ = ','; r += 2; continue; }
+ if (r[0] == '\\' && r[1] == '\\'){ *w++ = '\\'; r += 2; continue; }
+ *w++ = *r++;
+ }
+ *w = '\0';
+}
- *d = '\0';
- res = process_gopt(ctx, opts, 1);
- if (res == -1)
- return -1;
- d = opts;
- } else {
- if (s[0] == '\\' && s[1] != '\0') {
- s++;
- if (s[0] >= '0' && s[0] <= '3' &&
- s[1] >= '0' && s[1] <= '7' &&
- s[2] >= '0' && s[2] <= '7') {
- *d++ = (s[0] - '0') * 0100 +
- (s[1] - '0') * 0010 +
- (s[2] - '0');
- s += 2;
- } else {
- *d++ = *s;
- }
- } else {
- *d++ = *s;
+static void foreach_o_subopt(const char *oarg,
+ void (*cb)(const char *opt, void *user),
+ void *user)
+{
+ const char *p = oarg, *start = oarg;
+ bool esc = false, in_sq = false, in_dq = false;
+
+ for (; *p; ++p) {
+ if (esc) { esc = false; continue; }
+ if (*p == '\\') { esc = true; continue; }
+ if (*p == '\'' && !in_dq) { in_sq = !in_sq; continue; }
+ if (*p == '\"' && !in_sq) { in_dq = !in_dq; continue; }
+
+ /* Nur ein nicht gequotetes und nicht ge-escaptes Komma trennt */
+ if (*p == ',' && !in_sq && !in_dq) {
+ size_t len = (size_t)(p - start);
+ if (len) {
+ char *tmp = (char*)malloc(len + 1);
+ memcpy(tmp, start, len);
+ tmp[len] = '\0';
+ unescape_commas(tmp);
+ cb(tmp, user);
+ free(tmp);
}
+ start = p + 1;
}
- s++;
}
- return 0;
+ if (p > start) {
+ size_t len = (size_t)(p - start);
+ char *tmp = (char*)malloc(len + 1);
+ memcpy(tmp, start, len);
+ tmp[len] = '\0';
+ unescape_commas(tmp);
+ cb(tmp, user);
+ free(tmp);
+ }
}
+struct oproc_ctx {
+ struct fuse_opt_context *ctx;
+ int rc;
+};
+
+static void process_o_subopt_cb(const char *opt, void *user)
+{
+ struct oproc_ctx *st = (struct oproc_ctx*)user;
+ if (st->rc != 0) return; /* vorheriger Fehler? */
+ if (opt[0] == '\0') return; /* leere Teil-Option ignorieren */
+ st->rc = process_gopt(st->ctx, opt, 1);
+}
+
+static int process_real_option_group(struct fuse_opt_context *ctx, char *opts)
+{
+ struct oproc_ctx st = { ctx, 0 };
+ /* Robustes Splitting: beachtet \, sowie '...' und "..." */
+ foreach_o_subopt(opts, process_o_subopt_cb, &st);
+ return st.rc;
+}
+
+/* ---------------------------------------------------------------------- */
+
static int process_option_group(struct fuse_opt_context *ctx, const char *opts)
{
int res;
diff --git a/lib/mount.c b/lib/mount.c
index 6ed4444..0a31f81 100644
--- a/lib/mount.c
+++ b/lib/mount.c
@@ -29,6 +29,7 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
+#include <ctype.h>
#include "fuse_mount_compat.h"
@@ -54,6 +55,279 @@
#define MS_DIRSYNC 128
#endif
+/* === new-mount-API helpers (Linux only) ================================= */
+#if defined(__linux__)
+#include <linux/mount.h>
+#include <sys/syscall.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Fallback-Defines, falls ältere Kernel-Header verwendet werden */
+#ifndef FSCONFIG_SET_FLAG
+# define FSCONFIG_SET_FLAG 0
+# define FSCONFIG_SET_STRING 1
+# define FSCONFIG_SET_BINARY 2
+# define FSCONFIG_SET_PATH 3
+# define FSCONFIG_SET_PATH_EMPTY 4
+# define FSCONFIG_SET_FD 5
+# define FSCONFIG_CMD_CREATE 6
+# define FSCONFIG_CMD_RECONFIGURE 7
+#endif
+#ifndef MOVE_MOUNT_F_EMPTY_PATH
+# define MOVE_MOUNT_F_EMPTY_PATH 0x00000004
+#endif
+
+/* Syscall-Nummern fallbacken, wenn libc sie nicht kennt */
+#ifndef SYS_fsopen
+# if defined(__x86_64__)
+# define SYS_fsopen 430
+# elif defined(__aarch64__)
+# define SYS_fsopen 434
+# endif
+#endif
+#ifndef SYS_fsconfig
+# if defined(__x86_64__)
+# define SYS_fsconfig 431
+# elif defined(__aarch64__)
+# define SYS_fsconfig 435
+# endif
+#endif
+#ifndef SYS_fsmount
+# if defined(__x86_64__)
+# define SYS_fsmount 432
+# elif defined(__aarch64__)
+# define SYS_fsmount 436
+# endif
+#endif
+#ifndef SYS_move_mount
+# if defined(__x86_64__)
+# define SYS_move_mount 429
+# elif defined(__aarch64__)
+# define SYS_move_mount 433
+# endif
+#endif
+
+static inline int x_fsopen(const char *fsname, unsigned int flags) {
+ errno = 0; return (int)syscall(SYS_fsopen, fsname, flags);
+}
+static inline int x_fsconfig(int fd, unsigned int cmd,
+ const char *key, const void *val, int aux) {
+ errno = 0; return (int)syscall(SYS_fsconfig, fd, cmd, key, val, aux);
+}
+static inline int x_fsmount(int fsfd, unsigned int flags, unsigned int ms_flags) {
+ errno = 0; return (int)syscall(SYS_fsmount, fsfd, flags, ms_flags);
+}
+static inline int x_move_mount(int from_dfd, const char *from_pathname,
+ int to_dfd, const char *to_pathname,
+ unsigned int flags) {
+ errno = 0; return (int)syscall(SYS_move_mount, from_dfd, from_pathname, to_dfd, to_pathname, flags);
+}
+
+/* robustes Splitten der -o Kette:
+ - Respektiert Backslashes und '"/" Quotes
+ - Ruft cb(key, value|NULL) pro Teiloption auf (value==NULL => reines Flag) */
+struct opt_cb {
+ int (*cb)(const char *key, const char *val, void *userdata);
+ void *userdata;
+};
+static void for_each_o_subopt(const char *oarg, struct opt_cb *h) {
+ const char *p = oarg, *start = oarg;
+ bool esc = false, in_sq = false, in_dq = false;
+
+ for (; *p; ++p) {
+ if (esc) { esc = false; continue; }
+ if (*p == '\\') { esc = true; continue; }
+ if (*p == '\'' && !in_dq) { in_sq = !in_sq; continue; }
+ if (*p == '\"' && !in_sq) { in_dq = !in_dq; continue; }
+ if (*p == ',' && !in_sq && !in_dq) {
+ size_t len = (size_t)(p - start);
+ if (len) {
+ char *tmp = (char*)malloc(len + 1);
+ memcpy(tmp, start, len);
+ tmp[len] = '\0';
+ char *eq = strchr(tmp, '=');
+ if (eq) { *eq = '\0'; h->cb(tmp, eq + 1, h->userdata); }
+ else { h->cb(tmp, NULL, h->userdata); }
+ free(tmp);
+ }
+ start = p + 1;
+ }
+ }
+ if (p > start) {
+ size_t len = (size_t)(p - start);
+ char *tmp = (char*)malloc(len + 1);
+ memcpy(tmp, start, len);
+ tmp[len] = '\0';
+ char *eq = strchr(tmp, '=');
+ if (eq) { *eq = '\0'; h->cb(tmp, eq + 1, h->userdata); }
+ else { h->cb(tmp, NULL, h->userdata); }
+ free(tmp);
+ }
+}
+
+/* Sammelstruktur für Pflicht-Keys */
+struct fuse_kv_acc {
+ int fd; bool have_fd;
+};
+static int collect_core_opts_cb(const char *key, const char *val, void *userdata) {
+ struct fuse_kv_acc *acc = (struct fuse_kv_acc*)userdata;
+ if (!key) return 0;
+ if (strcmp(key, "fd") == 0 && val) {
+ acc->fd = atoi(val);
+ acc->have_fd = true;
+ }
+ return 0;
+}
+
+static const char *extract_subtype(const char *type) {
+ if (!type) return NULL;
+ const char *dot = strchr(type, '.');
+ return dot ? dot + 1 : NULL;
+}
+
+/* C-kompatibler Callback: setzt einen Key/Value mit fsconfig.
+ Überspringt "fd", weil der separat als FSCONFIG_SET_FD gesetzt wird. */
+static int fsconfig_apply_cb(const char *key, const char *val, void *userdata)
+{
+ int fsfd_local = *(int *)userdata;
+
+ if (!key) return 0;
+ if (strcmp(key, "fd") == 0) return 0; /* schon separat gesetzt */
+
+ int rc;
+ if (val && *val) {
+ rc = x_fsconfig(fsfd_local, FSCONFIG_SET_STRING, key, val, 0);
+ } else {
+ rc = x_fsconfig(fsfd_local, FSCONFIG_SET_FLAG, key, NULL, 0);
+ }
+ return (rc < 0) ? -1 : 0;
+}
+
+/* Übergibt ALLE Optionen einzeln (inkl. context=… mit Kommata) via fsconfig */
+static int set_all_opts_via_fsconfig(int fsfd, const char *optstr)
+{
+ if (!optstr || !*optstr)
+ return 0;
+
+ /* eigener robuster Parser (wie oben), der Kommas in Quotes/mit Backslash respektiert */
+ const char *s = optstr;
+ const char *p = s, *start = s;
+ bool esc = false, in_sq = false, in_dq = false;
+
+ for (; *p; ++p) {
+ if (esc) { esc = false; continue; }
+ if (*p == '\\') { esc = true; continue; }
+ if (*p == '\'' && !in_dq) { in_sq = !in_sq; continue; }
+ if (*p == '\"' && !in_sq) { in_dq = !in_dq; continue; }
+
+ if (*p == ',' && !in_sq && !in_dq) {
+ size_t len = (size_t)(p - start);
+ if (len) {
+ char *tmp = (char*)malloc(len + 1);
+ if (!tmp) { errno = ENOMEM; return -1; }
+ memcpy(tmp, start, len);
+ tmp[len] = '\0';
+
+ char *eq = strchr(tmp, '=');
+ int rc;
+ if (eq) {
+ *eq = '\0';
+ rc = fsconfig_apply_cb(tmp, eq + 1, &fsfd);
+ } else {
+ rc = fsconfig_apply_cb(tmp, NULL, &fsfd);
+ }
+ free(tmp);
+ if (rc < 0) return -1;
+ }
+ start = p + 1;
+ }
+ }
+
+ /* letztes Segment */
+ if (p > start) {
+ size_t len = (size_t)(p - start);
+ char *tmp = (char*)malloc(len + 1);
+ if (!tmp) { errno = ENOMEM; return -1; }
+ memcpy(tmp, start, len);
+ tmp[len] = '\0';
+
+ char *eq = strchr(tmp, '=');
+ int rc;
+ if (eq) {
+ *eq = '\0';
+ rc = fsconfig_apply_cb(tmp, eq + 1, &fsfd);
+ } else {
+ rc = fsconfig_apply_cb(tmp, NULL, &fsfd);
+ }
+ free(tmp);
+ if (rc < 0) return -1;
+ }
+
+ return 0;
+}
+
+/* Hauptfunktion: versucht neue Mount-API; bei Erfolg 0, sonst -1 für Fallback */
+static int fuse_mount_with_fsopen_try(const char *type,
+ const char *mountpoint,
+ const char *optstr,
+ unsigned long ms_flags)
+{
+ int fsfd = x_fsopen(type && strncmp(type, "fuseblk", 7)==0 ? "fuseblk" : "fuse", 0);
+ if (fsfd < 0) {
+ if (errno == ENOSYS) return -1; /* Kernel kann es nicht */
+ return -1;
+ }
+
+ /* Pflicht: fd aus optstr ziehen */
+ struct fuse_kv_acc acc = { .fd = -1, .have_fd = false };
+ if (optstr && *optstr) {
+ struct opt_cb c = { .cb = collect_core_opts_cb, .userdata = &acc };
+ for_each_o_subopt(optstr, &c);
+ }
+ if (!acc.have_fd) { close(fsfd); errno = EINVAL; return -1; }
+
+ /* subtype setzen (falls "fuse.xxx") */
+ const char *sub = extract_subtype(type);
+ if (sub && *sub) {
+ if (x_fsconfig(fsfd, FSCONFIG_SET_STRING, "subtype", sub, 0) < 0) {
+ int saved = errno; close(fsfd); errno = saved; return -1;
+ }
+ }
+
+ /* fd reinreichen */
+ if (x_fsconfig(fsfd, FSCONFIG_SET_FD, "fd", NULL, acc.fd) < 0) {
+ int saved = errno; close(fsfd); errno = saved; return -1;
+ }
+
+ /* nun alle restlichen Optionen einzeln setzen (inkl. context=… mit Kommata) */
+ if (set_all_opts_via_fsconfig(fsfd, optstr) < 0) {
+ int saved = errno; close(fsfd); errno = saved; return -1;
+ }
+
+ if (x_fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) < 0) {
+ int saved = errno; close(fsfd); errno = saved; return -1;
+ }
+
+ int mfd = x_fsmount(fsfd, 0, (unsigned int)ms_flags);
+ if (mfd < 0) {
+ int saved = errno; close(fsfd); errno = saved; return -1;
+ }
+
+ if (x_move_mount(mfd, "", AT_FDCWD, mountpoint, MOVE_MOUNT_F_EMPTY_PATH) < 0) {
+ int saved = errno; close(mfd); close(fsfd); errno = saved; return -1;
+ }
+
+ close(mfd);
+ close(fsfd);
+ return 0;
+}
+#endif /* __linux__ */
+/* === end new-mount-API helpers ========================================= */
+
enum {
KEY_KERN_FLAG,
KEY_KERN_OPT,
@@ -502,6 +776,59 @@ static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
#define O_CLOEXEC 0
#endif
+static char *escape_context_commas_precise(const char *opts_in)
+{
+ if (!opts_in) return NULL;
+ const char *ctx = strstr(opts_in, "context=");
+ if (!ctx) return NULL;
+
+ const char *val = ctx + 8; /* hinter "context=" */
+ const char *end = opts_in + strlen(opts_in);
+
+ /* Tokens, die nach context= vorkommen können */
+ static const char *tokens[] = {
+ ",fd=", ",rootmode=", ",user_id=", ",group_id=",
+ ",fsname=", ",subtype=", ",rw", ",ro",
+ ",allow_other", ",default_permissions", ",auto_unmount",
+ ",kernel_cache", ",attr_timeout=", ",entry_timeout=", ",negative_timeout=",
+ NULL
+ };
+
+ /* finde frühestes Auftreten eines Tokens hinter val */
+ const char *limit = end;
+ for (int i = 0; tokens[i]; i++) {
+ const char *p = strstr(val, tokens[i]);
+ if (p && p < limit) limit = p;
+ }
+
+ /* Falls nichts gefunden: der Rest der Zeichenkette ist der context-Wert */
+ size_t prefix_len = (size_t)(val - opts_in);
+ size_t ctx_len = (size_t)(limit - val);
+ size_t suffix_len = (size_t)(end - limit);
+
+ /* worst case: jedes ',' → "\054" (×4) innerhalb des ctx-Bereichs */
+ size_t cap = prefix_len + ctx_len * 4 + suffix_len + 1;
+ char *out = malloc(cap);
+ if (!out) return NULL;
+
+ /* kopiere prefix (inkl. "context=") */
+ memcpy(out, opts_in, prefix_len);
+ size_t o = prefix_len;
+
+ /* escapen innerhalb des context-Werts */
+ for (size_t i = 0; i < ctx_len; i++) {
+ char c = val[i];
+ if (c == ',') { memcpy(out + o, "\\054", 4); o += 4; }
+ else { out[o++] = c; }
+ }
+
+ /* suffix roh übernehmen */
+ memcpy(out + o, limit, suffix_len);
+ o += suffix_len;
+ out[o] = '\0';
+ return out;
+}
+
static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
const char *mnt_opts)
{
@@ -562,7 +889,25 @@ static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
strcpy(source,
mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname));
- res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
+#if defined(__linux__)
+ /* Erst neue Mount-API probieren: jede Option (inkl. context=… mit Kommata)
+ wird als separates K/V gesetzt – keine Ambiguität mehr. */
+ if (fuse_mount_with_fsopen_try(type, mnt, mo->kernel_opts, mo->flags) == 0) {
+ res = 0;
+ } else
+#endif
+ {
+ if (mo->kernel_opts) {
+ char *escaped = escape_context_commas_precise(mo->kernel_opts);
+ if (escaped) {
+ free(mo->kernel_opts);
+ mo->kernel_opts = escaped;
+ }
+ }
+ /* Fallback: klassisches mount(2) mit kommaseparierter Optionskette */
+ res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
+ }
+
if (res == -1 && errno == ENODEV && mo->subtype) {
/* Probably missing subtype support */
strcpy(type, mo->blkdev ? "fuseblk" : "fuse");