Ejemplo n.º 1
0
/**
 * Test C<guestfs_int_new_command> etc.
 *
 * XXX These tests could be made much more thorough.  So far we simply
 * test that it's not obviously broken.
 */
static void
test_command (void)
{
  guestfs_h *g;
  struct command *cmd;
  int r;

  g = guestfs_create ();
  assert (g);

  /* argv-style */
  cmd = guestfs_int_new_command (g);
  assert (cmd);
  guestfs_int_cmd_add_arg (cmd, "touch");
  guestfs_int_cmd_add_arg (cmd, "test-utils-test-command");
  r = guestfs_int_cmd_run (cmd);
  assert (r == 0);
  guestfs_int_cmd_close (cmd);

  /* system-style */
  cmd = guestfs_int_new_command (g);
  assert (cmd);
  guestfs_int_cmd_add_string_unquoted (cmd, "rm ");
  guestfs_int_cmd_add_string_quoted (cmd, "test-utils-test-command");
  r = guestfs_int_cmd_run (cmd);
  assert (r == 0);
  guestfs_int_cmd_close (cmd);

  guestfs_close (g);
}
Ejemplo n.º 2
0
/**
 * Test if the qemu-img info command supports the C<-U> option to
 * disable locking.  The result is memoized in the handle.
 *
 * Note this option was added in qemu 2.11.  We can remove this test
 * when we can assume everyone is using qemu >= 2.11.
 */
static int
qemu_img_supports_U_option (guestfs_h *g)
{
  if (g->qemu_img_supports_U_option >= 0)
    return g->qemu_img_supports_U_option;

  CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g);
  int r;

  guestfs_int_cmd_add_string_unquoted (cmd,
                                       "qemu-img --help | "
                                       "grep -sqE -- '\\binfo\\b.*-U\\b'");
  r = guestfs_int_cmd_run (cmd);
  if (r == -1)
    return -1;
  if (!WIFEXITED (r)) {
    guestfs_int_external_command_failed (g, r,
                                         "qemu-img info -U option test",
                                         NULL);
    return -1;
  }

  g->qemu_img_supports_U_option = WEXITSTATUS (r) == 0;
  return g->qemu_img_supports_U_option;
}
Ejemplo n.º 3
0
/* Run uml_mkcow to create a COW overlay. */
static char *
make_cow_overlay (guestfs_h *g, const char *original)
{
  CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g);
  char *overlay;
  int r;

  if (guestfs_int_lazy_make_tmpdir (g) == -1)
    return NULL;

  overlay = safe_asprintf (g, "%s/overlay%d", g->tmpdir, g->unique++);

  guestfs_int_cmd_add_arg (cmd, "uml_mkcow");
  guestfs_int_cmd_add_arg (cmd, overlay);
  guestfs_int_cmd_add_arg (cmd, original);
  r = guestfs_int_cmd_run (cmd);
  if (r == -1) {
    free (overlay);
    return NULL;
  }
  if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) {
    guestfs_int_external_command_failed (g, r, "uml_mkcow", original);
    free (overlay);
    return NULL;
  }

  return overlay;
}
Ejemplo n.º 4
0
static int
test_qemu (guestfs_h *g, struct qemu_data *data, struct version *qemu_version)
{
  CLEANUP_CMD_CLOSE struct command *cmd1 = guestfs_int_new_command (g);
  CLEANUP_CMD_CLOSE struct command *cmd2 = guestfs_int_new_command (g);
  int r;

  guestfs_int_cmd_add_arg (cmd1, g->hv);
  guestfs_int_cmd_add_arg (cmd1, "-display");
  guestfs_int_cmd_add_arg (cmd1, "none");
  guestfs_int_cmd_add_arg (cmd1, "-help");
  guestfs_int_cmd_set_stdout_callback (cmd1, read_all, &data->qemu_help,
				       CMD_STDOUT_FLAG_WHOLE_BUFFER);
  r = guestfs_int_cmd_run (cmd1);
  if (r == -1 || !WIFEXITED (r) || WEXITSTATUS (r) != 0)
    goto error;

  parse_qemu_version (g, data->qemu_help, qemu_version);

  guestfs_int_cmd_add_arg (cmd2, g->hv);
  guestfs_int_cmd_add_arg (cmd2, "-display");
  guestfs_int_cmd_add_arg (cmd2, "none");
  guestfs_int_cmd_add_arg (cmd2, "-machine");
  guestfs_int_cmd_add_arg (cmd2,
#ifdef MACHINE_TYPE
                           MACHINE_TYPE ","
#endif
                           "accel=kvm:tcg");
  guestfs_int_cmd_add_arg (cmd2, "-device");
  guestfs_int_cmd_add_arg (cmd2, "?");
  guestfs_int_cmd_clear_capture_errors (cmd2);
  guestfs_int_cmd_set_stderr_to_stdout (cmd2);
  guestfs_int_cmd_set_stdout_callback (cmd2, read_all, &data->qemu_devices,
				       CMD_STDOUT_FLAG_WHOLE_BUFFER);
  r = guestfs_int_cmd_run (cmd2);
  if (r == -1 || !WIFEXITED (r) || WEXITSTATUS (r) != 0)
    goto error;

  return 0;

 error:
  if (r == -1)
    return -1;

  guestfs_int_external_command_failed (g, r, g->hv, NULL);
  return -1;
}
Ejemplo n.º 5
0
/* Recursively remove a temporary directory.  If removal fails, just
 * return (it's a temporary directory so it'll eventually be cleaned
 * up by a temp cleaner).  This is done using "rm -rf" because that's
 * simpler and safer.
 */
void
guestfs_int_recursive_remove_dir (guestfs_h *g, const char *dir)
{
  CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g);

  guestfs_int_cmd_add_arg (cmd, "rm");
  guestfs_int_cmd_add_arg (cmd, "-rf");
  guestfs_int_cmd_add_arg (cmd, dir);
  ignore_value (guestfs_int_cmd_run (cmd));
}
Ejemplo n.º 6
0
/* Run supermin --build and tell it to generate the
 * appliance.
 */
static int
run_supermin_build (guestfs_h *g,
                    const char *lockfile,
                    const char *appliancedir,
                    const char *supermin_path)
{
  CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g);
  int r;
#if 0                           /* not supported in supermin 5 yet XXX */
  uid_t uid = getuid ();
  uid_t euid = geteuid ();
  gid_t gid = getgid ();
  gid_t egid = getegid ();
  int pass_u_g_args = uid != euid || gid != egid;
#endif

  guestfs_int_cmd_add_arg (cmd, SUPERMIN);
  guestfs_int_cmd_add_arg (cmd, "--build");
  if (g->verbose)
    guestfs_int_cmd_add_arg (cmd, "--verbose");
  guestfs_int_cmd_add_arg (cmd, "--if-newer");
  guestfs_int_cmd_add_arg (cmd, "--lock");
  guestfs_int_cmd_add_arg (cmd, lockfile);
#if 0
  if (pass_u_g_args) {
    guestfs_int_cmd_add_arg (cmd, "-u");
    guestfs_int_cmd_add_arg_format (cmd, "%d", euid);
    guestfs_int_cmd_add_arg (cmd, "-g");
    guestfs_int_cmd_add_arg_format (cmd, "%d", egid);
  }
#endif
  guestfs_int_cmd_add_arg (cmd, "--copy-kernel");
  guestfs_int_cmd_add_arg (cmd, "-f");
  guestfs_int_cmd_add_arg (cmd, "ext2");
  guestfs_int_cmd_add_arg (cmd, "--host-cpu");
  guestfs_int_cmd_add_arg (cmd, host_cpu);
#ifdef DTB_WILDCARD
  guestfs_int_cmd_add_arg (cmd, "--dtb");
  guestfs_int_cmd_add_arg (cmd, DTB_WILDCARD);
#endif
  guestfs_int_cmd_add_arg_format (cmd, "%s/supermin.d", supermin_path);
  guestfs_int_cmd_add_arg (cmd, "-o");
  guestfs_int_cmd_add_arg (cmd, appliancedir);

  r = guestfs_int_cmd_run (cmd);
  if (r == -1)
    return -1;
  if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) {
    guestfs_int_external_command_failed (g, r, SUPERMIN, NULL);
    return -1;
  }

  return 0;
}
Ejemplo n.º 7
0
int
guestfs_impl_copy_in (guestfs_h *g, const char *localpath, const char *remotedir)
{
  CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g);
  int fd;
  int r;
  char fdbuf[64];
  size_t buf_len = strlen (localpath) + 1;
  char buf[buf_len];
  const char *dirname, *basename;

  int remote_is_dir = guestfs_is_dir (g, remotedir);
  if (remote_is_dir == -1)
    return -1;

  if (!remote_is_dir) {
    error (g, _("target '%s' is not a directory"), remotedir);
    return -1;
  }

  if (split_path (g, buf, buf_len, localpath, &dirname, &basename) == -1)
    return -1;

  guestfs_int_cmd_add_arg (cmd, "tar");
  if (dirname) {
    guestfs_int_cmd_add_arg (cmd, "-C");
    guestfs_int_cmd_add_arg (cmd, dirname);
  }
  guestfs_int_cmd_add_arg (cmd, "-cf");
  guestfs_int_cmd_add_arg (cmd, "-");
  guestfs_int_cmd_add_arg (cmd, basename);

  r = guestfs_int_cmd_run_async (cmd, NULL, NULL, &fd, NULL);
  if (r == -1)
    return -1;

  snprintf (fdbuf, sizeof fdbuf, "/dev/fd/%d", fd);

  r = guestfs_tar_in (g, fdbuf, remotedir);

  if (close (fd) == -1) {
    perrorf (g, "close (tar subprocess)");
    return -1;
  }

  r = guestfs_int_cmd_wait (cmd);
  if (r == -1)
    return -1;
  if (!(WIFEXITED (r) && WEXITSTATUS (r) == 0))
    return -1;

  return 0;
}
Ejemplo n.º 8
0
/**
 * Return the location of firmware needed to boot the appliance.  This
 * is aarch64 only currently, since that's the only architecture where
 * UEFI is mandatory (and that only for RHEL).
 *
 * C<*code> is initialized with the path to the read-only UEFI code
 * file.  C<*vars> is initialized with the path to a copy of the UEFI
 * vars file (which is cleaned up automatically on exit).
 *
 * If C<*code> == C<*vars> == C<NULL> then no UEFI firmware is
 * available.
 *
 * C<*code> and C<*vars> should be freed by the caller.
 *
 * If the function returns C<-1> then there was a real error which
 * should cause appliance building to fail (no UEFI firmware is not an
 * error).
 *
 * See also F<v2v/utils.ml>:find_uefi_firmware
 */
int
guestfs_int_get_uefi (guestfs_h *g, char **code, char **vars, int *flags)
{
#ifdef __aarch64__
  size_t i;

  for (i = 0; guestfs_int_uefi_aarch64_firmware[i].code != NULL; ++i) {
    const char *codefile = guestfs_int_uefi_aarch64_firmware[i].code;
    const char *code_debug_file =
      guestfs_int_uefi_aarch64_firmware[i].code_debug;
    const char *varsfile = guestfs_int_uefi_aarch64_firmware[i].vars;

    if (access (codefile, R_OK) == 0 && access (varsfile, R_OK) == 0) {
      CLEANUP_CMD_CLOSE struct command *copycmd = guestfs_int_new_command (g);
      char *varst;
      int r;

      /* Make a copy of NVRAM variables file.  You can't just map it
       * into the address space read-only as that triggers a different
       * path inside UEFI.
       */
      varst = safe_asprintf (g, "%s/vars.fd.%d", g->tmpdir, ++g->unique);
      guestfs_int_cmd_add_arg (copycmd, "cp");
      guestfs_int_cmd_add_arg (copycmd, varsfile);
      guestfs_int_cmd_add_arg (copycmd, varst);
      r = guestfs_int_cmd_run (copycmd);
      if (r == -1 || !WIFEXITED (r) || WEXITSTATUS (r) != 0) {
        free (varst);
        return -1;
      }

      /* If debugging is enabled and we can find the code file with
       * debugging enabled, use that instead.
       */
      if (g->verbose && access (code_debug_file, R_OK) == 0)
	codefile = code_debug_file;

      /* Caller frees. */
      *code = safe_strdup (g, codefile);
      *vars = varst;
      *flags = guestfs_int_uefi_aarch64_firmware[i].flags;
      return 0;
    }
  }
#endif

  /* Not found. */
  *code = *vars = NULL;
  *flags = 0;
  return 0;
}
Ejemplo n.º 9
0
int
guestfs_impl_umount_local (guestfs_h *g,
			   const struct guestfs_umount_local_argv *optargs)
{
  const char *retry;
  int r;
  CLEANUP_FREE char *localmountpoint = NULL;
  CLEANUP_CMD_CLOSE struct command *cmd = NULL;

  /* How many times should we try the fusermount command? */
  if (optargs->bitmask & GUESTFS_UMOUNT_LOCAL_RETRY_BITMASK)
    retry = optargs->retry ? "--retry=5" : "--no-retry";
  else
    retry = "--no-retry";

  /* Make a local copy of g->localmountpoint.  It could be freed from
   * under us by another thread, except when we are holding the lock.
   */
  gl_lock_lock (mount_local_lock);
  if (g->localmountpoint)
    localmountpoint = safe_strdup (g, g->localmountpoint);
  else
    localmountpoint = NULL;
  gl_lock_unlock (mount_local_lock);

  if (!localmountpoint) {
    error (g, _("no filesystem is mounted"));
    return -1;
  }

  /* Run guestunmount --retry=... localmountpoint. */
  cmd = guestfs_int_new_command (g);
  guestfs_int_cmd_add_arg (cmd, "guestunmount");
  guestfs_int_cmd_add_arg (cmd, retry);
  guestfs_int_cmd_add_arg (cmd, localmountpoint);
  r = guestfs_int_cmd_run (cmd);
  if (r == -1)
    return -1;
  if (WIFEXITED (r) && WEXITSTATUS (r) == EXIT_SUCCESS)
    /* External fusermount succeeded.  Note that the original thread
     * is responsible for setting g->localmountpoint to NULL.
     */
    return 0;

  return -1;
}
Ejemplo n.º 10
0
static json_t *
get_json_output (guestfs_h *g, const char *filename)
{
  CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g);
  int r;
  json_t *tree = NULL;

  guestfs_int_cmd_add_arg (cmd, "qemu-img");
  guestfs_int_cmd_add_arg (cmd, "info");
  switch (qemu_img_supports_U_option (g)) {
  case -1: return NULL;
  case 0:  break;
  default: guestfs_int_cmd_add_arg (cmd, "-U");
  }
  guestfs_int_cmd_add_arg (cmd, "--output");
  guestfs_int_cmd_add_arg (cmd, "json");
  if (filename[0] == '/')
    guestfs_int_cmd_add_arg (cmd, filename);
  else
    guestfs_int_cmd_add_arg_format (cmd, "./%s", filename);
  guestfs_int_cmd_set_stdout_callback (cmd, parse_json, &tree,
                                       CMD_STDOUT_FLAG_WHOLE_BUFFER);
  set_child_rlimits (cmd);
  r = guestfs_int_cmd_run (cmd);
  if (r == -1)
    return NULL;
  if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) {
    guestfs_int_external_command_failed (g, r, "qemu-img info", filename);
    return NULL;
  }

  if (tree == NULL)
    return NULL;        /* parse_json callback already set an error */

  if (tree == PARSE_JSON_NO_OUTPUT) {
    /* If this ever happened, it would indicate a bug in 'qemu-img info'. */
    error (g, _("qemu-img info command produced no output, but didn't return an error status code"));
    return NULL;
  }

  return tree;          /* caller must call json_decref (tree) */
}
Ejemplo n.º 11
0
int
guestfs_int_get_uefi (guestfs_h *g, char **code, char **vars)
{
  size_t i;

  for (i = 0; uefi_firmware[i] != NULL; i += 2) {
    const char *codefile = uefi_firmware[i];
    const char *varsfile = uefi_firmware[i+1];

    if (access (codefile, R_OK) == 0 && access (varsfile, R_OK) == 0) {
      CLEANUP_CMD_CLOSE struct command *copycmd = guestfs_int_new_command (g);
      char *varst;
      int r;

      /* Make a copy of NVRAM variables file.  You can't just map it
       * into the address space read-only as that triggers a different
       * path inside UEFI.
       */
      varst = safe_asprintf (g, "%s/vars.fd.%d", g->tmpdir, ++g->unique);
      guestfs_int_cmd_add_arg (copycmd, "cp");
      guestfs_int_cmd_add_arg (copycmd, varsfile);
      guestfs_int_cmd_add_arg (copycmd, varst);
      r = guestfs_int_cmd_run (copycmd);
      if (r == -1 || !WIFEXITED (r) || WEXITSTATUS (r) != 0) {
        free (varst);
        return -1;
      }

      /* Caller frees. */
      *code = safe_strdup (g, codefile);
      *vars = varst;
      return 0;
    }
  }

  /* Not found. */
  *code = *vars = NULL;
  return 0;
}
Ejemplo n.º 12
0
int
guestfs_impl_copy_in (guestfs_h *g,
                      const char *localpath, const char *remotedir)
{
  CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g);
  int fd;
  int r;
  char fdbuf[64];
  size_t buf_len = strlen (localpath) + 1;
  CLEANUP_FREE char *buf = safe_malloc (g, buf_len);
  const char *dirname, *basename;
  struct stat statbuf;

  if (stat (localpath, &statbuf) == -1) {
    error (g, _("source '%s' does not exist (or cannot be read)"), localpath);
    return -1;
  }

  int remote_is_dir = guestfs_is_dir (g, remotedir);
  if (remote_is_dir == -1)
    return -1;

  if (!remote_is_dir) {
    error (g, _("target '%s' is not a directory"), remotedir);
    return -1;
  }

  if (split_path (g, buf, buf_len, localpath, &dirname, &basename) == -1)
    return -1;

  guestfs_int_cmd_add_arg (cmd, "tar");
  if (dirname) {
    guestfs_int_cmd_add_arg (cmd, "-C");
    guestfs_int_cmd_add_arg (cmd, dirname);
  }
  guestfs_int_cmd_add_arg (cmd, "-cf");
  guestfs_int_cmd_add_arg (cmd, "-");
  guestfs_int_cmd_add_arg (cmd, basename);

  guestfs_int_cmd_clear_capture_errors (cmd);

  fd = guestfs_int_cmd_pipe_run (cmd, "r");
  if (fd == -1)
    return -1;

  snprintf (fdbuf, sizeof fdbuf, "/dev/fd/%d", fd);

  r = guestfs_tar_in (g, fdbuf, remotedir);

  if (close (fd) == -1) {
    perrorf (g, "close (tar subprocess)");
    return -1;
  }

  r = guestfs_int_cmd_pipe_wait (cmd);
  if (r == -1)
    return -1;
  if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) {
    CLEANUP_FREE char *errors = guestfs_int_cmd_get_pipe_errors (cmd);
    if (errors == NULL)
      return -1;
    error (g, "tar subprocess failed: %s", errors);
    return -1;
  }

  return 0;
}
Ejemplo n.º 13
0
int
guestfs_impl_copy_out (guestfs_h *g,
                       const char *remotepath, const char *localdir)
{
  struct stat statbuf;
  int r;

  if (stat (localdir, &statbuf) == -1 ||
      ! (S_ISDIR (statbuf.st_mode))) {
    error (g, _("target '%s' is not a directory"), localdir);
    return -1;
  }

  /* If the remote is a file, download it.  If it's a directory,
   * create the directory in localdir first before using tar-out.
   */
  r = guestfs_is_file (g, remotepath);
  if (r == -1)
    return -1;

  if (r == 1) {               /* is file */
    CLEANUP_FREE char *filename = NULL;
    size_t buf_len = strlen (remotepath) + 1;
    CLEANUP_FREE char *buf = safe_malloc (g, buf_len);
    const char *basename;

    if (split_path (g, buf, buf_len, remotepath, NULL, &basename) == -1)
      return -1;

    if (asprintf (&filename, "%s/%s", localdir, basename) == -1) {
      perrorf (g, "asprintf");
      return -1;
    }
    if (guestfs_download (g, remotepath, filename) == -1)
      return -1;
  } else {                    /* not a regular file */
    CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g);
    struct copy_out_child_data data;
    char fdbuf[64];
    int fd;

    r = guestfs_is_dir (g, remotepath);
    if (r == -1)
      return -1;

    if (r == 0) {
      error (g, _("'%s' is not a file or directory"), remotepath);
      return -1;
    }

    size_t buf_len = strlen (remotepath) + 1;
    CLEANUP_FREE char *buf = safe_malloc (g, buf_len);
    const char *basename;
    if (split_path (g, buf, buf_len, remotepath, NULL, &basename) == -1)
      return -1;

    /* RHBZ#845522: If remotepath == "/" then basename would be an empty
     * string.  Replace it with "." so that make_tar_output writes
     * to "localdir/."
     */
    if (STREQ (basename, ""))
      basename = ".";

    data.localdir = localdir;
    data.basename = basename;

    guestfs_int_cmd_set_child_callback (cmd, &child_setup, &data);

    guestfs_int_cmd_add_arg (cmd, "tar");
    guestfs_int_cmd_add_arg (cmd, "-xf");
    guestfs_int_cmd_add_arg (cmd, "-");

    guestfs_int_cmd_clear_capture_errors (cmd);

    fd = guestfs_int_cmd_pipe_run (cmd, "w");
    if (fd == -1)
      return -1;

    snprintf (fdbuf, sizeof fdbuf, "/dev/fd/%d", fd);

    r = guestfs_tar_out (g, remotepath, fdbuf);

    if (close (fd) == -1) {
      perrorf (g, "close (tar-output subprocess)");
      return -1;
    }

    r = guestfs_int_cmd_pipe_wait (cmd);
    if (r == -1)
      return -1;
    if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) {
      CLEANUP_FREE char *errors = guestfs_int_cmd_get_pipe_errors (cmd);
      if (errors == NULL)
        return -1;
      error (g, "tar subprocess failed: %s", errors);
      return -1;
    }
  }

  return 0;
}
Ejemplo n.º 14
0
static int
disk_create_qcow2 (guestfs_h *g, const char *orig_filename, int64_t size,
                   const char *backingfile,
                   const struct guestfs_disk_create_argv *optargs)
{
  CLEANUP_FREE char *filename = NULL;
  const char *backingformat = NULL;
  const char *preallocation = NULL;
  const char *compat = NULL;
  int clustersize = -1;
  CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (optionsv);
  CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g);
  int r;

  /* If the filename is something like "file:foo" then qemu-img will
   * try to interpret that as "foo" in the file:/// protocol.  To
   * avoid that, if the path is relative prefix it with "./" since
   * qemu-img won't try to interpret such a path.
   */
  if (orig_filename[0] != '/')
    filename = safe_asprintf (g, "./%s", orig_filename);
  else
    filename = safe_strdup (g, orig_filename);

  if (optargs->bitmask & GUESTFS_DISK_CREATE_BACKINGFORMAT_BITMASK) {
    backingformat = optargs->backingformat;
    /* Conservative whitelist.  This can be extended with other
     * valid formats as required.
     */
    if (STRNEQ (backingformat, "raw") &&
        STRNEQ (backingformat, "qcow2") &&
        STRNEQ (backingformat, "vmdk")) {
      error (g, _("invalid value for backingformat parameter '%s'"),
             backingformat);
      return -1;
    }
  }
  if (optargs->bitmask & GUESTFS_DISK_CREATE_PREALLOCATION_BITMASK) {
    if (STREQ (optargs->preallocation, "off") ||
        STREQ (optargs->preallocation, "sparse"))
      preallocation = "off";
    else if (STREQ (optargs->preallocation, "metadata"))
      preallocation = "metadata";
    else if (STREQ (optargs->preallocation, "full"))
      /* Ugh: https://lists.gnu.org/archive/html/qemu-devel/2014-08/msg03863.html */
      preallocation = "falloc";
    else {
      error (g, _("invalid value for preallocation parameter '%s'"),
             preallocation);
      return -1;
    }
  }
  if (optargs->bitmask & GUESTFS_DISK_CREATE_COMPAT_BITMASK) {
    compat = optargs->compat;
    if (STRNEQ (compat, "0.10") && STRNEQ (compat, "1.1")) {
      error (g, _("invalid value for compat parameter '%s'"), compat);
      return -1;
    }
  }
  if (optargs->bitmask & GUESTFS_DISK_CREATE_CLUSTERSIZE_BITMASK) {
    clustersize = optargs->clustersize;
    if (clustersize < 512 || clustersize > 2097152 ||
        !is_power_of_2 ((unsigned) clustersize)) {
      error (g, _("invalid value for clustersize parameter '%d'"),
             clustersize);
      return -1;
    }
  }

  /* Assemble the qemu-img command line. */
  guestfs_int_cmd_add_arg (cmd, "qemu-img");
  guestfs_int_cmd_add_arg (cmd, "create");
  guestfs_int_cmd_add_arg (cmd, "-f");
  guestfs_int_cmd_add_arg (cmd, "qcow2");

  /* -o parameter. */
  if (backingfile) {
    CLEANUP_FREE char *p = qemu_escape_param (g, backingfile);
    guestfs_int_add_sprintf (g, &optionsv, "backing_file=%s", p);
  }
  if (backingformat)
    guestfs_int_add_sprintf (g, &optionsv, "backing_fmt=%s", backingformat);
  if (preallocation)
    guestfs_int_add_sprintf (g, &optionsv, "preallocation=%s", preallocation);
  if (compat)
    guestfs_int_add_sprintf (g, &optionsv, "compat=%s", compat);
  if (clustersize >= 0)
    guestfs_int_add_sprintf (g, &optionsv, "cluster_size=%d", clustersize);
  guestfs_int_end_stringsbuf (g, &optionsv);

  if (optionsv.size > 1) {
    CLEANUP_FREE char *options = guestfs_int_join_strings (",", optionsv.argv);
    guestfs_int_cmd_add_arg (cmd, "-o");
    guestfs_int_cmd_add_arg (cmd, options);
  }

  /* Complete the command line. */
  guestfs_int_cmd_add_arg (cmd, filename);
  if (size >= 0)
    guestfs_int_cmd_add_arg_format (cmd, "%" PRIi64, size);

  r = guestfs_int_cmd_run (cmd);
  if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) {
    guestfs_int_external_command_failed (g, r, "qemu-img", orig_filename);
    return -1;
  }

  return 0;
}