[Libguestfs] [PATCH nbdkit INCOMPLETE 6/6] filters: Implement chain of filters in front of ordinary plugin methods.

Richard W.M. Jones rjones at redhat.com
Mon Jan 15 09:00:58 UTC 2018


Here's a fixed 6/6 patch.  However I think this approach is wrong so
I'm going to try to come up with an alternative way.

Rich.

-- 
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
virt-builder quickly builds VMs from scratch
http://libguestfs.org/virt-builder.1.html
-------------- next part --------------
>From 7afbdf9da4608abcf0993fc1be95554639a6a415 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones at redhat.com>
Date: Sat, 13 Jan 2018 22:24:49 +0000
Subject: [PATCH 6/6] filters: Implement chain of filters in front of ordinary
 plugin methods.

---
 src/connections.c |  33 +++++-
 src/internal.h    |   4 +-
 src/plugins.c     | 315 +++++++++++++++++++++++++++++++++++++++++++++++++-----
 3 files changed, 321 insertions(+), 31 deletions(-)

diff --git a/src/connections.c b/src/connections.c
index 111a810..afa6107 100644
--- a/src/connections.c
+++ b/src/connections.c
@@ -69,10 +69,13 @@ struct connection {
   pthread_mutex_t write_lock;
   pthread_mutex_t status_lock;
   int status; /* 1 for more I/O with client, 0 for shutdown, -1 on error */
-  void *handle;
   void *crypto_session;
   int nworkers;
 
+  void *handle;
+  void **filter_handles;
+  size_t nr_filter_handles;
+
   uint64_t exportsize;
   int readonly;
   int can_flush;
@@ -112,6 +115,34 @@ connection_get_handle (struct connection *conn)
   return conn->handle;
 }
 
+int
+connection_set_filter_handle (struct connection *conn, size_t i, void *handle)
+{
+  if (i < conn->nr_filter_handles)
+    conn->filter_handles[i] = handle;
+  else {
+    conn->nr_filter_handles = i+1;
+    conn->filter_handles = realloc (conn->filter_handles,
+                                    conn->nr_filter_handles * sizeof (void *));
+    if (conn->filter_handles == NULL) {
+      perror ("realloc");
+      conn->nr_filter_handles = 0;
+      return -1;
+    }
+    conn->filter_handles[i] = handle;
+  }
+  return 0;
+}
+
+void *
+connection_get_filter_handle (struct connection *conn, size_t i)
+{
+  if (i < conn->nr_filter_handles)
+    return conn->filter_handles[i];
+  else
+    return NULL;
+}
+
 pthread_mutex_t *
 connection_get_request_lock (struct connection *conn)
 {
diff --git a/src/internal.h b/src/internal.h
index 86cb0aa..ac33e50 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -126,6 +126,8 @@ typedef void (*connection_close_function) (struct connection *);
 extern int handle_single_connection (int sockin, int sockout);
 extern void connection_set_handle (struct connection *conn, void *handle);
 extern void *connection_get_handle (struct connection *conn);
+extern int connection_set_filter_handle (struct connection *conn, size_t i, void *handle);
+extern void *connection_get_filter_handle (struct connection *conn, size_t i);
 extern pthread_mutex_t *connection_get_request_lock (struct connection *conn);
 extern void connection_set_crypto_session (struct connection *conn, void *session);
 extern void *connection_get_crypto_session (struct connection *conn);
@@ -166,7 +168,7 @@ extern int plugin_can_flush (struct connection *conn);
 extern int plugin_is_rotational (struct connection *conn);
 extern int plugin_can_trim (struct connection *conn);
 extern int plugin_pread (struct connection *conn, void *buf, uint32_t count, uint64_t offset);
-extern int plugin_pwrite (struct connection *conn, void *buf, uint32_t count, uint64_t offset);
+extern int plugin_pwrite (struct connection *conn, const void *buf, uint32_t count, uint64_t offset);
 extern int plugin_flush (struct connection *conn);
 extern int plugin_trim (struct connection *conn, uint32_t count, uint64_t offset);
 extern int plugin_zero (struct connection *conn, uint32_t count, uint64_t offset, int may_trim);
diff --git a/src/plugins.c b/src/plugins.c
index 3600293..490ff3d 100644
--- a/src/plugins.c
+++ b/src/plugins.c
@@ -53,6 +53,7 @@
  */
 struct filter {
   struct filter *next;
+  size_t i;                   /* index of the filter, starting at 0 */
   char *filename;
   void *dl;
   struct nbdkit_filter filter;
@@ -87,10 +88,14 @@ filter_register (const char *_filename,
     perror ("malloc");
     exit (EXIT_FAILURE);
   }
-  if (last_filter)
+  if (last_filter) {
     last_filter->next = f;
-  else
+    f->i = last_filter->i + 1;
+  }
+  else {
     filters = f;
+    f->i = 0;
+  }
   last_filter = f;
 
   f->next = NULL;
@@ -406,8 +411,37 @@ plugin_dump_fields (void)
     plugin.dump_plugin ();
 }
 
-void
-plugin_config (const char *key, const char *value)
+/* For UNPACK trick, see: https://stackoverflow.com/a/35999754 */
+#define UNPACK(...) __VA_ARGS__
+#define CALL_FILTERS_1(method, comma_args_decl, comma_args, args, args_comma) \
+  static int                                                            \
+  filter_##method (void *datav UNPACK comma_args_decl)                  \
+  {                                                                     \
+    struct filter *f = datav;                                           \
+                                                                        \
+    if (f == NULL)                                                      \
+      return final_##method (UNPACK args);                              \
+    else {                                                              \
+      if (f->filter.method) {                                           \
+        /* XXX Print args. */                                           \
+        debug ("%s: %s", f->filename, #method);                         \
+        if (f->filter.method (UNPACK args_comma                         \
+                              filter_##method, f->next) == -1)          \
+          return -1;                                                    \
+        return 0;                                                       \
+      }                                                                 \
+      else                                                              \
+        return filter_##method (f->next UNPACK comma_args);             \
+    }                                                                   \
+  }
+#define CALL_FILTERS(method, args_decl, args) \
+  CALL_FILTERS_1 (method, (, UNPACK args_decl), \
+                  (, UNPACK args), args, (UNPACK args,))
+#define CALL_FILTERS_NO_ARGS(method) \
+  CALL_FILTERS_1 (method, (), (), (), ())
+
+static int
+final_config (const char *key, const char *value)
 {
   assert (dl);
 
@@ -422,21 +456,37 @@ plugin_config (const char *key, const char *value)
     exit (EXIT_FAILURE);
   }
 
-  if (plugin.config (key, value) == -1)
-    exit (EXIT_FAILURE);
+  return plugin.config (key, value);
 }
 
+CALL_FILTERS (config, (const char *key, const char *value), (key, value))
+
 void
-plugin_config_complete (void)
+plugin_config (const char *key, const char *value)
+{
+  if (filter_config (filters, key, value) == -1)
+    exit (EXIT_FAILURE);
+}
+
+static int
+final_config_complete (void)
 {
   assert (dl);
 
   debug ("%s: config_complete", filename);
 
   if (!plugin.config_complete)
-    return;
+    return 0;
+
+  return plugin.config_complete ();
+}
+
+CALL_FILTERS_NO_ARGS (config_complete)
 
-  if (plugin.config_complete () == -1)
+void
+plugin_config_complete (void)
+{
+  if (filter_config_complete (filters) == -1)
     exit (EXIT_FAILURE);
 }
 
@@ -509,8 +559,48 @@ plugin_errno_is_preserved (void)
   return plugin.errno_is_preserved;
 }
 
-int
-plugin_open (struct connection *conn, int readonly)
+struct data {
+  struct connection *conn;
+  struct filter *f;
+};
+
+#undef CALL_FILTERS_1
+#undef CALL_FILTERS
+#undef CALL_FILTERS_NO_ARGS
+
+#define CALL_FILTERS_1(method, ret_type, comma_args_decl, comma_args, args, args_comma) \
+  static ret_type                                                       \
+  filter_##method (void *datav UNPACK comma_args_decl)                  \
+  {                                                                     \
+    struct data *data = datav;                                          \
+    struct connection *conn = data->conn;                               \
+    struct filter *f = data->f;                                         \
+    void *handle;                                                       \
+                                                                        \
+    if (f == NULL)                                                      \
+      return final_##method (conn UNPACK comma_args);                   \
+    else {                                                              \
+      data->f = data->f->next;                                          \
+                                                                        \
+      if (f->filter.method) {                                           \
+        /* XXX Print args. */                                           \
+        debug ("%s: %s", f->filename, #method);                         \
+        handle = connection_get_filter_handle (conn, f->i);             \
+        return f->filter.method (handle, UNPACK args_comma              \
+                                 filter_##method, data);                \
+      }                                                                 \
+      else                                                              \
+        return filter_##method (data UNPACK comma_args);                \
+    }                                                                   \
+  }
+#define CALL_FILTERS(method, ret_type, args_decl, args) \
+  CALL_FILTERS_1 (method, ret_type, (, UNPACK args_decl), \
+                  (, UNPACK args), args, (UNPACK args,))
+#define CALL_FILTERS_NO_ARGS(method, ret_type) \
+  CALL_FILTERS_1 (method, ret_type, (), (), (), ())
+
+static int
+final_open (struct connection *conn, int readonly)
 {
   void *handle;
 
@@ -528,8 +618,43 @@ plugin_open (struct connection *conn, int readonly)
   return 0;
 }
 
-void
-plugin_close (struct connection *conn)
+static int
+filter_open (void *datav, int readonly)
+{
+  struct data *data = datav;
+  struct connection *conn = data->conn;
+  struct filter *f = data->f;
+  void *handle;
+
+  if (f == NULL)
+    return final_open (conn, readonly);
+  else {
+    data->f = data->f->next;
+
+    if (f->filter.open) {
+      debug ("%s: open readonly=%d", f->filename, readonly);
+      handle = f->filter.open (readonly, filter_open, data);
+      if (!handle)
+        return -1;
+
+      if (connection_set_filter_handle (conn, f->i, handle) == -1)
+        return -1;
+      return 0;
+    }
+    else
+      return filter_open (data, readonly);
+  }
+}
+
+int
+plugin_open (struct connection *conn, int readonly)
+{
+  struct data data = { .conn = conn, .f = filters };
+  return filter_open (&data, readonly);
+}
+
+static void
+final_close (struct connection *conn)
 {
   assert (dl);
   assert (connection_get_handle (conn));
@@ -542,8 +667,39 @@ plugin_close (struct connection *conn)
   connection_set_handle (conn, NULL);
 }
 
-int64_t
-plugin_get_size (struct connection *conn)
+static void
+filter_close (void *datav)
+{
+  struct data *data = datav;
+  struct connection *conn = data->conn;
+  struct filter *f = data->f;
+  void *handle;
+
+  if (f == NULL)
+    final_close (conn);
+  else {
+    data->f = data->f->next;
+
+    if (f->filter.close) {
+      debug ("%s: close", data->f->filename);
+      handle = connection_get_filter_handle (conn, f->i);
+      f->filter.close (handle, filter_close, data);
+    }
+    else
+      filter_close (data);
+    connection_set_filter_handle (conn, f->i, NULL);
+  }
+}
+
+void
+plugin_close (struct connection *conn)
+{
+  struct data data = { .conn = conn, .f = filters };
+  filter_close (&data);
+}
+
+static int64_t
+final_get_size (struct connection *conn)
 {
   assert (dl);
   assert (connection_get_handle (conn));
@@ -554,8 +710,17 @@ plugin_get_size (struct connection *conn)
   return plugin.get_size (connection_get_handle (conn));
 }
 
-int
-plugin_can_write (struct connection *conn)
+CALL_FILTERS_NO_ARGS (get_size, int64_t)
+
+int64_t
+plugin_get_size (struct connection *conn)
+{
+  struct data data = { .conn = conn, .f = filters };
+  return filter_get_size (&data);
+}
+
+static int
+final_can_write (struct connection *conn)
 {
   assert (dl);
   assert (connection_get_handle (conn));
@@ -568,8 +733,17 @@ plugin_can_write (struct connection *conn)
     return plugin.pwrite != NULL;
 }
 
+CALL_FILTERS_NO_ARGS (can_write, int)
+
 int
-plugin_can_flush (struct connection *conn)
+plugin_can_write (struct connection *conn)
+{
+  struct data data = { .conn = conn, .f = filters };
+  return filter_can_write (&data);
+}
+
+static int
+final_can_flush (struct connection *conn)
 {
   assert (dl);
   assert (connection_get_handle (conn));
@@ -582,8 +756,17 @@ plugin_can_flush (struct connection *conn)
     return plugin.flush != NULL;
 }
 
+CALL_FILTERS_NO_ARGS (can_flush, int)
+
 int
-plugin_is_rotational (struct connection *conn)
+plugin_can_flush (struct connection *conn)
+{
+  struct data data = { .conn = conn, .f = filters };
+  return filter_can_flush (&data);
+}
+
+static int
+final_is_rotational (struct connection *conn)
 {
   assert (dl);
   assert (connection_get_handle (conn));
@@ -596,8 +779,17 @@ plugin_is_rotational (struct connection *conn)
     return 0; /* assume false */
 }
 
+CALL_FILTERS_NO_ARGS (is_rotational, int)
+
 int
-plugin_can_trim (struct connection *conn)
+plugin_is_rotational (struct connection *conn)
+{
+  struct data data = { .conn = conn, .f = filters };
+  return filter_is_rotational (&data);
+}
+
+static int
+final_can_trim (struct connection *conn)
 {
   assert (dl);
   assert (connection_get_handle (conn));
@@ -610,9 +802,18 @@ plugin_can_trim (struct connection *conn)
     return plugin.trim != NULL;
 }
 
+CALL_FILTERS_NO_ARGS (can_trim, int)
+
 int
-plugin_pread (struct connection *conn,
-              void *buf, uint32_t count, uint64_t offset)
+plugin_can_trim (struct connection *conn)
+{
+  struct data data = { .conn = conn, .f = filters };
+  return filter_can_trim (&data);
+}
+
+static int
+final_pread (struct connection *conn,
+             void *buf, uint32_t count, uint64_t offset)
 {
   assert (dl);
   assert (connection_get_handle (conn));
@@ -623,9 +824,21 @@ plugin_pread (struct connection *conn,
   return plugin.pread (connection_get_handle (conn), buf, count, offset);
 }
 
+CALL_FILTERS (pread, int,
+              (void *buf, uint32_t count, uint64_t offset),
+              (buf, count, offset))
+
 int
-plugin_pwrite (struct connection *conn,
-               void *buf, uint32_t count, uint64_t offset)
+plugin_pread (struct connection *conn,
+              void *buf, uint32_t count, uint64_t offset)
+{
+  struct data data = { .conn = conn, .f = filters };
+  return filter_pread (&data, buf, count, offset);
+}
+
+static int
+final_pwrite (struct connection *conn,
+              const void *buf, uint32_t count, uint64_t offset)
 {
   assert (dl);
   assert (connection_get_handle (conn));
@@ -640,8 +853,20 @@ plugin_pwrite (struct connection *conn,
   }
 }
 
+CALL_FILTERS (pwrite, int,
+              (const void *buf, uint32_t count, uint64_t offset),
+              (buf, count, offset))
+
 int
-plugin_flush (struct connection *conn)
+plugin_pwrite (struct connection *conn,
+               const void *buf, uint32_t count, uint64_t offset)
+{
+  struct data data = { .conn = conn, .f = filters };
+  return filter_pwrite (&data, buf, count, offset);
+}
+
+static int
+final_flush (struct connection *conn)
 {
   assert (dl);
   assert (connection_get_handle (conn));
@@ -656,8 +881,17 @@ plugin_flush (struct connection *conn)
   }
 }
 
+CALL_FILTERS_NO_ARGS (flush, int)
+
 int
-plugin_trim (struct connection *conn, uint32_t count, uint64_t offset)
+plugin_flush (struct connection *conn)
+{
+  struct data data = { .conn = conn, .f = filters };
+  return filter_flush (&data);
+}
+
+static int
+final_trim (struct connection *conn, uint32_t count, uint64_t offset)
 {
   assert (dl);
   assert (connection_get_handle (conn));
@@ -672,9 +906,20 @@ plugin_trim (struct connection *conn, uint32_t count, uint64_t offset)
   }
 }
 
+CALL_FILTERS (trim, int,
+              (uint32_t count, uint64_t offset),
+              (count, offset))
+
 int
-plugin_zero (struct connection *conn,
-             uint32_t count, uint64_t offset, int may_trim)
+plugin_trim (struct connection *conn, uint32_t count, uint64_t offset)
+{
+  struct data data = { .conn = conn, .f = filters };
+  return filter_trim (&data, count, offset);
+}
+
+static int
+final_zero (struct connection *conn,
+            uint32_t count, uint64_t offset, int may_trim)
 {
   assert (dl);
   assert (connection_get_handle (conn));
@@ -710,7 +955,7 @@ plugin_zero (struct connection *conn,
   }
 
   while (count) {
-    result = plugin.pwrite (connection_get_handle (conn), buf, limit, offset);
+    result = plugin_pwrite (conn, buf, limit, offset);
     if (result < 0)
       break;
     count -= limit;
@@ -723,3 +968,15 @@ plugin_zero (struct connection *conn,
   errno = err;
   return result;
 }
+
+CALL_FILTERS (zero, int,
+              (uint32_t count, uint64_t offset, int may_trim),
+              (count, offset, may_trim))
+
+int
+plugin_zero (struct connection *conn,
+             uint32_t count, uint64_t offset, int may_trim)
+{
+  struct data data = { .conn = conn, .f = filters };
+  return filter_zero (&data, count, offset, may_trim);
+}
-- 
2.15.1



More information about the Libguestfs mailing list