On 7/13/23 21:29, Eric Blake wrote:
> Enhance the regression tests to prove that the completion callback is
> not reached if the aio call itself reports an error; only the .free
> callback is guaranteed.
> 
> Also add some asserts to the library code that may aid future readers
> in seeing how we track transfer semantics of a callback.
> 
> Goes hand in hand with the documentation cleanups made in the previous
> patch, but done separately for ease of backporting as nbd_aio_opt
> calls are not in as many releases.
> 
> Reported-by: Tage Johansson <tage.j.li...@posteo.net>
> Signed-off-by: Eric Blake <ebl...@redhat.com>
> ---
>  lib/opt.c                          |   2 +
>  tests/Makefile.am                  |   5 +
>  tests/errors-not-negotiating-aio.c | 170 +++++++++++++++++++++++++++++
>  .gitignore                         |   1 +
>  4 files changed, 178 insertions(+)
>  create mode 100644 tests/errors-not-negotiating-aio.c

Acked-by: Laszlo Ersek <ler...@redhat.com>

Laszlo

> 
> diff --git a/lib/opt.c b/lib/opt.c
> index f58d5e19..1446eef3 100644
> --- a/lib/opt.c
> +++ b/lib/opt.c
> @@ -223,6 +223,7 @@ nbd_unlocked_opt_list (struct nbd_handle *h, 
> nbd_list_callback *list)
>    if (nbd_unlocked_aio_opt_list (h, &l, &c) == -1)
>      return -1;
> 
> +  assert (CALLBACK_IS_NULL (l));
>    SET_CALLBACK_TO_NULL (*list);
>    if (wait_for_option (h) == -1)
>      return -1;
> @@ -269,6 +270,7 @@ opt_meta_context_queries (struct nbd_handle *h,
>    if (aio_opt_meta_context_queries (h, opt, queries, &l, &c) == -1)
>      return -1;
> 
> +  assert (CALLBACK_IS_NULL (l));
>    SET_CALLBACK_TO_NULL (*context);
>    if (wait_for_option (h) == -1)
>      return -1;
> diff --git a/tests/Makefile.am b/tests/Makefile.am
> index 3a93251e..6eddcb7a 100644
> --- a/tests/Makefile.am
> +++ b/tests/Makefile.am
> @@ -171,6 +171,7 @@ check_PROGRAMS += \
>       errors-connect-null \
>       errors-connect-twice \
>       errors-not-negotiating \
> +     errors-not-negotiating-aio \
>       errors-notify-not-blocked \
>       errors-bad-cookie \
>       errors-pread-structured \
> @@ -243,6 +244,7 @@ TESTS += \
>       errors-connect-null \
>       errors-connect-twice \
>       errors-not-negotiating \
> +     errors-not-negotiating-aio \
>       errors-notify-not-blocked \
>       errors-bad-cookie \
>       errors-pread-structured \
> @@ -332,6 +334,9 @@ errors_connect_twice_LDADD = $(top_builddir)/lib/libnbd.la
>  errors_not_negotiating_SOURCES = errors-not-negotiating.c
>  errors_not_negotiating_LDADD = $(top_builddir)/lib/libnbd.la
> 
> +errors_not_negotiating_aio_SOURCES = errors-not-negotiating-aio.c
> +errors_not_negotiating_aio_LDADD = $(top_builddir)/lib/libnbd.la
> +
>  errors_notify_not_blocked_SOURCES = errors-notify-not-blocked.c
>  errors_notify_not_blocked_LDADD = $(top_builddir)/lib/libnbd.la
> 
> diff --git a/tests/errors-not-negotiating-aio.c 
> b/tests/errors-not-negotiating-aio.c
> new file mode 100644
> index 00000000..b09cae82
> --- /dev/null
> +++ b/tests/errors-not-negotiating-aio.c
> @@ -0,0 +1,170 @@
> +/* NBD client library in userspace
> + * Copyright Red Hat
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 
> USA
> + */
> +
> +/* Deliberately provoke some errors and check the error messages from
> + * nbd_get_error etc look reasonable.
> + */
> +
> +#include <config.h>
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <errno.h>
> +
> +#include <libnbd.h>
> +
> +static char *progname;
> +
> +static void
> +check (int experr, const char *prefix)
> +{
> +  const char *msg = nbd_get_error ();
> +  int errnum = nbd_get_errno ();
> +
> +  fprintf (stderr, "error: \"%s\"\n", msg);
> +  fprintf (stderr, "errno: %d (%s)\n", errnum, strerror (errnum));
> +  if (strncmp (msg, prefix, strlen (prefix)) != 0) {
> +    fprintf (stderr, "%s: test failed: missing context prefix: %s\n",
> +             progname, msg);
> +    exit (EXIT_FAILURE);
> +  }
> +  if (errnum != experr) {
> +    fprintf (stderr, "%s: test failed: "
> +             "expected errno = %d (%s), but got %d\n",
> +             progname, experr, strerror (experr), errnum);
> +    exit (EXIT_FAILURE);
> +  }
> +}
> +
> +struct progress {
> +  int id;
> +  bool call_freed;
> +  bool comp_freed;
> +};
> +
> +static int
> +list_call (void *user_data, const char *name, const char *description)
> +{
> +  struct progress *p = user_data;
> +
> +  fprintf (stderr, "%s: list callback should not be reached, iter %d\n",
> +           progname, p->id);
> +  exit (EXIT_FAILURE);
> +}
> +
> +static void
> +list_free (void *user_data)
> +{
> +  struct progress *p = user_data;
> +
> +  if (p->comp_freed) {
> +    fprintf (stderr, "%s: list free called too late, iter %d\n",
> +             progname, p->id);
> +    exit (EXIT_FAILURE);
> +  }
> +  p->call_freed = true;
> +}
> +
> +static int
> +comp_call (void *user_data, int *error)
> +{
> +  struct progress *p = user_data;
> +
> +  fprintf (stderr, "%s: list callback should not be reached, iter %d\n",
> +           progname, p->id);
> +  exit (EXIT_FAILURE);
> +}
> +
> +static void
> +comp_free (void *user_data)
> +{
> +  struct progress *p = user_data;
> +
> +  if (!p->call_freed) {
> +    fprintf (stderr, "%s: completion free called too early, iter %d\n",
> +             progname, p->id);
> +    exit (EXIT_FAILURE);
> +  }
> +  p->comp_freed = true;
> +}
> +
> +int
> +main (int argc, char *argv[])
> +{
> +  struct nbd_handle *nbd;
> +  const char *cmd[] = {
> +    "nbdkit", "-s", "-v", "--exit-with-parent", "memory", "1048576", NULL
> +  };
> +  struct progress p;
> +  nbd_list_callback list_cb = { .callback = list_call,
> +                                .user_data = &p,
> +                                .free = list_free };
> +  nbd_completion_callback comp_cb = { .callback = comp_call,
> +                                      .user_data = &p,
> +                                      .free = comp_free };
> +
> +  progname = argv[0];
> +
> +  nbd = nbd_create ();
> +  if (nbd == NULL) {
> +    fprintf (stderr, "%s\n", nbd_get_error ());
> +    exit (EXIT_FAILURE);
> +  }
> +
> +  /* Too early to try nbd_aio_opt_*. */
> +  p = (struct progress) { .id = 0 };
> +  if (nbd_aio_opt_list (nbd, list_cb, comp_cb) != -1) {
> +    fprintf (stderr, "%s: test failed: "
> +             "nbd_aio_opt_list did not reject attempt in wrong state\n",
> +             argv[0]);
> +    exit (EXIT_FAILURE);
> +  }
> +  check (ENOTCONN, "nbd_aio_opt_list: ");
> +  if (!p.call_freed || !p.comp_freed) {
> +    fprintf (stderr, "%s: test failed: "
> +             "nbd_aio_opt_list did not call .free callbacks\n",
> +             argv[0]);
> +    exit (EXIT_FAILURE);
> +  }
> +
> +  /* Connect to the server without requesting negotiation mode. */
> +  if (nbd_connect_command (nbd, (char **)cmd) == -1) {
> +    fprintf (stderr, "%s: %s\n", argv[0], nbd_get_error ());
> +    exit (EXIT_FAILURE);
> +  }
> +
> +  /* Too late to try nbd_aio_opt_*. */
> +  p = (struct progress) { .id = 1 };
> +  if (nbd_aio_opt_list (nbd, list_cb, comp_cb) != -1) {
> +    fprintf (stderr, "%s: test failed: "
> +             "nbd_aio_opt_list did not reject attempt in wrong state\n",
> +             argv[0]);
> +    exit (EXIT_FAILURE);
> +  }
> +  check (EINVAL, "nbd_aio_opt_list: ");
> +  if (!p.call_freed || !p.comp_freed) {
> +    fprintf (stderr, "%s: test failed: "
> +             "nbd_aio_opt_list did not call .free callbacks\n",
> +             argv[0]);
> +    exit (EXIT_FAILURE);
> +  }
> +
> +  nbd_close (nbd);
> +  exit (EXIT_SUCCESS);
> +}
> diff --git a/.gitignore b/.gitignore
> index efe3080a..b43e83c0 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -220,6 +220,7 @@ Makefile.in
>  /tests/errors-name-too-long
>  /tests/errors-not-connected
>  /tests/errors-not-negotiating
> +/tests/errors-not-negotiating-aio
>  /tests/errors-notify-not-blocked
>  /tests/errors-poll-no-fd
>  /tests/errors-pread-structured

_______________________________________________
Libguestfs mailing list
Libguestfs@redhat.com
https://listman.redhat.com/mailman/listinfo/libguestfs

Reply via email to