[Libguestfs] [PATCH 6/6] New API: btrfs_subvolume_show

Pino Toscano ptoscano at redhat.com
Fri Nov 21 16:17:13 UTC 2014


On Friday 21 November 2014 13:18:00 Hu Tao wrote:
> btrfs_subvolume_show shows the detailed information of a subvolume or
> snapshot.
> 
> Signed-off-by: Hu Tao <hutao at cn.fujitsu.com>
> ---
>  daemon/btrfs.c       | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  generator/actions.ml |   9 +++
>  src/MAX_PROC_NR      |   2 +-
>  3 files changed, 177 insertions(+), 1 deletion(-)
> 
> diff --git a/daemon/btrfs.c b/daemon/btrfs.c
> index e179b57..36ba588 100644
> --- a/daemon/btrfs.c
> +++ b/daemon/btrfs.c
> @@ -29,6 +29,7 @@
>  #include "actions.h"
>  #include "optgroups.h"
>  #include "xstrtol.h"
> +#include "c-ctype.h"
>  
>  GUESTFSD_EXT_CMD(str_btrfs, btrfs);
>  GUESTFSD_EXT_CMD(str_btrfstune, btrfstune);
> @@ -813,3 +814,169 @@ do_btrfs_fsck (const char *device, int64_t superblock, int repair)
>  
>    return 0;
>  }
> +
> +char **do_btrfs_subvolume_show (const char *subvolume)
> +{
> +  const size_t MAX_ARGS = 64;
> +  const char *argv[MAX_ARGS];
> +  size_t i = 0;
> +  CLEANUP_FREE char *subvolume_buf = NULL;
> +  CLEANUP_FREE char *err = NULL;
> +  CLEANUP_FREE char *out = NULL;
> +  char *p, *pend, *colon;
> +  DECLARE_STRINGSBUF (ret);
> +  int r;
> +
> +  subvolume_buf = sysroot_path (subvolume);
> +  if (subvolume_buf == NULL) {
> +    reply_with_perror ("malloc");
> +    return NULL;
> +  }
> +
> +  ADD_ARG (argv, i, str_btrfs);
> +  ADD_ARG (argv, i, "subvolume");
> +  ADD_ARG (argv, i, "show");
> +  ADD_ARG (argv, i, subvolume_buf);
> +  ADD_ARG (argv, i, NULL);
> +
> +  r = commandv (&out, &err, argv);
> +  if (r == -1) {
> +    reply_with_error ("%s: %s", subvolume, err);
> +    return NULL;
> +  }
> +
> +  /* Output is:
> +   *
> +   * /
> +   *         Name:                   root
> +   *         uuid:                   c875169e-cf4e-a04d-9959-b667dec36234
> +   *         Parent uuid:            -
> +   *         Creation time:          2014-11-13 10:13:08
> +   *         Object ID:              256
> +   *         Generation (Gen):       6579
> +   *         Gen at creation:        5
> +   *         Parent:                 5
> +   *         Top Level:              5
> +   *         Flags:                  -
> +   *         Snapshot(s):
> +   *                                 snapshots/test1
> +   *                                 snapshots/test2
> +   *                                 snapshots/test3
> +   *
> +   */
> +
> +  p = out;
> +
> +  p = strchr (p, '\n');
> +  if (p) {
> +    *p = '\0';
> +    p++;
> +  }
> +  else {

Minor style note:
  } else {

(also in other parts of this patch)

> +    reply_with_error ("truncated output");

I'd print out anyway, to ease debugging failures a bit.

> +    return NULL;
> +  }
> +
> +  /* If the path is the btrfs root, `btrfs subvolume show' reports:
> +   *   <path> is btrfs root
> +   */
> +  if (strstr(out, "is btrfs root") != NULL) {

Take care of adding a space before parenthesis (also in other parts of
this patch).

> +    reply_with_error ("%s is btrfs root", subvolume);
> +    return NULL;
> +  }
> +
> +  /* The first line is the path of the subvolume. */
> +  if (add_string (&ret, strdup("path")) == -1) {

add_string duplicates the string, so you don't need to do that
manually.

> +    return NULL;
> +  }
> +  if (add_string (&ret, out) == -1) {
> +    return NULL;
> +  }
> +
> +  /* Read the lines and split into "key: value". */
> +  while (*p) {
> +    /* leading spaces and tabs */
> +    while (*p && c_isspace (*p)) p++;

Properly indent such lines, so:
  while (*p && c_isspace (*p))
    p++;

(also, a minor optimization is to use ++p instead of p++, as it can
avoid a temporary value)

> +
> +    pend = strchrnul (p, '\n');
> +    if (*pend == '\n') {
> +      *pend = '\0';
> +      pend++;
> +    }
> +
> +    if (!*p) continue;
> +
> +    colon = strchr (p, ':');
> +    if (colon) {
> +      *colon = '\0';
> +
> +      /* snapshot is special, see the output above */
> +      if (strncmp(p, "Snapshot", sizeof("Snapshot") - 1) == 0) {

Please use the STR* macros we have in src/guestfs-internal-all.h.

Also, most probably it is better to match "Snapshot(s)", so if in the
future a new field starting with "Snapshot" is added then it won't be
mistaken for this multiline one.

> +        char *ss = NULL;
> +
> +        if (add_string (&ret, p) == -1) {
> +          return NULL;
> +        }

Single statements don't need curly brackets (also in other parts of
the patch).

> +
> +        /* each line is the path of a snapshot. */
> +        p = pend;
> +        while (*p && strchr(p, ':') == NULL) {

This will break once the output will have fields after Snapshot, as
strchr(p, ':') will return a non-null value in one of the lines
following the first line after the Snapshot one.

> +          while (*p && c_isspace (*p)) p++;
> +          pend = strchrnul (p, '\n');
> +          if (*pend == '\n') {
> +            *pend = '\0';
> +            pend++;
> +          }

I see this code repeated already; I think it would be better to create
a small helper function like:
  char *analyze_line (char *p, char **field, char **value)
which would
- destructively update p (setting null bytes, just like it is done now)
- return in field the start of the field text
- return in value, if present, the start of the value (after the colon)
- return the pointer to the first character of the new line (or null,
  if none following)
this way it is IMHO easier to iterate over the lines, and also to detect
whether a line contains a field+value or just text.

> +          if (ss) {
> +            ss = realloc(ss, strlen(ss) + strlen(p));

If realloc fails, the return value is null; this means that the
following will leak ss in that case.

Also, sounds like you are missing "," in the length of the string?

> +            strcat(ss, ",");

Just save the length of ss before the realloc, and you can simply do
  ss[old_length_of_ss] = ',';

> +            strcat(ss, p);

You can easily use memcpy here:
  memcpy (ss + old_length_of_ss + 1, p, strlen (p));

> +          } else {
> +            ss = strdup(p);

Missing check for failed strdup.

> +          }
> +
> +          p = pend;
> +        }
> +
> +        if (ss) {
> +          if (add_string (&ret, ss) == -1) {

This needs to be add_string_nodup.

> +            free(ss);

No need to free ss here; theoretically add_string will take care of it,
be it added to the stringbuf or freed on error.

> +            return NULL;
> +          }
> +        }
> +        else {
> +          if (add_string (&ret, strdup("")) == -1) {
> +            return NULL;
> +          }
> +        }

If there are no elements for "Snapshot(s)", then it would be better
to just not add an empty hash element.

> +
> +        continue;
> +      }
> +
> +      do { colon++; } while (*colon && c_isspace (*colon));
> +      if (add_string (&ret, p) == -1) {
> +        return NULL;
> +      }
> +      if (add_string (&ret, colon) == -1) {
> +        return NULL;
> +      }

At least from the example output added as comment, it seems that
"not available" values might be "-" -- it'd just not add those, since
as users of this API would still need to check for the existency of
a certain key in the returned hash.

> +    }
> +    else {
> +      if (add_string (&ret, p) == -1) {
> +        return NULL;
> +      }
> +      if (add_string (&ret, "") == -1) {
> +        return NULL;
> +      }

As above, I would not add elements with empty values in the hash.

> +    }
> +
> +    p = pend;
> +  }
> +
> +  if (end_stringsbuf (&ret) == -1)
> +    return NULL;
> +
> +  return ret.argv;
> +
> +}
> diff --git a/generator/actions.ml b/generator/actions.ml
> index cf96039..24d3ecc 100644
> --- a/generator/actions.ml
> +++ b/generator/actions.ml
> @@ -12014,6 +12014,15 @@ This is the internal call which implements C<guestfs_lstatnslist>." };
>      longdesc = "\
>  Get the default subvolume or snapshot of a filesystem mounted at C<mountpoint>." };
>  
> +  { defaults with
> +    name = "btrfs_subvolume_show";
> +    style = RHashtable "btrfssubvolumeinfo", [Pathname "subvolume"], [];
> +    proc_nr = Some 425;
> +    optional = Some "btrfs"; camel_name = "BTRFSSubvolumeShow";
> +    shortdesc = "show detailed information of the subvolume";

"returns detailed information about the subvolume"

> +    longdesc = "\
> +show detailed information of the subvolume." };

Like above, making sure it is properly capitalized at the beginning.

-- 
Pino Toscano




More information about the Libguestfs mailing list