Instead of returning an int on success, return a pointer to everything
needed to call into the next layer via next_ops. The public interface
in nbdkit-filter.h still returns int, so we need a next_open() wrapper
added back in filters.h. Similarly, an upcoming patch wants to update
backend_* functions to directly take a struct context* instead of the
current approach of a struct backend* coupled with querying TLS for
the current connection. This will be easier by making struct context
track the struct backend that it was opened under. Struct connection
still maintains a list of contexts, but now instead of directly
populating direct members of an array during backend_open, we now
populate pointers in the callers of backend_open; this will let us
progress towards filters opening a context independently from the
current client connection.
---
server/internal.h | 15 +++++--
server/backend.c | 65 +++++++++++++++-------------
server/connections.c | 7 +--
server/filters.c | 16 ++++++-
server/protocol-handshake-newstyle.c | 8 ++--
server/protocol-handshake.c | 7 ++-
6 files changed, 73 insertions(+), 45 deletions(-)
diff --git a/server/internal.h b/server/internal.h
index 5e382fe0..1add5f36 100644
--- a/server/internal.h
+++ b/server/internal.h
@@ -208,6 +208,7 @@ enum {
struct context {
void *handle; /* Plugin or filter handle. */
+ struct backend *b; /* Backend that provided handle. */
unsigned char state; /* Bitmask of HANDLE_* values */
@@ -254,7 +255,7 @@ struct connection {
void *crypto_session;
int nworkers;
- struct context *contexts; /* One per plugin and filter. */
+ struct context **contexts; /* One per plugin and filter. */
char **default_exportname; /* One per plugin and filter. */
uint32_t cflags;
@@ -277,7 +278,13 @@ struct connection {
static inline struct context *
get_context (struct connection *conn, int i)
{
- return &conn->contexts[i];
+ return conn->contexts[i];
+}
+
+static inline void
+set_context (struct connection *conn, int i, struct context *context)
+{
+ conn->contexts[i] = context;
}
extern void handle_single_connection (int sockin, int sockout);
@@ -443,8 +450,8 @@ extern const char *backend_export_description (struct backend *b)
* freed on return of this function, so backends must save the
* exportname if they need to refer to it later.
*/
-extern int backend_open (struct backend *b,
- int readonly, const char *exportname)
+extern struct context *backend_open (struct backend *b,
+ int readonly, const char *exportname)
__attribute__((__nonnull__ (1, 3)));
extern int backend_prepare (struct backend *b)
__attribute__((__nonnull__ (1)));
diff --git a/server/backend.c b/server/backend.c
index 0a67e8be..cebd55ab 100644
--- a/server/backend.c
+++ b/server/backend.c
@@ -169,8 +169,7 @@ backend_list_exports (struct backend *b, int readonly,
controlpath_debug ("%s: list_exports readonly=%d tls=%d",
b->name, readonly, conn->using_tls);
- assert (c->handle == NULL);
- assert ((c->state & HANDLE_OPEN) == 0);
+ assert (c == NULL);
if (b->list_exports (b, readonly, conn->using_tls, exports) == -1 ||
exports_resolve_default (exports, b, readonly) == -1) {
@@ -194,8 +193,7 @@ backend_default_export (struct backend *b, int readonly)
b->name, readonly, conn->using_tls);
if (conn->default_exportname[b->i] == NULL) {
- assert (c->handle == NULL);
- assert ((c->state & HANDLE_OPEN) == 0);
+ assert (c == NULL);
s = b->default_export (b, readonly, conn->using_tls);
/* Ignore over-length strings. XXX Also ignore non-UTF8? */
if (s && strnlen (s, NBD_MAX_STRING + 1) > NBD_MAX_STRING) {
@@ -213,18 +211,22 @@ backend_default_export (struct backend *b, int readonly)
return conn->default_exportname[b->i];
}
-int
+struct context *
backend_open (struct backend *b, int readonly, const char *exportname)
{
GET_CONN;
- struct context *c = get_context (conn, b->i);
+ struct context *c = malloc (sizeof *c);
+
+ if (c == NULL) {
+ nbdkit_error ("malloc: %m");
+ return NULL;
+ }
controlpath_debug ("%s: open readonly=%d exportname=\"%s\"
tls=%d",
b->name, readonly, exportname, conn->using_tls);
- assert (c->handle == NULL);
- assert ((c->state & HANDLE_OPEN) == 0);
- assert (c->can_write == -1);
+ assert (conn->contexts[b->i] == NULL);
+ reset_context (c);
if (readonly)
c->can_write = 0;
@@ -233,7 +235,8 @@ backend_open (struct backend *b, int readonly, const char
*exportname)
exportname = backend_default_export (b, readonly);
if (exportname == NULL) {
nbdkit_error ("default export (\"\") not permitted");
- return -1;
+ free (c);
+ return NULL;
}
}
@@ -246,11 +249,13 @@ backend_open (struct backend *b, int readonly, const char
*exportname)
if (c->handle == NULL) {
if (b->i) /* Do not strand backend if this layer failed */
backend_close (b->next);
- return -1;
+ free (c);
+ return NULL;
}
c->state |= HANDLE_OPEN;
- return 0;
+ c->b = b;
+ return c;
}
int
@@ -266,7 +271,7 @@ backend_prepare (struct backend *b)
* plugin, similar to typical .open order. But remember that
* a filter may skip opening its backend.
*/
- if (b->i && get_context (conn, b->i-1)->handle != NULL &&
+ if (b->i && get_context (conn, b->i-1) != NULL &&
backend_prepare (b->next) == -1)
return -1;
@@ -301,7 +306,7 @@ backend_finalize (struct backend *b)
}
}
- if (b->i)
+ if (b->i && get_context (conn, b->i-1))
return backend_finalize (b->next);
return 0;
}
@@ -313,16 +318,13 @@ backend_close (struct backend *b)
struct context *c = get_context (conn, b->i);
/* outer-to-inner order, opposite .open */
-
- if (c->handle) {
- assert (c->state & HANDLE_OPEN);
- controlpath_debug ("%s: close", b->name);
- b->close (b, c->handle);
- }
- else
- assert (! (c->state & HANDLE_OPEN));
- reset_context (c);
- if (b->i)
+ assert (c->handle);
+ assert (c->state & HANDLE_OPEN);
+ controlpath_debug ("%s: close", b->name);
+ b->close (b, c->handle);
+ free (c);
+ set_context (conn, b->i, NULL);
+ if (b->i && get_context (conn, b->i-1))
backend_close (b->next);
}
@@ -342,16 +344,21 @@ backend_valid_range (struct backend *b, uint64_t offset, uint32_t
count)
int
backend_reopen (struct backend *b, int readonly, const char *exportname)
{
+ GET_CONN;
+ struct context *h;
+
controlpath_debug ("%s: reopen readonly=%d exportname=\"%s\"",
b->name, readonly, exportname);
- if (backend_finalize (b) == -1)
- return -1;
- backend_close (b);
- if (backend_open (b, readonly, exportname) == -1) {
+ if (get_context (conn, b->i)) {
+ if (backend_finalize (b) == -1)
+ return -1;
backend_close (b);
- return -1;
}
+ h = backend_open (b, readonly, exportname);
+ if (h == NULL)
+ return -1;
+ set_context (conn, b->i, h);
if (backend_prepare (b) == -1) {
backend_finalize (b);
backend_close (b);
diff --git a/server/connections.c b/server/connections.c
index 87db2d80..e3fdc649 100644
--- a/server/connections.c
+++ b/server/connections.c
@@ -240,7 +240,6 @@ new_connection (int sockin, int sockout, int nworkers)
struct connection *conn;
int opt;
socklen_t optlen = sizeof opt;
- struct backend *b;
conn = calloc (1, sizeof *conn);
if (conn == NULL) {
@@ -259,9 +258,6 @@ new_connection (int sockin, int sockout, int nworkers)
perror ("malloc");
goto error1;
}
- for_each_backend (b)
- reset_context (get_context (conn, b->i));
-
conn->default_exportname = calloc (top->i + 1,
sizeof *conn->default_exportname);
if (conn->default_exportname == NULL) {
@@ -364,7 +360,8 @@ free_connection (struct connection *conn)
*/
if (!quit) {
lock_request ();
- backend_close (top);
+ if (get_context (conn, top->i))
+ backend_close (top);
unlock_request ();
}
diff --git a/server/filters.c b/server/filters.c
index 31be9742..dde36306 100644
--- a/server/filters.c
+++ b/server/filters.c
@@ -260,6 +260,18 @@ filter_default_export (struct backend *b, int readonly, int is_tls)
return backend_default_export (b->next, readonly);
}
+static int
+next_open (struct backend *b, int readonly, const char *exportname)
+{
+ GET_CONN;
+ struct context *c = backend_open (b, readonly, exportname);
+
+ if (c == NULL)
+ return -1;
+ set_context (conn, b->i, c);
+ return 0;
+}
+
static void *
filter_open (struct backend *b, int readonly, const char *exportname,
int is_tls)
@@ -271,9 +283,9 @@ filter_open (struct backend *b, int readonly, const char *exportname,
* inner-to-outer ordering.
*/
if (f->filter.open)
- handle = f->filter.open (backend_open, b->next, readonly, exportname,
+ handle = f->filter.open (next_open, b->next, readonly, exportname,
is_tls);
- else if (backend_open (b->next, readonly, exportname) == -1)
+ else if (next_open (b->next, readonly, exportname) == -1)
handle = NULL;
else
handle = NBDKIT_HANDLE_NOT_NEEDED;
diff --git a/server/protocol-handshake-newstyle.c b/server/protocol-handshake-newstyle.c
index fb4a93bc..015e7f9b 100644
--- a/server/protocol-handshake-newstyle.c
+++ b/server/protocol-handshake-newstyle.c
@@ -567,9 +567,11 @@ negotiate_handshake_newstyle_options (void)
*/
if (finish_newstyle_options (&exportsize,
&data[4], exportnamelen) == -1) {
- if (backend_finalize (top) == -1)
- return -1;
- backend_close (top);
+ if (get_context (conn, top->i)) {
+ if (backend_finalize (top) == -1)
+ return -1;
+ backend_close (top);
+ }
if (send_newstyle_option_reply (option, NBD_REP_ERR_UNKNOWN) == -1)
return -1;
continue;
diff --git a/server/protocol-handshake.c b/server/protocol-handshake.c
index 80233713..82e7e647 100644
--- a/server/protocol-handshake.c
+++ b/server/protocol-handshake.c
@@ -1,5 +1,5 @@
/* nbdkit
- * Copyright (C) 2013-2020 Red Hat Inc.
+ * Copyright (C) 2013-2021 Red Hat Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -79,9 +79,12 @@ protocol_common_open (uint64_t *exportsize, uint16_t *flags,
int64_t size;
uint16_t eflags = NBD_FLAG_HAS_FLAGS;
int fl;
+ struct context *c;
- if (backend_open (top, read_only, exportname) == -1)
+ c = backend_open (top, read_only, exportname);
+ if (c == NULL)
return -1;
+ set_context (conn, top->i, c);
/* Prepare (for filters), called just after open. */
if (backend_prepare (top) == -1)
--
2.30.1