[libvirt] [PATCH 1/5] virstorage: Introduce virStorageAuthDef

John Ferlan jferlan at redhat.com
Wed Jul 2 16:08:44 UTC 2014



On 07/02/2014 08:13 AM, Michal Privoznik wrote:
> On 27.06.2014 17:11, John Ferlan wrote:
>> Introduce virStorageAuthDef and friends.  Future patches will merge/utilize
>> their view of storage source/pool auth/secret definitions.
>>
>> New API's include:
>>      virStorageAuthDefParse:  Parse the "<auth" XML data for either the
>>                               domain disk or storage pool returning a
> 
> If I were to be narrow-minded OCD libvirt developer, I'd point out:
> s,<auth,<auth/>, but since I'm not, I'll leave it :)
> 
>>                               virStorageAuthDefPtr
>>      virStorageAuthDefCopy:   Copy a virStorageAuthDefPtr - to be used by
>>                               the qemuTranslateDiskSourcePoolAuth when it
>>                               copies storage pool auth data into domain
>>                               disk auth data
>>      virStorageAuthDefFormat: Common output of the "<auth" in the domain
>>                               disk or storage pool XML
>>      virStorageAuthDefFree:   Free memory associated with virStorageAuthDef
>>
>> Subsequent patches will utilize the new functions for the domain disk and
>> storage pools.
>>
>> Future work in the hostdev pass through can then make use of common data
>> structures and code.
>>
>> Signed-off-by: John Ferlan <jferlan at redhat.com>
>> ---
>>   src/libvirt_private.syms  |   4 +
>>   src/util/virstoragefile.c | 205 ++++++++++++++++++++++++++++++++++++++++++++++
>>   src/util/virstoragefile.h |  27 ++++++
>>   3 files changed, 236 insertions(+)
>>
>> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
>> index 1e1dd84..17dcd67 100644
>> --- a/src/libvirt_private.syms
>> +++ b/src/libvirt_private.syms
>> @@ -1879,6 +1879,10 @@ virStorageGenerateQcowPassphrase;
>>
>>
>>   # util/virstoragefile.h
>> +virStorageAuthDefCopy;
>> +virStorageAuthDefFormat;
>> +virStorageAuthDefFree;
>> +virStorageAuthDefParse;
> 
> I have some doubts it the Format and Parse should be in src/util/. But 
> since we already have something similar in virstorageencryption.c I 
> guess we can live with this location too.
> 

Yes, that was my model - the main goal was to avoid duplicated code. I
think that's more ripe for forgetting something or doing something in
one place, but not the other and some day having someone wonder why
there's a difference.

>>   virStorageFileCanonicalizePath;
>>   virStorageFileChainGetBroken;
>>   virStorageFileChainLookup;
>> diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c
>> index 0c50de1..056e59e 100644
>> --- a/src/util/virstoragefile.c
>> +++ b/src/util/virstoragefile.c
>> @@ -29,6 +29,8 @@
>>   #include <fcntl.h>
>>   #include <stdlib.h>
>>   #include "viralloc.h"
>> +#include "virxml.h"
>> +#include "viruuid.h"
>>   #include "virerror.h"
>>   #include "virlog.h"
>>   #include "virfile.h"
>> @@ -97,6 +99,10 @@ VIR_ENUM_IMPL(virStorageSourcePoolMode,
>>                 "host",
>>                 "direct")
>>
>> +VIR_ENUM_IMPL(virStorageAuth,
>> +              VIR_STORAGE_AUTH_TYPE_LAST,
>> +              "none", "chap", "ceph")
>> +
>>   enum lv_endian {
>>       LV_LITTLE_ENDIAN = 1, /* 1234 */
>>       LV_BIG_ENDIAN         /* 4321 */
>> @@ -1500,6 +1506,205 @@ virStorageNetHostDefCopy(size_t nhosts,
>>   }
>>
>>
>> +void
>> +virStorageAuthDefFree(virStorageAuthDefPtr authdef)
>> +{
>> +    if (!authdef)
>> +        return;
>> +
>> +    VIR_FREE(authdef->username);
>> +    VIR_FREE(authdef->secrettype);
>> +    if (authdef->secretType == VIR_STORAGE_SECRET_TYPE_USAGE)
>> +        VIR_FREE(authdef->secret.usage);
>> +    VIR_FREE(authdef);
>> +}
>> +
>> +
>> +virStorageAuthDefPtr
>> +virStorageAuthDefCopy(const virStorageAuthDef *src)
>> +{
>> +    virStorageAuthDefPtr ret;
>> +
>> +    if (VIR_ALLOC(ret) < 0)
>> +        return NULL;
>> +
>> +    if (VIR_STRDUP(ret->username, src->username) < 0)
>> +        goto error;
>> +    /* Not present for storage pool, but used for disk source */
>> +    if (src->secrettype)
>> +        if (VIR_STRDUP(ret->secrettype, src->secrettype) < 0)
>> +            goto error;
> 
> There's no need to check for src->secrettype as VIR_STRDUP accepts NULL.
> 
>> +    ret->authType = src->authType;
>> +    ret->secretType = src->secretType;
>> +    if (ret->secretType == VIR_STORAGE_SECRET_TYPE_UUID) {
>> +        memcpy(ret->secret.uuid, src->secret.uuid, sizeof(ret->secret.uuid));
>> +    } else if (ret->secretType == VIR_STORAGE_SECRET_TYPE_USAGE) {
>> +        if (VIR_STRDUP(ret->secret.usage, src->secret.usage) < 0)
>> +            goto error;
>> +    }
>> +    return ret;
>> +
>> + error:
>> +    virStorageAuthDefFree(ret);
>> +    return NULL;
>> +}
>> +
> 
> I like the explicit copying, but just a suggestion to consider:
> {
>     VIR_ALLOC(ret);
>     memcpy(ret, src, sizeof(*ret));

^^^
For me this is more ripe for someone forgetting to strdup the authname
and if necessary the ret->secret.usage.

>     VIR_STRDUP(ret->scerettype, src->secrettype);
>     if (ret->secretType == VIR_STORAGE_SECRET_TYPE_USAGE &&
>         VIR_STRDUP() < 0)
>         goto error;

^^^
See... no VIR_STRDUP(ret->username, src->username); :-)

Also no error checking for sercrettype strdup failure with us leaving
the API with nothing in secrettype which may have been important.


> }
> 
> I'm worried that if a new item is added into thepy virStorageAuthDef 
> struct, sooner or later we forget to update the Copy function. With 
> memcpy() we are safe for shallow copies at least.
> 

But when using memcpy() if we fail/error on a strdup(), then we have
pointers from one structure in the other structure - the failure path
gets messy. I think if you're not copying pointers to allocated things,
then sure memcpy() is fine, but once you have pointers to allocated
things - it's safer to be explicit on what you're copying.


>> +
>> +static int
>> +virStorageAuthDefParseSecret(xmlXPathContextPtr ctxt,
>> +                             virStorageAuthDefPtr authdef)
>> +{
>> +    char *uuid;
>> +    char *usage;
>> +    int ret = -1;
>> +
>> +    /* Used by the domain disk xml parsing in order to ensure the
>> +     * <secret type='%s' value matches the expected secret type for
>> +     * the style of disk (iscsi is chap, nbd is ceph). For some reason
>> +     * the virSecretUsageType{From|To}String() cannot be linked here
>> +     * and because only the domain parsing code cares - just keep
>> +     * it as a string.
>> +     */
>> +    authdef->secrettype = virXPathString("string(./secret/@type)", ctxt);
>> +
>> +    uuid = virXPathString("string(./secret/@uuid)", ctxt);
>> +    usage = virXPathString("string(./secret/@usage)", ctxt);
>> +    if (uuid == NULL && usage == NULL) {
>> +        virReportError(VIR_ERR_XML_ERROR, "%s",
>> +                       _("missing auth secret uuid or usage attribute"));
>> +        goto cleanup;
>> +    }
>> +
>> +    if (uuid && usage) {
>> +        virReportError(VIR_ERR_XML_ERROR, "%s",
>> +                       _("either auth secret uuid or usage expected"));
>> +        goto cleanup;
>> +    }
>> +
>> +    if (uuid) {
>> +        if (virUUIDParse(uuid, authdef->secret.uuid) < 0) {
>> +            virReportError(VIR_ERR_XML_ERROR, "%s",
>> +                            _("invalid auth secret uuid"));
>> +            goto cleanup;
>> +        }
>> +        authdef->secretType = VIR_STORAGE_SECRET_TYPE_UUID;
>> +    } else {
>> +        authdef->secret.usage = usage;
>> +        usage = NULL;
>> +        authdef->secretType = VIR_STORAGE_SECRET_TYPE_USAGE;
>> +    }
>> +    ret = 0;
>> +
>> + cleanup:
>> +    VIR_FREE(uuid);
>> +    VIR_FREE(usage);
>> +    return ret;
>> +}
>> +
>> +
>> +static virStorageAuthDefPtr
>> +virStorageAuthDefParseXML(xmlXPathContextPtr ctxt)
>> +{
>> +    virStorageAuthDefPtr authdef = NULL;
>> +    char *username = NULL;
>> +    char *authtype = NULL;
>> +
>> +    if (VIR_ALLOC(authdef) < 0)
>> +        return NULL;
>> +
>> +    if (!(username = virXPathString("string(./@username)", ctxt))) {
>> +        virReportError(VIR_ERR_XML_ERROR, "%s",
>> +                       _("missing username for auth"));
>> +        goto error;
>> +    }
>> +    authdef->username = username;
>> +    username = NULL;
>> +
>> +    authdef->authType = VIR_STORAGE_AUTH_TYPE_NONE;
>> +    authtype = virXPathString("string(./@type)", ctxt);
>> +    if (authtype) {
>> +        /* Used by the storage pool instead of the secret type field
>> +         * to define whether chap or ceph being used
>> +         */
>> +        if ((authdef->authType = virStorageAuthTypeFromString(authtype)) < 0) {
>> +            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
>> +                           _("unknown auth type '%s'"), authtype);
>> +            goto error;
>> +        }
>> +        VIR_FREE(authtype);
> 
> No need for this, as you'll free it just a few lines below.
> 

Only on the error path...

>> +    }
>> +
>> +    authdef->secretType = VIR_STORAGE_SECRET_TYPE_NONE;
>> +    if (virStorageAuthDefParseSecret(ctxt, authdef) < 0)
>> +        goto error;
>> +
>> +    return authdef;
>> +
>> + error:
>> +    VIR_FREE(authtype);
>> +    VIR_FREE(username);
>> +    virStorageAuthDefFree(authdef);
>> +    return NULL;
>> +}
>> +
>> +
>> +virStorageAuthDefPtr
>> +virStorageAuthDefParse(xmlDocPtr xml, xmlNodePtr root)
>> +{
>> +    xmlXPathContextPtr ctxt = NULL;
>> +    virStorageAuthDefPtr authdef = NULL;
>> +
>> +    ctxt = xmlXPathNewContext(xml);
>> +    if (ctxt == NULL) {
>> +        virReportOOMError();
>> +        goto cleanup;
>> +    }
>> +
>> +    ctxt->node = root;
>> +    authdef = virStorageAuthDefParseXML(ctxt);
>> +
>> + cleanup:
>> +    xmlXPathFreeContext(ctxt);
>> +    return authdef;
>> +}
>> +
>> +
>> +int
>> +virStorageAuthDefFormat(virBufferPtr buf,
>> +                        virStorageAuthDefPtr authdef)
>> +{
>> +    char uuidstr[VIR_UUID_STRING_BUFLEN];
>> +
>> +    if (authdef->authType == VIR_STORAGE_AUTH_TYPE_NONE) {
>> +        virBufferEscapeString(buf, "<auth username='%s'>\n", authdef->username);
>> +    } else {
>> +        virBufferAsprintf(buf, "<auth type='%s' ",
>> +                          virStorageAuthTypeToString(authdef->authType));
>> +        virBufferEscapeString(buf, "username='%s'>\n", authdef->username);
>> +    }
>> +
>> +    virBufferAdjustIndent(buf, 2);
>> +    if (authdef->secrettype)
>> +        virBufferAsprintf(buf, "<secret type='%s'", authdef->secrettype);
>> +    else
>> +        virBufferAddLit(buf, "<secret");
>> +
>> +    if (authdef->secretType == VIR_STORAGE_SECRET_TYPE_UUID) {
>> +        virUUIDFormat(authdef->secret.uuid, uuidstr);
>> +        virBufferAsprintf(buf, " uuid='%s'/>\n", uuidstr);
>> +    } else if (authdef->secretType == VIR_STORAGE_SECRET_TYPE_USAGE) {
>> +        virBufferEscapeString(buf, " usage='%s'/>\n",
>> +                              authdef->secret.usage);
>> +    } else {
>> +        virBufferAddLit(buf, "/>\n");
>> +    }
>> +    virBufferAdjustIndent(buf, -2);
>> +    virBufferAddLit(buf, "</auth>\n");
>> +
>> +    return 0;
>> +}
>> +
>> +
>>   virSecurityDeviceLabelDefPtr
>>   virStorageSourceGetSecurityLabelDef(virStorageSourcePtr src,
>>                                       const char *model)
>> diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h
>> index 48c7e02..383ba96 100644
>> --- a/src/util/virstoragefile.h
>> +++ b/src/util/virstoragefile.h
>> @@ -193,6 +193,15 @@ typedef virStorageSourcePoolDef *virStorageSourcePoolDefPtr;
>>
>>
>>   typedef enum {
>> +    VIR_STORAGE_AUTH_TYPE_NONE,
>> +    VIR_STORAGE_AUTH_TYPE_CHAP,
>> +    VIR_STORAGE_AUTH_TYPE_CEPHX,
>> +
>> +    VIR_STORAGE_AUTH_TYPE_LAST,
>> +} virStorageAuthType;
>> +VIR_ENUM_DECL(virStorageAuth)
>> +
>> +typedef enum {
>>       VIR_STORAGE_SECRET_TYPE_NONE,
>>       VIR_STORAGE_SECRET_TYPE_UUID,
>>       VIR_STORAGE_SECRET_TYPE_USAGE,
>> @@ -200,6 +209,19 @@ typedef enum {
>>       VIR_STORAGE_SECRET_TYPE_LAST
>>   } virStorageSecretType;
>>
>> +typedef struct _virStorageAuthDef virStorageAuthDef;
>> +typedef virStorageAuthDef *virStorageAuthDefPtr;
>> +struct _virStorageAuthDef {
>> +    char *username;
>> +    char *secrettype; /* <secret type='%s' for disk source */
>> +    int authType;     /* virStorageAuthType */
>> +    int secretType;   /* virStorageSecretType */
>> +    union {
>> +        unsigned char uuid[VIR_UUID_BUFLEN];
>> +        char *usage;
>> +    } secret;
>> +};
> 
> Is it necessary to expose the struct or can we hide it in the 
> virstoragefile.c?
> 

As you found out in 3/5... not easily... To be used by <disk> and
<pool>, but could also be used by <hostdev> for iSCSI.

John

>> +
>>   typedef struct _virStorageDriverData virStorageDriverData;
>>   typedef virStorageDriverData *virStorageDriverDataPtr;
>>
>> @@ -307,6 +329,11 @@ int virStorageFileGetLVMKey(const char *path,
>>   int virStorageFileGetSCSIKey(const char *path,
>>                                char **key);
>>
>> +void virStorageAuthDefFree(virStorageAuthDefPtr def);
>> +virStorageAuthDefPtr virStorageAuthDefCopy(const virStorageAuthDef *src);
>> +virStorageAuthDefPtr virStorageAuthDefParse(xmlDocPtr xml, xmlNodePtr root);
>> +int virStorageAuthDefFormat(virBufferPtr buf, virStorageAuthDefPtr authdef);
>> +
>>   virSecurityDeviceLabelDefPtr
>>   virStorageSourceGetSecurityLabelDef(virStorageSourcePtr src,
>>                                       const char *model);
>>
> 
> Michal
> 




More information about the libvir-list mailing list