[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[libvirt] [PATCH 2/5] Parsing and file handling operations for an OVA file



The set of routines provides parsing and file handling
operation for a given OVA package, the extracted information
contains:
1. OVF descriptor (xml descriptor contained in the package)
2. List of virtual disk and related details (file name, size and
   offset)

Parsing results are stored in a structure virOVA, one of the
members of this structure is head of linked list (single pointed)
which stores file details for all OVA contained files (such as:
OVF descriptor(s), virtual disk, metaconf files etc.).
---
 po/POTFILES.in    |    1 +
 src/Makefile.am   |    1 +
 src/util/virova.c |  463 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/util/virova.h |   68 ++++++++
 4 files changed, 533 insertions(+)
 create mode 100644 src/util/virova.c
 create mode 100644 src/util/virova.h

diff --git a/po/POTFILES.in b/po/POTFILES.in
index 95619f9..580a035 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -182,6 +182,7 @@ src/util/viruri.c
 src/util/virusb.c
 src/util/virutil.c
 src/util/virxml.c
+src/util/virova.c
 src/vbox/vbox_MSCOMGlue.c
 src/vbox/vbox_XPCOMCGlue.c
 src/vbox/vbox_driver.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 955973e..2c20433 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -116,6 +116,7 @@ UTIL_SOURCES =							\
 		util/virutil.c util/virutil.h			\
 		util/viruuid.c util/viruuid.h			\
 		util/virxml.c util/virxml.h			\
+		util/virova.c util/virova.h			\
 		$(NULL)
 
 
diff --git a/src/util/virova.c b/src/util/virova.c
new file mode 100644
index 0000000..23709e0
--- /dev/null
+++ b/src/util/virova.c
@@ -0,0 +1,463 @@
+/*
+ * virova.c: OVA file handling/parsing
+ *
+ * Copyright (C) 2013 Ata E Husain Bohra <ata husain hotmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <config.h>
+#include "internal.h"
+
+#include <string.h>
+#include <sys/stat.h>
+#include <libxml/parser.h>
+#include <libxml/xpathInternals.h>
+
+#include "virova.h"
+#include "virfile.h"
+#include "virerror.h"
+#include "viralloc.h"
+#include "virbuffer.h"
+
+#define VIR_FROM_THIS VIR_FROM_NONE
+
+#define XMLCHAR_CAST(VALUE)  (const char *)(VALUE)
+
+/**
+ * OVA file is a tar file, the details of header/metadata are:
+ *
+ * ---------------- OVA/TAR Header--------------------------------------
+ *___________________________________________________________________
+ *| Field | Field |   Field                                          |
+ *| Offset| Size  |                                                  |
+ *|_______|_______|__________________________________________________|
+ *| 0     | 100   |File name                                         |
+ *| 100   | 8     |File mode                                         |
+ *| 108   | 8     |Owner's numeric user ID                           |
+ *| 116   | 8     |Group's numeric user ID                           |
+ *| 124   | 12    |File size in bytes                                |
+ *| 136   | 12    |Last modification time in numeric Unix time format|
+ *| 148   | 8     |Checksum for header block                         |
+ *| 156   | 1     |Link indicator (file type)                        |
+ *| 157   | 100   |Name of linked file                               |
+ *| 257   | 6     |TAR magic                                         |
+ *| 263   | 2     |Version                                           |
+ *|_______|_______|__________________________________________________|
+ */
+
+#define FILE_NAME_LENGTH  100
+#define FILE_SIZE_LENGTH  12
+#define HEADER_SIZE       512
+#define VERSION_LENGTH    2
+#define MAGIC_LENGTH      6
+
+static const char *MAGIC_STRING = "ustar";
+static const char *GNU_VERSION = " ";
+
+static const int  START_OFFSET           = 0L;
+static const int  FILE_SIZE_OFFSET       = 24L;
+static const int  MAGIC_OFFSET           = 257;
+static const int  VERSION_OFFSET         = 263L;
+static const int  HEADER_END_OFFSET      = 377L;
+static const char LARGE_FILE_OCTET       = '\200';
+
+static int validateOVAMagic(FILE *fHandle);
+
+
+/**
+ * validateOVAMagic: verify OVA magix string
+ *
+ * @fHandle: FILE*
+ *
+ * validate if given OVA package contains a valid TAR magic string.
+ */
+int
+validateOVAMagic(FILE *fHandle)
+{
+    int result = -1;
+    char magic[MAGIC_LENGTH+1] = {'\0'};
+    char version[VERSION_LENGTH+1] = {'\0'};
+    size_t bytesRead = 0;
+
+    if (fseeko(fHandle, MAGIC_OFFSET, SEEK_SET) < 0) {
+        virReportSystemError(errno, "%s", _("Seek error: OVA magic string"));
+        goto cleanup;
+    }
+
+    bytesRead = fread(magic, 1, MAGIC_LENGTH, fHandle);
+    if (bytesRead != MAGIC_LENGTH) {
+        virReportSystemError(errno, "%s", _("Unable to read magic string"));
+        goto cleanup;
+    }
+
+    if (STRNEQLEN(magic, MAGIC_STRING, MAGIC_LENGTH-1)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                        _("Invalid magic string"));
+        goto cleanup;
+    }
+
+    /* FIXME: Version string can be found at offset 263 in the header.
+     * Two supported tar version packages are:
+     * 1. POSIX_VERSION: version string is "00"
+     * 2. GNU_VERSION: version string is " "
+     *
+     * Current parsing support is valid ONLY for GNU VERSION format
+     */
+    if (fseeko(fHandle, VERSION_OFFSET, SEEK_SET) < 0) {
+        virReportSystemError(errno, "%s", _("Seek error: version string"));
+        goto cleanup;
+    }
+
+    bytesRead = fread(version, 1, VERSION_LENGTH, fHandle);
+    if (bytesRead != VERSION_LENGTH) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                          _("Error reading version string"));
+        goto cleanup;
+    }
+
+    if (STRNEQLEN(version, GNU_VERSION, VERSION_LENGTH-1)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                          _("Only GNU VERSION OVA packages are supported"));
+        goto cleanup;
+    }
+
+    result = 0;
+
+cleanup:
+
+    /* ignore seek error */
+    fseeko(fHandle, START_OFFSET, SEEK_SET);
+
+    return result;
+
+}
+
+
+
+/**
+ * virParseOva
+ *
+ * @ovaPath: string containing full path of OVA file.
+ *
+ * Function open OVA package and populates virOVA struct
+ */
+int
+virParseOVA(const char *ovaPath, virOVAPtr *ret)
+{
+    int result  = -1;
+    virOVAPtr ova = NULL;
+    virOVADiskListPtr listItr = NULL;
+    char fileSizeBuff[FILE_SIZE_LENGTH+1] = {'\0'};
+    size_t bytesRead = 0;
+    off_t adjust = 0;
+    struct stat buff;
+
+    if (VIR_ALLOC(ova) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    ova->fHandle = fopen(ovaPath, "r");
+    if (ova->fHandle == NULL) {
+        virReportSystemError(errno, "%s", _("Unable to open file"));
+        goto cleanup;
+    }
+
+    if (fstat(fileno(ova->fHandle), &buff) < 0) {
+        virReportSystemError(errno, "%s", _("File stat error"));
+        goto cleanup;
+    }
+
+    ova->totalSize = buff.st_size;
+
+    /* seek to the stating of the package */
+    if (fseeko(ova->fHandle, START_OFFSET, SEEK_SET) < 0) {
+        virReportSystemError(errno, "%s", _("File seek error"));
+        goto cleanup;
+    }
+
+    /* verify OVA magic string is valid. */
+    if (validateOVAMagic(ova->fHandle) < 0) {
+        goto cleanup;
+    }
+
+    /* extract details */
+    while (1) {
+        virOVADiskList *diskList = NULL;
+
+        if (VIR_ALLOC(diskList) < 0 ||
+            VIR_ALLOC(diskList->data)) {
+            virReportOOMError();
+            goto cleanup;
+        }
+
+        memset(diskList->data->name, '\0', FILE_NAME_LENGTH+1);
+        diskList->data->offset = 0;
+        diskList->data->size = 0;
+        diskList->next = NULL;
+
+        memset(fileSizeBuff, '\0', FILE_SIZE_LENGTH+1);
+
+        /* extract file name */
+        bytesRead = fread(diskList->data->name, 1, FILE_NAME_LENGTH,
+                          ova->fHandle);
+        if (bytesRead <= 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                                _("Invalid file name"));
+            goto cleanup;
+        }
+
+        if (diskList->data->name[0] == '\0') {
+            /* package parsing is done */
+            VIR_FREE(diskList->data);
+            VIR_FREE(diskList);
+            break;
+        }
+
+        /* extract file size */
+        if (fseeko(ova->fHandle, FILE_SIZE_OFFSET, SEEK_CUR) < 0) {
+            virReportSystemError(VIR_ERR_INTERNAL_ERROR, "%s",
+                                        _("File seek error"));
+            goto cleanup;
+        }
+
+        bytesRead = fread(fileSizeBuff, 1, FILE_SIZE_LENGTH, ova->fHandle);
+        if (bytesRead <= 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                                _("Invalid file length"));
+            goto cleanup;
+        }
+
+        /**
+         * if file size exceeds 8 G, then file size has to be obtained
+         * by reading 11 bytes of which first byte will be '\200'
+         */
+        if (fileSizeBuff[0] == LARGE_FILE_OCTET) {
+            const int byteSize = 4;
+            int i = 0;
+
+            for (i = 0; i < 8; ++i) {
+                uint8_t c = 0;
+                c = fileSizeBuff[byteSize + i];
+                diskList->data->size = (diskList->data->size << 8) | c;
+            }
+        } else {
+            sscanf(fileSizeBuff, "%lo", &diskList->data->size);
+        }
+
+        /* set the file content offset */
+        if (fseeko(ova->fHandle, HEADER_END_OFFSET, SEEK_CUR) < 0) {
+            virReportSystemError(errno, "%s", _("Seek error"));
+            VIR_FREE(diskList->data);
+            VIR_FREE(diskList);
+            goto cleanup;
+        }
+
+        diskList->data->offset = ftello(ova->fHandle) - 1;
+        if (diskList->data->offset < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                                _("Invalid file offset"));
+            VIR_FREE(diskList->data);
+            VIR_FREE(diskList);
+            goto cleanup;
+        }
+
+        /* adjust seek head to header size boundary */
+        adjust = diskList->data->size;
+        if (diskList->data->size % HEADER_SIZE) {
+            adjust = (adjust/HEADER_SIZE + 1) * HEADER_SIZE;
+        }
+
+        if (fseeko(ova->fHandle, adjust - 1, SEEK_CUR) < 0) {
+            virReportSystemError(errno, "%s", _("Seek error"));
+            goto cleanup;
+        }
+
+        if (!ova->head) {
+            ova->head = diskList;
+            listItr = ova->head;
+        } else {
+            listItr->next = diskList;
+            listItr = listItr->next;
+        }
+    }
+
+    *ret = ova;
+    result = 0;
+
+cleanup:
+
+    if (result < 0) {
+        virFreeOVA(ova);
+    }
+
+    return result;
+
+}
+
+
+/**
+ * virGetOVFDescriptor
+ *
+ * @ova: virOVAPtr
+ *
+ * Returns OVF descriptor string.
+ */
+
+int
+virGetOVFDescriptor(virOVAPtr ova, char **ret)
+{
+    int result = -1;
+    virOVADiskListPtr candidate = NULL;
+    char *fileExtension = NULL;
+    size_t bytesRead = 0;
+
+    if (ova == NULL || ret == NULL) {
+        goto cleanup;
+    }
+
+    /**
+     * TODO: OVA may have multiple OVF descriptors,
+     *       for now consider deploying only one OVF at once
+     */
+    for (candidate = ova->head; candidate != NULL;
+         candidate = candidate->next) {
+        if (!candidate->data) {
+            goto cleanup;
+        }
+        fileExtension = strrchr(candidate->data->name, '.');
+        if (fileExtension && STREQ(fileExtension, ".ovf")) {
+            break;
+        }
+    }
+
+    if (!candidate) {
+        /**
+         * OVA may have multiple OVF descriptor, leave error handling
+         * to the caller
+         */
+        goto cleanup;
+    }
+
+    if (fseeko(ova->fHandle, candidate->data->offset, SEEK_SET) < 0) {
+        virReportSystemError(errno, "%s", _("File seek error"));
+        goto cleanup;
+    }
+
+    if (VIR_ALLOC_N(*ret, candidate->data->size+1) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    bytesRead = fread(*ret, 1, candidate->data->size, ova->fHandle);
+    if (bytesRead != candidate->data->size) {
+        virReportSystemError(errno, "%s", _("File read error"));
+        goto cleanup;
+    }
+
+    result = 0;
+
+cleanup:
+
+    return result;
+
+}
+
+
+
+/**
+ * virGetOVFDomainName
+ *
+ * Extract domain name from a OVF file
+ */
+char *
+virGetOVFDomainName(const char *ovf)
+{
+    char *domainName = NULL;
+    xmlNode *root_element = NULL;
+    xmlNode *curNode = NULL;
+    xmlNode *section = NULL;
+    xmlNode *element = NULL;
+    xmlDoc *doc = xmlReadMemory(ovf, strlen(ovf), NULL, NULL, 0);
+
+    if (!doc) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                        _("Error reading OVF xml"));
+        goto cleanup;
+    }
+
+    root_element = xmlDocGetRootElement(doc);
+    for (curNode = root_element; curNode != NULL; curNode = curNode->next) {
+        if (STRCASENEQ(XMLCHAR_CAST(curNode->name), "envelope")) {
+            /* not the OVF root element */
+            continue;
+        }
+
+        for (section = curNode->children; section; section = section->next) {
+            if (STRCASENEQ(XMLCHAR_CAST(section->name), "virtualsystem")) {
+                /* wait for VirtualSystem section */
+                continue;
+            }
+
+            for (element = section->children; element ; element = element->next) {
+                if (STRCASENEQ(XMLCHAR_CAST(element->name), "name")) {
+                    continue;
+                }
+
+                domainName = strdup(XMLCHAR_CAST(xmlNodeGetContent(element)));
+            }
+        }
+    }
+
+cleanup:
+
+    xmlFreeDoc(doc);
+
+    return domainName;
+
+}
+
+
+
+/**
+ * virFreeOVA: free OVA structure members
+ */
+void virFreeOVA(virOVAPtr ova)
+{
+    struct _virOVADiskList *list = NULL;
+
+    if (ova == NULL) {
+        /* nothing to free */
+        return ;
+    }
+
+    if (VIR_FCLOSE(ova->fHandle) < 0) {
+        VIR_FORCE_FCLOSE(ova->fHandle);
+    }
+
+    /* iterate over linked list and free */
+    while (ova->head != NULL) {
+        list = ova->head;
+        ova->head = ova->head->next;
+
+        VIR_FREE(list->data);
+        VIR_FREE(list);
+    }
+
+    VIR_FREE(ova);
+
+}
diff --git a/src/util/virova.h b/src/util/virova.h
new file mode 100644
index 0000000..df74e0f
--- /dev/null
+++ b/src/util/virova.h
@@ -0,0 +1,68 @@
+/*
+ * virova.h: OVA file handling/parsing
+ *
+ * Copyright (C) 2013 Ata E Husain Bohra <ata husain hotmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __VIR_OVA_H_
+# define __VIR_OVA_H_
+
+# include <fcntl.h>
+# include <stdio.h>
+
+typedef struct _virOVADiskList virOVADiskList;
+typedef struct _virOVADiskFile virOVADiskFile;
+typedef struct _virOVA virOVA;
+
+/* opaque management of OVA file details */
+struct _virOVADiskFile {
+    char        name[101];          /* TAR header file name limit */
+    off_t       size;               /* virtual disk file size */
+    off_t       offset;             /* virtual disk file offset */
+};
+
+typedef virOVADiskFile *virOVADiskFilePtr;
+
+/* opaque type to maintain OVA files as single pointer linked list */
+struct _virOVADiskList {
+    struct _virOVADiskList     *next;
+    virOVADiskFilePtr          data;
+};
+
+typedef virOVADiskList *virOVADiskListPtr;
+
+/**
+ * parse OVA and populate the containing file details (OVA desc, vDisk etc.)
+ */
+struct _virOVA {
+    FILE*                       fHandle;   /* file handle for OVA file */
+    virOVADiskListPtr           head;      /* virtual disk single linked list */
+    off_t                       totalSize; /* total size of OVA package */
+};
+
+typedef virOVA *virOVAPtr;
+
+int virParseOVA(const char *ovaPath, virOVAPtr *ret);
+
+int virGetOVFDescriptor(virOVAPtr ova, char **ret);
+
+char *virGetOVFDomainName(const char *ovf);
+
+void virFreeOVA(virOVAPtr ova);
+
+#endif /* __VIR_OVA_H_ */
-- 
1.7.9.5


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]