mirror of
https://github.com/containers/fuse-overlayfs.git
synced 2025-09-08 06:42:27 -04:00
fuse-overlayfs: add plugin system
Add a simple plugin mechanism that will help to expand fuse-overlayfs functionalities, in particular it allows to load data from a layer on demand. A plugin is loaded into fuse-overlayfs using the option: -o plugins=path/to/plugin.so:path/to/another/plugin.so A layer can use a plugin with the syntax: -o lowerdir=//plugin-name/DATA-FOR-THE-PLUGIN/path Each time a file/directory is looked up, if a plugin is registered for a layer, the plugin is first notified about the request. After the callback is invoked, fuse-overlayfs still expects the data to be accessible at the specified directory. Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
This commit is contained in:
parent
c2c2ac5b82
commit
b28a23a39d
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
11
direct.c
11
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);
|
||||
|
@ -18,8 +18,12 @@
|
||||
#ifndef FUSE_OVERLAYFS_H
|
||||
# define FUSE_OVERLAYFS_H
|
||||
|
||||
# include <hash.h>
|
||||
# include <sys/stat.h>
|
||||
# include <plugin-manager.h>
|
||||
# include <stdbool.h>
|
||||
# include <sys/types.h>
|
||||
|
||||
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);
|
||||
|
76
main.c
76
main.c
@ -81,6 +81,8 @@
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <plugin.h>
|
||||
|
||||
#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 : "<none>");
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
122
plugin-manager.c
Normal file
122
plugin-manager.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <plugin.h>
|
||||
#include <stdlib.h>
|
||||
#include <error.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
35
plugin-manager.h
Normal file
35
plugin-manager.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PLUGIN_MANAGER_H
|
||||
# define PLUGIN_MANAGER_H
|
||||
# include <config.h>
|
||||
|
||||
# include <dlfcn.h>
|
||||
|
||||
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
|
41
plugin.h
Normal file
41
plugin.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PLUGIN_H
|
||||
# define PLUGIN_H
|
||||
# include <config.h>
|
||||
|
||||
# include <fuse-overlayfs.h>
|
||||
# include <utils.h>
|
||||
|
||||
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
|
Loading…
x
Reference in New Issue
Block a user