Skip to content

Commit

Permalink
fuse-overlayfs: add plugin system
Browse files Browse the repository at this point in the history
support loading layers on demand.

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 <[email protected]>
  • Loading branch information
giuseppe committed Aug 21, 2019
1 parent 4dc60f0 commit edf84bb
Show file tree
Hide file tree
Showing 5 changed files with 301 additions and 22 deletions.
4 changes: 2 additions & 2 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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
EXTRA_DIST = m4/gnulib-cache.m4 rpm/fuse-overlayfs.spec.template autogen.sh fuse-overlayfs.1.md utils.h plugin.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
fuse_overlayfs_SOURCES = main.c plugin.c

WD := $(shell pwd)

Expand Down
1 change: 1 addition & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
155 changes: 135 additions & 20 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@

#include <pthread.h>

#include <plugin.h>

#ifndef TEMP_FAILURE_RETRY
#define TEMP_FAILURE_RETRY(expression) \
(__extension__ \
Expand Down Expand Up @@ -190,6 +192,9 @@ struct ovl_layer
char *path;
int fd;
bool low;

struct ovl_plugin *plugin;
void *plugin_opaque;
};

struct ovl_mapping
Expand Down Expand Up @@ -229,6 +234,7 @@ struct ovl_data
char *gid_str;
struct ovl_mapping *uid_mappings;
struct ovl_mapping *gid_mappings;
char *plugins;
char *mountpoint;
char *lowerdir;
char *context;
Expand All @@ -242,6 +248,7 @@ struct ovl_data
struct ovl_node *root;
char *timeout_str;
double timeout;

int threaded;
int fsync;
int fast_ino_check;
Expand All @@ -251,6 +258,8 @@ struct ovl_data
/* current uid/gid*/
uid_t uid;
uid_t gid;

struct ovl_plugin_context *plugins_ctx;
};

static double
Expand Down Expand Up @@ -286,6 +295,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
};

Expand Down Expand Up @@ -1319,7 +1330,12 @@ load_dir (struct ovl_data *lo, struct ovl_node *n, struct ovl_layer *layer, char
{
int fd;
cleanup_dir DIR *dp = NULL;
cleanup_close int cleanup_fd = TEMP_FAILURE_RETRY (openat (it->fd, path, O_DIRECTORY));
cleanup_close int cleanup_fd = -1;

if (it->plugin && it->plugin->fetch (it->plugin_opaque, n->parent ? n->parent->path : ".", name, LAYER_MODE_DIRECTORY) < 0)
return NULL;

cleanup_fd = TEMP_FAILURE_RETRY (openat (it->fd, path, O_DIRECTORY));
if (cleanup_fd < 0)
continue;

Expand Down Expand Up @@ -1353,6 +1369,9 @@ load_dir (struct ovl_data *lo, struct ovl_node *n, struct ovl_layer *layer, char
if ((strcmp (dent->d_name, ".") == 0) || strcmp (dent->d_name, "..") == 0)
continue;

if (it->plugin && it->plugin->fetch (it->plugin_opaque, path, dent->d_name, LAYER_MODE_METADATA) < 0)
return NULL;

child = hash_lookup (n->children, &key);
if (child)
{
Expand All @@ -1374,6 +1393,9 @@ load_dir (struct ovl_data *lo, struct ovl_node *n, struct ovl_layer *layer, char

strconcat3 (node_path, PATH_MAX, n->path, "/", dent->d_name);

if (it->plugin && it->plugin->fetch (it->plugin_opaque, path, whiteout_path, LAYER_MODE_METADATA) < 0)
return NULL;

ret = file_exists_at (fd, whiteout_path);
if (ret < 0 && errno != ENOENT)
return NULL;
Expand Down Expand Up @@ -1474,7 +1496,7 @@ cleanup_layerp (struct ovl_layer **p)
#define cleanup_layer __attribute__((cleanup (cleanup_layerp)))

static struct ovl_layer *
read_dirs (char *path, bool low, struct ovl_layer *layers)
read_dirs (struct ovl_plugin_context *plugins_ctx, const char *workdir, int workdirfd, char *path, bool low, struct ovl_layer *layers)
{
char *saveptr = NULL, *it;
struct ovl_layer *last;
Expand All @@ -1493,21 +1515,68 @@ read_dirs (char *path, bool low, struct ovl_layer *layers)

for (it = strtok_r (buf, ":", &saveptr); it; it = strtok_r (NULL, ":", &saveptr))
{
/* Used to initialize the plugin. */
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->fd = -1;

l->path = realpath (it, NULL);
if (it[0] == '/' && it[1] == '/')
{
char *plugin_data_sep, *plugin_sep;

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;

l->plugin = plugin_find (plugins_ctx, name);
if (! l->plugin)
{
fprintf (stderr, "cannot find plugin %s\n", name);
return NULL;
}
}

l->path = realpath (it_path, NULL);
if (l->path == NULL)
return NULL;

l->fd = open (l->path, O_DIRECTORY);
if (l->fd < 0)
return NULL;

if (l->plugin)
{
l->plugin_opaque = l->plugin->init (data, workdir, workdirfd, it_path, l->fd);
if (! l->plugin_opaque)
{
fprintf (stderr, "cannot initialize plugin %s\n", name);
return NULL;
}
}

l->low = low;
if (low)
{
Expand Down Expand Up @@ -1567,6 +1636,9 @@ do_lookup_file (struct ovl_data *lo, fuse_ino_t parent, const char *name)

strconcat3 (path, PATH_MAX, pnode->path, "/", name);

if (it->plugin && it->plugin->fetch (it->plugin_opaque, pnode->path, name, LAYER_MODE_METADATA) < 0)
return NULL;

ret = TEMP_FAILURE_RETRY (fstatat (it->fd, path, &st, AT_SYMLINK_NOFOLLOW));
if (ret < 0)
{
Expand All @@ -1577,6 +1649,11 @@ do_lookup_file (struct ovl_data *lo, fuse_ino_t parent, const char *name)
if (node)
continue;

strconcat3 (whpath, PATH_MAX, "/.wh.", name, NULL);

if (it->plugin && it->plugin->fetch (it->plugin_opaque, pnode->path, whpath, LAYER_MODE_METADATA) < 0)
return NULL;

strconcat3 (whpath, PATH_MAX, pnode->path, "/.wh.", name);

ret = file_exists_at (it->fd, whpath);
Expand Down Expand Up @@ -1606,7 +1683,13 @@ do_lookup_file (struct ovl_data *lo, fuse_ino_t parent, const char *name)
continue;
}

strconcat3 (whpath, PATH_MAX, "/.wh.", name, NULL);

if (it->plugin && it->plugin->fetch (it->plugin_opaque, pnode->path, whpath, LAYER_MODE_METADATA) < 0)
return NULL;

strconcat3 (whpath, PATH_MAX, pnode->path, "/.wh.", name);

ret = file_exists_at (it->fd, whpath);
if (ret < 0 && errno != ENOENT)
return NULL;
Expand Down Expand Up @@ -3057,11 +3140,15 @@ ovl_do_open (fuse_req_t req, fuse_ino_t parent, const char *name, int flags, mod
return ret;
}

if (n->layer->plugin && n->layer->plugin->fetch (n->layer->plugin_opaque, n->parent ? n->parent->path : ".", n->name, LAYER_MODE_FILE) < 0)
return -1;

/* readonly, we can use both lowerdir and upperdir. */
if (readonly)
{
if (retnode)
*retnode = n;

return TEMP_FAILURE_RETRY (openat (node_dirfd (n), n->path, flags, mode));
}
else
Expand Down Expand Up @@ -4714,6 +4801,27 @@ set_limits ()
error (EXIT_FAILURE, errno, "cannot set process rlimit");
}

static 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 (ctx, it);

return ctx;
}

int
main (int argc, char *argv[])
{
Expand All @@ -4733,6 +4841,7 @@ main (int argc, char *argv[])
.timeout = 1000000000.0,
.timeout_str = NULL,
.writeback = 1,
.plugins = NULL,
};
struct fuse_loop_config fuse_conf = {
.clone_fd = 1,
Expand Down Expand Up @@ -4806,6 +4915,7 @@ main (int argc, char *argv[])
fprintf (stderr, "workdir=%s\n", lo.workdir);
fprintf (stderr, "lowerdir=%s\n", lo.lowerdir);
fprintf (stderr, "mountpoint=%s\n", lo.mountpoint);
fprintf (stderr, "plugins=%s\n", lo.plugins);
}

lo.uid_mappings = lo.uid_str ? read_mappings (lo.uid_str) : NULL;
Expand All @@ -4818,23 +4928,6 @@ main (int argc, char *argv[])
if (errno == ERANGE)
error (EXIT_FAILURE, errno, "cannot convert %s", lo.timeout_str);
}

layers = read_dirs (lo.lowerdir, true, NULL);
if (layers == NULL)
{
error (EXIT_FAILURE, errno, "cannot read lower dirs");
}

tmp_layer = read_dirs (lo.upperdir, false, layers);
if (tmp_layer == NULL)
error (EXIT_FAILURE, errno, "cannot read upper dir");
lo.layers = layers = tmp_layer;

lo.root = load_dir (&lo, NULL, get_upper_layer (&lo), ".", "");
if (lo.root == NULL)
error (EXIT_FAILURE, errno, "cannot read upper dir");
lo.root->lookups = 2;

if (lo.workdir == NULL)
error (EXIT_FAILURE, 0, "workdir not specified");
else
Expand All @@ -4859,6 +4952,24 @@ main (int argc, char *argv[])
empty_dirfd (dfd);
}

if (lo.plugins)
lo.plugins_ctx = load_plugins (lo.plugins);

layers = read_dirs (lo.plugins_ctx, lo.workdir, lo.workdir_fd, lo.lowerdir, true, NULL);
if (layers == NULL)
error (EXIT_FAILURE, errno, "cannot read lower dirs");

tmp_layer = read_dirs (lo.plugins_ctx, lo.workdir, lo.workdir_fd, lo.upperdir, false, layers);
if (tmp_layer == NULL)
error (EXIT_FAILURE, errno, "cannot read upper dir");

lo.layers = layers = tmp_layer;

lo.root = load_dir (&lo, NULL, get_upper_layer (&lo), ".", "");
if (lo.root == NULL)
error (EXIT_FAILURE, errno, "cannot read upper dir");
lo.root->lookups = 2;

umask (0);
disable_locking = !lo.threaded;

Expand Down Expand Up @@ -4900,6 +5011,10 @@ main (int argc, char *argv[])
free_mapping (lo.uid_mappings);
free_mapping (lo.gid_mappings);

for (tmp_layer = lo.layers; tmp_layer; tmp_layer = tmp_layer->next)
if (tmp_layer->plugin)
tmp_layer->plugin->release (tmp_layer->plugin_opaque);

close (lo.workdir_fd);

fuse_opt_free_args (&args);
Expand Down
Loading

0 comments on commit edf84bb

Please sign in to comment.