diff --git a/Makefile.am b/Makefile.am index 2a48744..c637f46 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,14 +4,14 @@ bin_PROGRAMS = fuse-overlayfs ACLOCAL_AMFLAGS = -Im4 -EXTRA_DIST = m4/gnulib-cache.m4 rpm/fuse-overlayfs.spec.template autogen.sh fuse-overlayfs.1.md utils.h NEWS tests/suid-test.c +EXTRA_DIST = m4/gnulib-cache.m4 rpm/fuse-overlayfs.spec.template autogen.sh fuse-overlayfs.1.md utils.h NEWS tests/suid-test.c plugin.h plugin-manager.h CLEANFILES = fuse-overlayfs.1 fuse_overlayfs_CFLAGS = -I . -I $(abs_srcdir)/lib $(FUSE_CFLAGS) fuse_overlayfs_LDFLAGS = fuse_overlayfs_LDADD = lib/libgnu.a $(FUSE_LIBS) -fuse_overlayfs_SOURCES = main.c direct.c utils.c +fuse_overlayfs_SOURCES = main.c direct.c utils.c plugin-manager.c WD := $(shell pwd) diff --git a/configure.ac b/configure.ac index c1ae18d..aeb9efd 100644 --- a/configure.ac +++ b/configure.ac @@ -54,6 +54,7 @@ LDFLAGS=$old_LDFLAGS AC_DEFINE_UNQUOTED([HAVE_FUSE_CACHE_READDIR], $cache_readdir, [Define if libfuse is available]) +AC_SEARCH_LIBS([dlopen], [dl], [], [AC_MSG_ERROR([unable to find dlopen()])]) AC_FUNC_ERROR_AT_LINE AC_FUNC_MALLOC diff --git a/direct.c b/direct.c index 9a5de81..bc91ea3 100644 --- a/direct.c +++ b/direct.c @@ -130,13 +130,16 @@ direct_readlinkat (struct ovl_layer *l, const char *path, char *buf, size_t bufs } static int -direct_load_data_source (struct ovl_layer *l, const char *opaque) +direct_load_data_source (struct ovl_layer *l, const char *opaque, const char *path) { - l->path = realpath (opaque, NULL); + l->path = realpath (path, NULL); if (l->path == NULL) - return -1; + { + fprintf (stderr, "cannot resolve path %s\n", path); + return -1; + } - l->fd = open (l->path, O_DIRECTORY); + l->fd = open (path, O_DIRECTORY); if (l->fd < 0) { free (l->path); diff --git a/fuse-overlayfs.h b/fuse-overlayfs.h index 3280f5c..abc667a 100644 --- a/fuse-overlayfs.h +++ b/fuse-overlayfs.h @@ -18,8 +18,12 @@ #ifndef FUSE_OVERLAYFS_H # define FUSE_OVERLAYFS_H -# include # include +# include +# include +# include + +typedef struct hash_table Hash_table; struct ovl_ino { @@ -76,6 +80,7 @@ struct ovl_data char *upperdir; char *workdir; char *redirect_dir; + char *plugins; int workdir_fd; int debug; struct ovl_layer *layers; @@ -94,6 +99,8 @@ struct ovl_data /* current uid/gid*/ uid_t uid; uid_t gid; + + struct ovl_plugin_context *plugins_ctx; }; struct ovl_layer @@ -104,12 +111,14 @@ struct ovl_layer char *path; int fd; bool low; + + void *data_source_private_data; }; /* a data_source defines the methods for accessing a lower layer. */ struct data_source { - int (*load_data_source)(struct ovl_layer *l, const char *opaque); + int (*load_data_source)(struct ovl_layer *l, const char *opaque, const char *path); int (*cleanup)(struct ovl_layer *l); int (*file_exists)(struct ovl_layer *l, const char *pathname); int (*statat)(struct ovl_layer *l, const char *path, struct stat *st, int flags); diff --git a/main.c b/main.c index 5821341..55fc2ec 100644 --- a/main.c +++ b/main.c @@ -81,6 +81,8 @@ #include +#include + #ifndef TEMP_FAILURE_RETRY #define TEMP_FAILURE_RETRY(expression) \ (__extension__ \ @@ -211,6 +213,8 @@ static const struct fuse_opt ovl_opts[] = { offsetof (struct ovl_data, writeback), 1}, {"noxattrs=%d", offsetof (struct ovl_data, disable_xattrs), 1}, + {"plugins=%s", + offsetof (struct ovl_data, plugins), 0}, FUSE_OPT_END }; @@ -1540,21 +1544,80 @@ read_dirs (struct ovl_data *lo, char *path, bool low, struct ovl_layer *layers) for (it = strtok_r (buf, ":", &saveptr); it; it = strtok_r (NULL, ":", &saveptr)) { + char *name, *data; + char *it_path = it; cleanup_layer struct ovl_layer *l = NULL; l = calloc (1, sizeof (*l)); if (l == NULL) return NULL; - l->ds = &direct_access_ds; + if (it[0] != '/' || it[1] != '/') + { + /* By default use the direct access data store. */ + l->ds = &direct_access_ds; + + data = NULL; + path = it_path; + } + else + { + struct ovl_plugin *p; + char *plugin_data_sep, *plugin_sep; + + if (! low) + { + fprintf (stderr, "plugins are supported only with lower layers\n"); + return NULL; + } + + plugin_sep = strchr (it + 2, '/'); + if (! plugin_sep) + { + fprintf (stderr, "invalid separator for plugin\n"); + return NULL; + } + + *plugin_sep = '\0'; + + name = it + 2; + data = plugin_sep + 1; + + plugin_data_sep = strchr (data, '/'); + if (! plugin_data_sep) + { + fprintf (stderr, "invalid separator for plugin\n"); + return NULL; + } + + *plugin_data_sep = '\0'; + path = plugin_data_sep + 1; + + p = plugin_find (lo->plugins_ctx, name); + if (! p) + { + fprintf (stderr, "cannot find plugin %s\n", name); + return NULL; + } + + l->ds = p->load (l, data, path); + if (l->ds == NULL) + { + fprintf (stderr, "cannot load plugin %s\n", name); + return NULL; + } + } l->ovl_data = lo; l->path = NULL; l->fd = -1; - if (l->ds->load_data_source (l, it) < 0) - return NULL; + if (l->ds->load_data_source (l, data, path) < 0) + { + fprintf (stderr, "cannot load store %s at %s\n", data, path); + return NULL; + } l->low = low; if (low) @@ -4950,6 +5013,7 @@ main (int argc, char *argv[]) fprintf (stderr, "workdir=%s\n", lo.workdir ? lo.workdir : "NOT USED"); fprintf (stderr, "lowerdir=%s\n", lo.lowerdir); fprintf (stderr, "mountpoint=%s\n", lo.mountpoint); + fprintf (stderr, "plugins=%s\n", lo.plugins ? lo.plugins : ""); } lo.uid_mappings = lo.uid_str ? read_mappings (lo.uid_str) : NULL; @@ -4963,6 +5027,9 @@ main (int argc, char *argv[]) error (EXIT_FAILURE, errno, "cannot convert %s", lo.timeout_str); } + if (lo.plugins) + lo.plugins_ctx = load_plugins (lo.plugins); + layers = read_dirs (&lo, lo.lowerdir, true, NULL); if (layers == NULL) { @@ -5051,6 +5118,9 @@ err_out1: hash_free (lo.inodes); + if (lo.plugins) + plugin_free_all (lo.plugins_ctx); + free_mapping (lo.uid_mappings); free_mapping (lo.gid_mappings); diff --git a/plugin-manager.c b/plugin-manager.c new file mode 100644 index 0000000..1d1e4c6 --- /dev/null +++ b/plugin-manager.c @@ -0,0 +1,122 @@ +/* fuse-overlayfs: Overlay Filesystem in Userspace + + Copyright (C) 2019 Red Hat Inc. + + 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 . +*/ + +#include +#include +#include +#include +#include +#include + +struct ovl_plugin_context * +load_plugins (const char *plugins) +{ + char *saveptr = NULL, *it; + cleanup_free char *buf = NULL; + struct ovl_plugin_context *ctx; + + ctx = calloc (1, sizeof (*ctx)); + if (ctx == NULL) + error (EXIT_FAILURE, errno, "cannot allocate context"); + + buf = strdup (plugins); + if (buf == NULL) + error (EXIT_FAILURE, errno, "cannot allocate memory"); + + for (it = strtok_r (buf, ":", &saveptr); it; it = strtok_r (NULL, ":", &saveptr)) + plugin_load_one (ctx, it); + + return ctx; +} + +void +plugin_load_one (struct ovl_plugin_context *context, const char *path) +{ + plugin_name name; + struct ovl_plugin *p; + plugin_version version; + void *handle = dlopen (path, RTLD_NOW|RTLD_LOCAL); + if (! handle) + error (EXIT_FAILURE, 0, "cannot load plugin %s: %s", path, dlerror()); + + p = calloc (1, sizeof (*p)); + if (p == NULL) + error (EXIT_FAILURE, errno, "cannot load plugin %s", path); + p->next = context->plugins; + + version = dlsym (handle, "plugin_version"); + if (version == NULL) + error (EXIT_FAILURE, 0, "cannot find symbol `plugin_version` in plugin %s", path); + + if (version () != 1) + error (EXIT_FAILURE, 0, "invalid plugin version for %s", path); + + p->handle = handle; + name = dlsym (handle, "plugin_name"); + if (name == NULL) + error (EXIT_FAILURE, 0, "cannot find symbol `plugin_name` in plugin %s", path); + + p->name = name (); + + if (plugin_find (context, p->name)) + error (EXIT_FAILURE, 0, "plugin %s added twice", p->name); + + p->load = dlsym (handle, "plugin_load"); + if (p->load == NULL) + error (EXIT_FAILURE, 0, "cannot find symbol `plugin_load` in plugin %s", path); + + p->release = dlsym (handle, "plugin_release"); + if (p->release == NULL) + error (EXIT_FAILURE, 0, "cannot find symbol `plugin_release` in plugin %s", path); + + context->plugins = p; +} + +struct ovl_plugin * +plugin_find (struct ovl_plugin_context *context, const char *name) +{ + struct ovl_plugin *it; + + for (it = context->plugins; it; it = it->next) + { + if (strcmp (name, it->name) == 0) + return it; + } + return NULL; +} + +int +plugin_free_all (struct ovl_plugin_context *context) +{ + struct ovl_plugin *it, *next; + + it = context->plugins; + while (it) + { + next = it->next; + + dlclose (it->handle); + free (it); + + it = next; + } + + free (context); + + return 0; +} diff --git a/plugin-manager.h b/plugin-manager.h new file mode 100644 index 0000000..51722d1 --- /dev/null +++ b/plugin-manager.h @@ -0,0 +1,35 @@ +/* fuse-overlayfs: Overlay Filesystem in Userspace + + Copyright (C) 2019 Red Hat Inc. + + 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 . +*/ + +#ifndef PLUGIN_MANAGER_H +# define PLUGIN_MANAGER_H +# include + +# include + +struct ovl_plugin_context +{ + struct ovl_plugin *plugins; +}; + +void plugin_load_one (struct ovl_plugin_context *context, const char *path); +int plugin_free_all (struct ovl_plugin_context *context); +struct ovl_plugin *plugin_find (struct ovl_plugin_context *context, const char *name); +struct ovl_plugin_context *load_plugins (const char *plugins); + +#endif diff --git a/plugin.h b/plugin.h new file mode 100644 index 0000000..4ae3562 --- /dev/null +++ b/plugin.h @@ -0,0 +1,41 @@ +/* fuse-overlayfs: Overlay Filesystem in Userspace + + Copyright (C) 2019 Red Hat Inc. + + 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 . +*/ + +#ifndef PLUGIN_H +# define PLUGIN_H +# include + +# include +# include + +typedef struct data_source *(*plugin_load_data_source)(struct ovl_layer *layer, const char *opaque, const char *path); +typedef int (*plugin_release)(struct ovl_layer *layer); +typedef const char *(*plugin_name)(); +typedef int (*plugin_version)(); + +struct ovl_plugin +{ + struct ovl_plugin *next; + const char *name; + void *handle; + + plugin_load_data_source load; + plugin_release release; +}; + +#endif