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 <gscrivan@redhat.com>
  • Loading branch information
giuseppe committed May 27, 2019
1 parent 2cbd1c4 commit f88599e
Show file tree
Hide file tree
Showing 5 changed files with 293 additions and 23 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
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ AC_TYPE_UINT8_T
AC_CHECK_TYPES([ptrdiff_t])

PKG_CHECK_MODULES([FUSE], [fuse3 >= 3.2.1], [AC_DEFINE([HAVE_FUSE], 1, [Define if libfuse is available])], [AC_MSG_ERROR([*** libfuse not found])]])

AC_SEARCH_LIBS([dlopen], [dl], [], [AC_MSG_ERROR([unable to find dlopen()])])
AC_FUNC_ERROR_AT_LINE
AC_FUNC_MALLOC
AC_CHECK_FUNCS([open_by_handle_at error memset strdup])
Expand Down
149 changes: 129 additions & 20 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@

#include <utils.h>

#include <plugin.h>

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

struct ovl_plugin *plugin;
void *plugin_opaque;
};

struct ovl_mapping
Expand Down Expand Up @@ -187,6 +192,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 @@ -197,6 +203,7 @@ struct ovl_data
struct ovl_layer *layers;

struct ovl_node *root;
struct ovl_plugin_context *plugins_ctx;
};

static const struct fuse_opt ovl_opts[] = {
Expand All @@ -214,6 +221,8 @@ static const struct fuse_opt ovl_opts[] = {
offsetof (struct ovl_data, uid_str), 0},
{"gidmapping=%s",
offsetof (struct ovl_data, gid_str), 0},
{"plugins=%s",
offsetof (struct ovl_data, plugins), 0},
FUSE_OPT_END
};

Expand Down Expand Up @@ -1078,7 +1087,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 @@ -1113,6 +1127,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;

if (TEMP_FAILURE_RETRY (fstatat (fd, dent->d_name, &st, AT_SYMLINK_NOFOLLOW)) < 0)
return NULL;

Expand Down Expand Up @@ -1141,6 +1158,9 @@ load_dir (struct ovl_data *lo, struct ovl_node *n, struct ovl_layer *layer, char
if (ret < 0)
return NULL;

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

ret = TEMP_FAILURE_RETRY (fstatat (fd, whiteout_path, &tmp_st, AT_SYMLINK_NOFOLLOW));
if (ret < 0 && errno != ENOENT)
return NULL;
Expand Down Expand Up @@ -1215,7 +1235,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 @@ -1234,21 +1254,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 *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 (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, 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 @@ -1310,6 +1377,9 @@ do_lookup_file (struct ovl_data *lo, fuse_ino_t parent, const char *name)
if (ret < 0)
return NULL;

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 @@ -1324,6 +1394,9 @@ do_lookup_file (struct ovl_data *lo, fuse_ino_t parent, const char *name)
if (ret < 0)
return NULL;

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, whpath, &tmp_st, AT_SYMLINK_NOFOLLOW));
if (ret < 0 && errno != ENOENT && errno != ENOTDIR)
return NULL;
Expand Down Expand Up @@ -1359,6 +1432,9 @@ do_lookup_file (struct ovl_data *lo, fuse_ino_t parent, const char *name)
return NULL;
}

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, whpath, &tmp_st, AT_SYMLINK_NOFOLLOW));
if (ret < 0 && errno != ENOENT)
return NULL;
Expand Down Expand Up @@ -2578,7 +2654,11 @@ ovl_do_open (fuse_req_t req, fuse_ino_t parent, const char *name, int flags, mod

/* readonly, we can use both lowerdir and upperdir. */
if (readonly)
return TEMP_FAILURE_RETRY (openat (node_dirfd (n), n->path, flags, mode));
{
if (n->layer->plugin && n->layer->plugin->fetch (n->layer->plugin_opaque, n->parent ? n->parent->path : ".", n->name, LAYER_MODE_METADATA) < 0)
return -1;
return TEMP_FAILURE_RETRY (openat (node_dirfd (n), n->path, flags, mode));
}
else
{
n = get_node_up (lo, n);
Expand Down Expand Up @@ -3956,6 +4036,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 @@ -3971,6 +4072,7 @@ main (int argc, char *argv[])
.lowerdir = NULL,
.redirect_dir = NULL,
.mountpoint = NULL,
.plugins = NULL,
};
int ret = -1;
cleanup_layer struct ovl_layer *layers = NULL;
Expand Down Expand Up @@ -4034,27 +4136,12 @@ 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;
lo.gid_mappings = lo.gid_str ? read_mappings (lo.gid_str) : NULL;

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 @@ -4075,6 +4162,24 @@ main (int argc, char *argv[])
error (EXIT_FAILURE, errno, "cannot open workdir");
}

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);

se = fuse_session_new (&args, &ovl_oper, sizeof (ovl_oper), &lo);
Expand Down Expand Up @@ -4110,6 +4215,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 f88599e

Please sign in to comment.