>From 7afbdf9da4608abcf0993fc1be95554639a6a415 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" 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