[libvirt] [PATCH v2 2/6] logical: Fix parsing of the 'devices' field lvs output

Pavel Hrdina phrdina at redhat.com
Mon Feb 1 09:48:18 UTC 2016


On Fri, Jan 29, 2016 at 12:50:06PM -0500, John Ferlan wrote:
> 
> 
> On 01/29/2016 11:07 AM, Pavel Hrdina wrote:
> > On Thu, Jan 28, 2016 at 05:44:05PM -0500, John Ferlan wrote:
> > 
> > [...]
> > 
> >> Signed-off-by: John Ferlan <jferlan at redhat.com>
> >> ---
> >>  src/storage/storage_backend_logical.c | 149 ++++++++++++----------------------
> >>  tests/virstringtest.c                 |  11 +++
> >>  2 files changed, 62 insertions(+), 98 deletions(-)
> >>
> >> diff --git a/src/storage/storage_backend_logical.c b/src/storage/storage_backend_logical.c
> >> index 7c05b6a..3232c08 100644
> >> --- a/src/storage/storage_backend_logical.c
> >> +++ b/src/storage/storage_backend_logical.c
> >> @@ -64,8 +64,6 @@ virStorageBackendLogicalSetActive(virStoragePoolObjPtr pool,
> >>  }
> >>  
> >>  
> >> -#define VIR_STORAGE_VOL_LOGICAL_SEGTYPE_STRIPED "striped"
> >> -
> >>  struct virStorageBackendLogicalPoolVolData {
> >>      virStoragePoolObjPtr pool;
> >>      virStorageVolDefPtr vol;
> >> @@ -75,121 +73,76 @@ static int
> >>  virStorageBackendLogicalParseVolExtents(virStorageVolDefPtr vol,
> >>                                          char **const groups)
> >>  {
> >> -    int nextents, ret = -1;
> >> -    const char *regex_unit = "(\\S+)\\((\\S+)\\)";
> >> -    char *regex = NULL;
> >> -    regex_t *reg = NULL;
> >> -    regmatch_t *vars = NULL;
> >> -    char *p = NULL;
> >> -    size_t i;
> >> -    int err, nvars;
> >> +    int ret = -1;
> >> +    char **extents = NULL;
> >> +    char *extnum, *end;
> >> +    size_t i, nextents = 0;
> >>      unsigned long long offset, size, length;
> >>  
> >> -    nextents = 1;
> >> -    if (STREQ(groups[4], VIR_STORAGE_VOL_LOGICAL_SEGTYPE_STRIPED)) {
> >> -        if (virStrToLong_i(groups[5], NULL, 10, &nextents) < 0) {
> >> -            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> >> -                           _("malformed volume extent stripes value"));
> >> -            goto cleanup;
> >> -        }
> >> -    }
> >> +    /* The 'devices' (or extents) are split by a comma ",", so let's split
> >> +     * each out into a parseable string. Since our regex to generate this
> >> +     * data is "(\\S+)", we can safely assume at least one exists. */
> >> +    if (!(extents = virStringSplitCount(groups[3], ",", 0, &nextents)))
> >> +        goto cleanup;
> > 
> > At first I thought of the same solution but what if the device path contains
> > a comma?  I know, it's very unlikely but it's possible.
> > 
> 
> Well it would have failed with the current code... The existing
> algorithm would concatenate 'nsegments' of 'regex_units' separated by a
> comma [1].

That's not true, regex is greedy and it tries to match as much as possible:

    https://regex101.com/r/lS9tZ5/1

Like I said, the regex is more robust and parse the string correctly event with
comma as part of a device name, for example "/dev/sda,1".

> >> -    if (VIR_STRDUP(regex, regex_unit) < 0)
> >> -        goto cleanup;
> >> -
> >> -    for (i = 1; i < nextents; i++) {
> >> -        if (VIR_REALLOC_N(regex, strlen(regex) + strlen(regex_unit) + 2) < 0)
> >> -            goto cleanup;
> >> -        /* "," is the separator of "devices" field */
> >> -        strcat(regex, ",");
> >> -        strncat(regex, regex_unit, strlen(regex_unit));
> 
> [1]
> 
> So for 2 'nextents' it's:
> 
> "(\\S+)\\((\\S+)\\),(\\S+)\\((\\S+)\\)"
> 
> 
> >> -    }
> > 
> > I would rather allocate the string with correct size outside of the cycle as we
> > know the length and just simply fill the string in a cycle.  The regex is more
> > robust than splitting the string by comma.
> > 
> 
> Not sure I follow the comment here, but I'll point out that part of the
> problem with the existing algorithm is that it "assumes" extents=1 and
> only allows adjustment if the segtype=="striped". This is bad for
> "mirror" now and eventually bad for "thin" later.

Well, I meant something like this instead of the current code:


if (nextents) {
    if (VIR_ALLOC_N(regex, ....)
        goto cleanup;

    strcpy();

    for (....) {
        strcat();
        strncat();
    }
}


> 
> I find usage of regex in here an over complication. There's more agita,
> allocation, possible errors, etc. spent trying to just set up the regex.
> It's one of those - close my eyes and hope it works...
> 
> > [...]
> > 
> >> -
> >> -        vol->source.extents[vol->source.nextent].start = offset * size;
> >> -        vol->source.extents[vol->source.nextent].end = (offset * size) + length;
> >> -        vol->source.nextent++;
> >> +        vol->source.extents[i].start = offset * size;
> >> +        vol->source.extents[i].end = (offset * size) + length;
> > 
> > [1] ... there you would call VIR_APPEND_ELEMENT().
> > 
> 
> I don't find the whole VIR_APPEND_ELEMENT option as clean...  But I'll
> go with it ...
> 
> Usage will require a change to src/conf/storage_conf.h in order to
> change 'nextent' to a 'size_t' (from an 'int') - compiler complains
> otherwise.
> 
> Then remove the VIR_REALLOC_N
> 
> Add a memset(&extent, 0, sizeof(extent)); prior to the for loop and one
> at the end of the for loop after the successful VIR_APPEND_ELEMENT

You don't need to add one after the successful VIR_APPEND_ELEMENT, it does that
for us.  There is a different version called VIR_APPEND_ELEMENT_COPY to not
clean the original element.

In this case, where we know the number of elements, we can pre-allocate the
array and use VIR_APPEND_ELEMENT_INSERT which requires the array to be
allocated.

> 
> Add the following in cleanup due to :
> 
>     if (extent.path)
>         VIR_FREE(extent.path);
> 
> If you'd like to see the adjusted patch - I can post it.

Yes I would like to see the adjusted patch.

> 
> >>      }
> >> -
> >>      ret = 0;
> >>  
> >>   cleanup:
> >> -    VIR_FREE(regex);
> >> -    VIR_FREE(reg);
> >> -    VIR_FREE(vars);
> >> +    virStringFreeList(extents);
> >> +
> >>      return ret;
> > 
> > The cleanup is nice, but still I would rather use VIR_APPEND_ELEMENT as it's
> > more common in libvirt to create a new array of some elements.
> > 
> > One more question about the mirror device name, it's not actually a device name
> > but internal name used by lvm and it's mapped to real device.  Shouldn't we do
> > the job for user and display a real device?
> > 
> 
> Another patch for a different day perhaps...

Yes I know and I'm not asking to fix it together with this patch series.  I just
wanted to note it so there is at least some discussion about it.

> 
> Mirror, raid, and thin lv to "real" device is managed by lvm. I would
> think someone knowing how to use the lvs tools would be able to figure
> that out...
> 
> It's possible to get using an 'lvs -a -o name,devices $vgname' call and
> iterating through, but I'm not sure it'd be worth doing.

Yes, it's probably not worth doing it unless someone asks for it.

Pavel




More information about the libvir-list mailing list