Ejemplo n.º 1
0
void
prep_postlaunch_lv (const char *filename, prep_data *data, const char *device)
{
  if (guestfs_part_disk (g, device, data->params[2]) == -1)
    prep_error (data, filename, _("failed to partition disk: %s"),
                guestfs_last_error (g));

  CLEANUP_FREE char *vg;
  CLEANUP_FREE char *lv;
  if (vg_lv_parse (data->params[0], &vg, &lv) == -1)
    prep_error (data, filename, _("incorrect format for LV name, use '/dev/VG/LV'"));

  CLEANUP_FREE char *part;
  if (asprintf (&part, "%s1", device) == -1) {
    perror ("asprintf");
    exit (EXIT_FAILURE);
  }

  if (guestfs_pvcreate (g, part) == -1)
    prep_error (data, filename, _("failed to create PV: %s: %s"),
                part, guestfs_last_error (g));

  char *parts[] = { part, NULL };
  if (guestfs_vgcreate (g, vg, parts) == -1)
    prep_error (data, filename, _("failed to create VG: %s: %s"),
                vg, guestfs_last_error (g));

  /* Create the largest possible LV. */
  if (guestfs_lvcreate_free (g, lv, vg, 100) == -1)
    prep_error (data, filename, _("failed to create LV: /dev/%s/%s: %s"),
                vg, lv, guestfs_last_error (g));
}
Ejemplo n.º 2
0
void
prep_postlaunch_bootrootlv (const char *filename, prep_data *data, const char *device)
{
  off_t bootsize;
  if (parse_size (data->params[4], &bootsize) == -1)
    prep_error (data, filename, _("could not parse boot size"));

  const int sector = guestfs_blockdev_getss (g, device);
  if (sector == -1)
    prep_error (data, filename, _("failed to get sector size of disk: %s"),
                guestfs_last_error (g));

  if (guestfs_part_init (g, device, data->params[5]) == -1)
    prep_error (data, filename, _("failed to partition disk: %s"),
                guestfs_last_error (g));

  off_t lastbootsect = 64 + bootsize/sector - 1;
  if (guestfs_part_add (g, device, "primary", 64, lastbootsect) == -1)
    prep_error (data, filename, _("failed to add boot partition: %s"),
                guestfs_last_error (g));

  if (guestfs_part_add (g, device, "primary", lastbootsect+1, -64) == -1)
    prep_error (data, filename, _("failed to add root partition: %s"),
                guestfs_last_error (g));

  CLEANUP_FREE char *vg;
  CLEANUP_FREE char *lv;
  if (vg_lv_parse (data->params[0], &vg, &lv) == -1)
    prep_error (data, filename, _("incorrect format for LV name, use '/dev/VG/LV'"));

  CLEANUP_FREE char *part;
  if (asprintf (&part, "%s1", device) == -1)
    error (EXIT_FAILURE, errno, "asprintf");
  if (guestfs_mkfs (g, data->params[1], part) == -1)
    prep_error (data, filename, _("failed to create boot filesystem: %s"),
                guestfs_last_error (g));

  CLEANUP_FREE char *part2;
  if (asprintf (&part2, "%s2", device) == -1)
    error (EXIT_FAILURE, errno, "asprintf");
  if (guestfs_pvcreate (g, part2) == -1)
    prep_error (data, filename, _("failed to create PV: %s: %s"),
                part2, guestfs_last_error (g));

  char *parts[] = { part2, NULL };
  if (guestfs_vgcreate (g, vg, parts) == -1)
    prep_error (data, filename, _("failed to create VG: %s: %s"),
                vg, guestfs_last_error (g));

  /* Create the largest possible LV. */
  if (guestfs_lvcreate_free (g, lv, vg, 100) == -1)
    prep_error (data, filename, _("failed to create LV: /dev/%s/%s: %s"),
                vg, lv, guestfs_last_error (g));

  if (guestfs_mkfs (g, data->params[2], data->params[0]) == -1)
    prep_error (data, filename, _("failed to create root filesystem: %s"),
                guestfs_last_error (g));
}
Ejemplo n.º 3
0
void
prep_postlaunch_fs (const char *filename, prep_data *data, const char *device)
{
  if (guestfs_part_disk (g, device, data->params[2]) == -1)
    prep_error (data, filename, _("failed to partition disk: %s"),
                guestfs_last_error (g));

  CLEANUP_FREE char *part;
  if (asprintf (&part, "%s1", device) == -1)
    error (EXIT_FAILURE, errno, "asprintf");

  if (guestfs_mkfs (g, data->params[0], part) == -1)
    prep_error (data, filename, _("failed to create filesystem (%s): %s"),
                data->params[0], guestfs_last_error (g));
}
Ejemplo n.º 4
0
void
prep_postlaunch_part (const char *filename, prep_data *data, const char *device)
{
  if (guestfs_part_disk (g, device, data->params[1]) == -1)
    prep_error (data, filename, _("failed to partition disk: %s"),
                guestfs_last_error (g));
}
Ejemplo n.º 5
0
int
main (int argc, char *argv[])
{
  guestfs_h *g;
  size_t i;
  int lengths[] = { 0, 1, 1024,
                    GUESTFS_ERROR_LEN-2, GUESTFS_ERROR_LEN-1,
                    GUESTFS_ERROR_LEN, GUESTFS_ERROR_LEN+1,
                    GUESTFS_ERROR_LEN+2,
                    GUESTFS_ERROR_LEN*2, -1 };
  char len_s[64];
  char *args[2];

  g = guestfs_create ();
  if (g == NULL) {
    perror ("guestfs_create");
    exit (EXIT_FAILURE);
  }

  if (guestfs_add_drive (g, "/dev/null") == -1)
    exit (EXIT_FAILURE);

  if (guestfs_launch (g) == -1)
    exit (EXIT_FAILURE);

  guestfs_push_error_handler (g, NULL, NULL);

  for (i = 0; lengths[i] != -1; ++i) {
    snprintf (len_s, sizeof len_s, "%d", lengths[i]);
    args[0] = len_s;
    args[1] = NULL;

    if (guestfs_debug (g, "error", args) != NULL) {
      fprintf (stderr, "%s: unexpected return value from 'debug error'\n",
               argv[0]);
      exit (EXIT_FAILURE);
    }
    /* EROFS is a magic value returned by debug_error in the daemon. */
    if (guestfs_last_errno (g) != EROFS) {
      fprintf (stderr, "%s: unexpected error from 'debug error': %s\n",
               argv[0], guestfs_last_error (g));
      exit (EXIT_FAILURE);
    }
    /* else OK */
  }

  guestfs_pop_error_handler (g);
  guestfs_close (g);
  exit (EXIT_SUCCESS);
}
Ejemplo n.º 6
0
void
ocaml_guestfs_raise_error (guestfs_h *g, const char *func)
{
  CAMLparam0 ();
  CAMLlocal1 (v);
  const char *msg;

  msg = guestfs_last_error (g);

  if (msg)
    v = caml_copy_string (msg);
  else
    v = caml_copy_string (func);
  caml_raise_with_arg (*caml_named_value ("ocaml_guestfs_error"), v);
  CAMLnoreturn;
}
Ejemplo n.º 7
0
PyObject *
py_guestfs_set_event_callback (PyObject *self, PyObject *args)
{
  PyObject *py_g;
  guestfs_h *g;
  PyObject *py_callback;
  unsigned PY_LONG_LONG events;
  int eh;
  PyObject *py_eh;
  char key[64];

  if (!PyArg_ParseTuple (args, (char *) "OOK:guestfs_set_event_callback",
                         &py_g, &py_callback, &events))
    return NULL;

  if (!PyCallable_Check (py_callback)) {
    PyErr_SetString (PyExc_TypeError,
                     "callback parameter is not callable "
                     "(eg. lambda or function)");
    return NULL;
  }

  g = get_handle (py_g);

  eh = guestfs_set_event_callback (g, py_guestfs_event_callback_wrapper,
                                   events, 0, py_callback);
  if (eh == -1) {
    PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g));
    return NULL;
  }

  /* Increase the refcount for this callback since we are storing it
   * in the opaque C libguestfs handle.  We need to remember that we
   * did this, so we can decrease the refcount for all undeleted
   * callbacks left around at close time (see py_guestfs_close).
   */
  Py_XINCREF (py_callback);

  snprintf (key, sizeof key, "_python_event_%d", eh);
  guestfs_set_private (g, key, py_callback);

  py_eh = PyLong_FromLong ((long) eh);
  return py_eh;
}
Ejemplo n.º 8
0
JNIEXPORT jint JNICALL
Java_com_redhat_et_libguestfs_GuestFS__1set_1event_1callback
  (JNIEnv *env, jobject obj, jlong jg, jobject jcallback, jlong jevents)
{
  guestfs_h *g = (guestfs_h *) (long) jg;
  int r;
  struct callback_data *data;
  jclass callback_class;
  jmethodID method;
  char key[64];

  callback_class = (*env)->GetObjectClass (env, jcallback);
  method = (*env)->GetMethodID (env, callback_class, METHOD_NAME, METHOD_SIGNATURE);
  if (method == 0) {
    throw_exception (env, "GuestFS.set_event_callback: callback class does not implement the EventCallback interface");
    return -1;
  }

  data = malloc (sizeof *data);
  if (data == NULL) {
    throw_out_of_memory (env, "malloc");
    return -1;
  }
  (*env)->GetJavaVM (env, &data->jvm);
  data->method = method;

  r = guestfs_set_event_callback (g, java_callback,
                                  (uint64_t) jevents, 0, data);
  if (r == -1) {
    free (data);
    throw_exception (env, guestfs_last_error (g));
    return -1;
  }

  /* Register jcallback as a global reference so the GC won't free it. */
  data->callback = (*env)->NewGlobalRef (env, jcallback);

  /* Store 'data' in the handle, so we can free it at some point. */
  snprintf (key, sizeof key, "_java_event_%d", r);
  guestfs_set_private (g, key, data);

  return (jint) r;
}
Ejemplo n.º 9
0
void
prep_postlaunch_bootroot (const char *filename, prep_data *data, const char *device)
{
  off_t bootsize;
  if (parse_size (data->params[3], &bootsize) == -1)
    prep_error (data, filename, _("could not parse boot size"));

  int sector = guestfs_blockdev_getss (g, device);
  if (sector == -1)
    prep_error (data, filename, _("failed to get sector size of disk: %s"),
                guestfs_last_error (g));

  if (guestfs_part_init (g, device, data->params[4]) == -1)
    prep_error (data, filename, _("failed to partition disk: %s"),
                guestfs_last_error (g));

  off_t lastbootsect = 64 + bootsize/sector - 1;
  if (guestfs_part_add (g, device, "primary", 64, lastbootsect) == -1)
    prep_error (data, filename, _("failed to add boot partition: %s"),
                guestfs_last_error (g));

  if (guestfs_part_add (g, device, "primary", lastbootsect+1, -64) == -1)
    prep_error (data, filename, _("failed to add root partition: %s"),
                guestfs_last_error (g));

  CLEANUP_FREE char *part;
  if (asprintf (&part, "%s1", device) == -1) {
    perror ("asprintf");
    exit (EXIT_FAILURE);
  }
  if (guestfs_mkfs (g, data->params[0], part) == -1)
    prep_error (data, filename, _("failed to create boot filesystem: %s"),
                guestfs_last_error (g));

  CLEANUP_FREE char *part2;
  if (asprintf (&part2, "%s2", device) == -1) {
    perror ("asprintf");
    exit (EXIT_FAILURE);
  }
  if (guestfs_mkfs (g, data->params[1], part2) == -1)
    prep_error (data, filename, _("failed to create root filesystem: %s"),
                guestfs_last_error (g));
}
Ejemplo n.º 10
0
static void *
start_thread (void *vi)
{
  guestfs_h *g;
  int r, thread_id = *(int *)vi;
  const char *error;

  g = guestfs_create ();
  if (g == NULL) {
    perror ("guestfs_create");
    *(int *)vi = -1;
    pthread_exit (vi);
  }

  if (guestfs_add_drive_opts (g, "/dev/null",
                              GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw",
                              GUESTFS_ADD_DRIVE_OPTS_READONLY, 1,
                              -1) == -1) {
    *(int *)vi = -1;
    pthread_exit (vi);
  }

  /* Fake out qemu. */
  if (guestfs_set_qemu (g, "/bin/true") == -1) {
    *(int *)vi = -1;
    pthread_exit (vi);
  }

  /* Wait for the other threads to finish starting up. */
  r = pthread_barrier_wait (&barrier);
  if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) {
    fprintf (stderr, "pthread_barrier_wait: [thread %d]: %s\n",
             thread_id, strerror (r));
    *(int *)vi = -1;
    pthread_exit (vi);
  }

  /* Launch the handle.  Because of the faked out qemu, we expect this
   * will fail with "child process died unexpectedly".  We are
   * interested in other failures.
   */
  guestfs_push_error_handler (g, NULL, NULL);
  r = guestfs_launch (g);
  error = guestfs_last_error (g);

  if (r == 0) { /* This should NOT happen. */
    fprintf (stderr, "rhbz790721: [thread %d]: "
             "strangeness in test: expected launch to fail, but it didn't!\n",
             thread_id);
    *(int *)vi = -1;
    pthread_exit (vi);
  }

  if (error == NULL) { /* This also should NOT happen. */
    fprintf (stderr, "rhbz790721: [thread %d]: "
             "strangeness in test: no error message!\n",
             thread_id);
    *(int *)vi = -1;
    pthread_exit (vi);
  }

  /* If this happens, it indicates a bug/race in the appliance
   * building code which is what this regression test is designed to
   * spot.
   */
  if (STRNEQ (error, "child process died unexpectedly")) {
    fprintf (stderr, "rhbz790721: [thread %d]: error: %s\n", thread_id, error);
    *(int *)vi = -1;
    pthread_exit (vi);
  }

  guestfs_pop_error_handler (g);

  /* Close the handle. */
  guestfs_close (g);

  *(int *)vi = 0;
  pthread_exit (vi);
}
Ejemplo n.º 11
0
static int
do_make_fs (const char *input, const char *output_str)
{
  const char *dev, *options;
  CLEANUP_UNLINK_FREE char *output = NULL;
  uint64_t estimate, size;
  struct guestfs_disk_create_argv optargs;
  CLEANUP_FREE char *ifmt = NULL;
  CLEANUP_FREE char *ifile = NULL;
  pid_t pid;
  int status, fd;

  /* Use of CLEANUP_UNLINK_FREE *output ensures the output file is
   * deleted unless we successfully reach the end of this function.
   */
  output = strdup (output_str);
  if (output == NULL) {
    perror ("strdup");
    return -1;
  }

  /* Input.  What is it?  Estimate how much space it will need. */
  if (estimate_input (input, &estimate, &ifmt) == -1)
    return -1;

  if (verbose) {
    fprintf (stderr, "input format = %s\n", ifmt);
    fprintf (stderr, "estimate = %" PRIu64 " bytes "
             "(%" PRIu64 " 1K blocks, %" PRIu64 " 4K blocks)\n",
             estimate, estimate / 1024, estimate / 4096);
  }

  estimate += 256 * 1024;       /* For superblocks &c. */

  if (STRPREFIX (type, "ext") && type[3] >= '3') {
    /* For ext3+, add some more for the journal. */
    estimate += 1024 * 1024;
  }

  else if (STREQ (type, "ntfs")) {
    estimate += 4 * 1024 * 1024; /* NTFS journal. */
  }

  else if (STREQ (type, "btrfs")) {
    /* For BTRFS, the minimum metadata allocation is 256MB, with data
     * additional to that.  Note that we disable data and metadata
     * duplication below.
     */
    estimate += 256 * 1024 * 1024;
  }

  /* Add 10%, see above. */
  estimate *= 1.10;

  /* Calculate the output size. */
  if (size_str == NULL)
    size = estimate;
  else
    if (parse_size (size_str, estimate, &size) == -1)
      return -1;

  /* Create the output disk. */
  optargs.bitmask = 0;
  if (STREQ (format, "qcow2")) {
    optargs.bitmask |= GUESTFS_DISK_CREATE_PREALLOCATION_BITMASK;
    optargs.preallocation = "metadata";
  }
  if (guestfs_disk_create_argv (g, output, format, size, &optargs) == -1)
    return -1;

  if (guestfs_add_drive_opts (g, output,
                              GUESTFS_ADD_DRIVE_OPTS_FORMAT, format,
                              -1) == -1)
    return -1;

  if (guestfs_launch (g) == -1)
    return -1;

  if (check_ntfs_available () == -1)
    return -1;

  /* Partition the disk. */
  dev = "/dev/sda";
  if (partition) {
    int mbr_id = 0;

    if (STREQ (partition, ""))
      partition = "mbr";

    if (guestfs_part_disk (g, dev, partition) == -1)
      return -1;

    dev = "/dev/sda1";

    /* Set the partition type byte if it's MBR and the filesystem type
     * is one that we know about.
     */
    if (STREQ (partition, "mbr") || STREQ (partition, "msdos")) {
      if (STREQ (type, "msdos"))
        /* According to Wikipedia.  However I have not actually tried this. */
        mbr_id = 0x1;
      else if (STREQ (type, "vfat") || STREQ (type, "fat"))
        mbr_id = 0xb;
      else if (STREQ (type, "ntfs"))
        mbr_id = 0x7;
      else if (STRPREFIX (type, "ext"))
        mbr_id = 0x83;
      else if (STREQ (type, "minix"))
        mbr_id = 0x81;
    }
    if (mbr_id != 0) {
      if (guestfs_part_set_mbr_id (g, "/dev/sda", 1, mbr_id) == -1)
        return -1;
    }
  }

  if (verbose)
    fprintf (stderr, "creating %s filesystem on %s ...\n", type, dev);

  /* Create the filesystem. */
  if (STRNEQ (type, "btrfs")) {
    int r;
    struct guestfs_mkfs_opts_argv optargs = { .bitmask = 0 };

    if (label) {
      optargs.label = label;
      optargs.bitmask |= GUESTFS_MKFS_OPTS_LABEL_BITMASK;
    }

    guestfs_push_error_handler (g, NULL, NULL);
    r = guestfs_mkfs_opts_argv (g, type, dev, &optargs);
    guestfs_pop_error_handler (g);

    if (r == -1) {
      /* Provide more guidance in the error message (RHBZ#823883). */
      fprintf (stderr, "%s: 'mkfs' (create filesystem) operation failed: %s\n",
               guestfs_int_program_name, guestfs_last_error (g));
      if (STREQ (type, "fat"))
        fprintf (stderr, "Instead of 'fat', try 'vfat' (long filenames) or 'msdos' (short filenames).\n");
      else
        fprintf (stderr, "Is '%s' a correct filesystem type?\n", type);

      return -1;
    }
  }
  else {
Ejemplo n.º 12
0
static void
test_virtio_serial (void)
{
  int fd, r, eh;
  char tmpfile[] = "/tmp/speedtestXXXXXX";
  struct sigaction sa, old_sa;

  if (!virtio_serial_upload && !virtio_serial_download)
    return;

  /* Create a sparse file.  We could upload from /dev/zero, but we
   * won't get progress messages because libguestfs tests if the
   * source file is a regular file.
   */
  fd = mkstemp (tmpfile);
  if (fd == -1)
    error (EXIT_FAILURE, errno, "mkstemp: %s", tmpfile);
  if (ftruncate (fd, TEST_SERIAL_MAX_SIZE) == -1)
    error (EXIT_FAILURE, errno, "ftruncate");
  if (close (fd) == -1)
    error (EXIT_FAILURE, errno, "close");

  g = guestfs_create ();
  if (!g)
    error (EXIT_FAILURE, errno, "guestfs_create");

  if (guestfs_add_drive_scratch (g, INT64_C (100*1024*1024), -1) == -1)
    exit (EXIT_FAILURE);

  if (guestfs_launch (g) == -1)
    exit (EXIT_FAILURE);

  /* Make and mount a filesystem which will be used by the download test. */
  if (guestfs_mkfs (g, "ext4", "/dev/sda") == -1)
    exit (EXIT_FAILURE);
  if (guestfs_mount (g, "/dev/sda", "/") == -1)
    exit (EXIT_FAILURE);

  /* Time out the upload after TEST_SERIAL_MAX_TIME seconds have passed. */
  memset (&sa, 0, sizeof sa);
  sa.sa_handler = stop_transfer;
  sa.sa_flags = SA_RESTART;
  sigaction (SIGALRM, &sa, &old_sa);

  /* Get progress messages, which will tell us how much data has been
   * transferred.
   */
  eh = guestfs_set_event_callback (g, progress_cb, GUESTFS_EVENT_PROGRESS,
                                   0, NULL);
  if (eh == -1)
    exit (EXIT_FAILURE);

  if (virtio_serial_upload) {
    gettimeofday (&start, NULL);
    rate = -1;
    operation = "upload";
    alarm (max_time_override > 0 ? max_time_override : TEST_SERIAL_MAX_TIME);

    /* For the upload test, upload the sparse file to /dev/null in the
     * appliance.  Hopefully this is mostly testing just virtio-serial.
     */
    guestfs_push_error_handler (g, NULL, NULL);
    r = guestfs_upload (g, tmpfile, "/dev/null");
    alarm (0);
    unlink (tmpfile);
    guestfs_pop_error_handler (g);

    /* It's possible that the upload will finish before the alarm fires,
     * or that the upload will be stopped by the alarm.
     */
    if (r == -1 && guestfs_last_errno (g) != EINTR) {
      fprintf (stderr,
               "%s: expecting upload command to return EINTR\n%s\n",
               guestfs_int_program_name, guestfs_last_error (g));
      exit (EXIT_FAILURE);
    }

    if (rate == -1) {
    rate_error:
      fprintf (stderr, "%s: internal error: progress callback was not called! (r=%d, errno=%d)\n",
               guestfs_int_program_name,
               r, guestfs_last_errno (g));
      exit (EXIT_FAILURE);
    }

    print_rate ("virtio-serial upload rate:", rate);
  }

  if (virtio_serial_download) {
    /* For the download test, download a sparse file within the
     * appliance to /dev/null on the host.
     */
    if (guestfs_touch (g, "/sparse") == -1)
      exit (EXIT_FAILURE);
    if (guestfs_truncate_size (g, "/sparse", TEST_SERIAL_MAX_SIZE) == -1)
      exit (EXIT_FAILURE);

    gettimeofday (&start, NULL);
    rate = -1;
    operation = "download";
    alarm (max_time_override > 0 ? max_time_override : TEST_SERIAL_MAX_TIME);
    guestfs_push_error_handler (g, NULL, NULL);
    r = guestfs_download (g, "/sparse", "/dev/null");
    alarm (0);
    guestfs_pop_error_handler (g);

    if (r == -1 && guestfs_last_errno (g) != EINTR) {
      fprintf (stderr,
               "%s: expecting download command to return EINTR\n%s\n",
               guestfs_int_program_name, guestfs_last_error (g));
      exit (EXIT_FAILURE);
    }

    if (rate == -1)
      goto rate_error;

    print_rate ("virtio-serial download rate:", rate);
  }

  if (guestfs_shutdown (g) == -1)
    exit (EXIT_FAILURE);

  guestfs_close (g);

  /* Restore SIGALRM signal handler. */
  sigaction (SIGALRM, &old_sa, NULL);
}
Ejemplo n.º 13
0
static void
do_output_filesystems (void)
{
  size_t i;

  CLEANUP_FREE_STRING_LIST char **fses = guestfs_list_filesystems (g);
  if (fses == NULL)
    exit (EXIT_FAILURE);

  for (i = 0; fses[i] != NULL; i += 2) {
    CLEANUP_FREE char *dev = NULL, *vfs_label = NULL, *vfs_uuid = NULL;
    CLEANUP_FREE_STRING_LIST char **parents = NULL;
    int64_t size = -1;

    /* Skip swap and unknown, unless --extra flag was given. */
    if (!(output & OUTPUT_FILESYSTEMS_EXTRA) &&
        (STREQ (fses[i+1], "swap") || STREQ (fses[i+1], "unknown")))
      continue;

    dev = guestfs_canonical_device_name (g, fses[i]);
    if (dev == NULL)
      exit (EXIT_FAILURE);

    /* Only bother to look these up if we will be displaying them,
     * otherwise pass them as NULL.
     */
    if ((columns & COLUMN_VFS_LABEL)) {
      guestfs_push_error_handler (g, NULL, NULL);
      vfs_label = guestfs_vfs_label (g, fses[i]);
      guestfs_pop_error_handler (g);
      if (vfs_label == NULL) {
        vfs_label = strdup ("");
        if (!vfs_label)
          error (EXIT_FAILURE, errno, "strdup");
      }
    }
    if ((columns & COLUMN_UUID)) {
      guestfs_push_error_handler (g, NULL, NULL);
      vfs_uuid = guestfs_vfs_uuid (g, fses[i]);
      guestfs_pop_error_handler (g);
      if (vfs_uuid == NULL) {
        vfs_uuid = strdup ("");
        if (!vfs_uuid)
          error (EXIT_FAILURE, errno, "strdup");
      }
    }
    if ((columns & COLUMN_SIZE)) {
      CLEANUP_FREE char *device = guestfs_mountable_device (g, fses[i]);
      CLEANUP_FREE char *subvolume = NULL;

      guestfs_push_error_handler (g, NULL, NULL);

      subvolume = guestfs_mountable_subvolume (g, fses[i]);
      if (subvolume == NULL && guestfs_last_errno (g) != EINVAL) {
        fprintf (stderr,
                 _("%s: cannot determine the subvolume for %s: %s: %s\n"),
                getprogname (), fses[i],
                guestfs_last_error (g),
                strerror (guestfs_last_errno (g)));
        exit (EXIT_FAILURE);
      }

      guestfs_pop_error_handler (g);

      if (!device || !subvolume) {
        size = guestfs_blockdev_getsize64 (g, fses[i]);
        if (size == -1)
          exit (EXIT_FAILURE);
      }
    }

    if (is_md (fses[i]))
      parents = parents_of_md (fses[i]);
    else
      parents = no_parents ();

    write_row (dev, "filesystem",
               fses[i+1], vfs_label, -1, size, parents, vfs_uuid);
  }
}