]> xenbits.xensource.com Git - unikraft/unikraft.git/commitdiff
lib/ukcpio: Add CPIO extraction functionality
authorAlexander Jung <a.jung@lancs.ac.uk>
Mon, 12 Apr 2021 10:38:24 +0000 (12:38 +0200)
committerUnikraft <monkey@unikraft.io>
Mon, 5 Jul 2021 04:47:27 +0000 (04:47 +0000)
Modeled after the FreeBSD libarchive:
https://github.com/freebsd/freebsd/blob/master/contrib/libarchive/libarchive/archive_read_support_format_cpio.c

The implementation is mostly complete except that it does not yet
support symlinks.

Signed-off-by: Robert Hrusecky <roberth@cs.utexas.edu>
Signed-off-by: Omar Jamil <omarj2898@gmail.com>
Signed-off-by: Sachin Beldona <sachinbeldona@utexas.edu>
Signed-off-by: Gabriel Mocanu <gabi.mocanu98@gmail.com>
Signed-off-by: Alexander Jung <a.jung@lancs.ac.uk>
Reviewed-by: Simon Kuenzer <simon.kuenzer@neclab.eu>
Tested-by: Unikraft CI <monkey@unikraft.io>
GitHub-Pull-Request: #179

lib/ukcpio/Makefile.uk
lib/ukcpio/cpio.c
lib/ukcpio/exportsyms.uk
lib/ukcpio/include/uk/cpio.h

index 56ed68a76f3e02cdd996e33846574b5e28cb0d70..da4bd99301ca2343da710fd89c28e45dd4d9a904 100644 (file)
@@ -2,3 +2,5 @@ $(eval $(call addlib_s,libukcpio,$(CONFIG_LIBUKCPIO)))
 
 CINCLUDES-$(CONFIG_LIBUKCPIO) += -I$(LIBUKCPIO_BASE)/include
 CXXINCLUDES-$(CONFIG_LIBUKCPIO) += -I$(LIBUKCPIO_BASE)/include
+
+LIBUKCPIO_SRCS-y += $(LIBUKCPIO_BASE)/cpio.c
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..06aed1e61571f411a2cd4078afcca19a4da2c8be 100644 (file)
@@ -0,0 +1,315 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Authors: Robert Hrusecky <roberth@cs.utexas.edu>
+ *          Omar Jamil <omarj2898@gmail.com>
+ *          Sachin Beldona <sachinbeldona@utexas.edu>
+ *
+ * Copyright (c) 2017, The University of Texas at Austin. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <uk/assert.h>
+#include <uk/print.h>
+#include <uk/cpio.h>
+#include <uk/essentials.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+/*
+ * Currently only supports BSD new-style cpio archive format.
+ */
+#define UKCPIO_MAGIC_NEWC "070701"
+#define UKCPIO_MAGIC_CRC  "070702"
+#define FILE_TYPE_MASK    0170000
+#define DIRECTORY_BITS    040000
+#define FILE_BITS         0100000
+
+#define ALIGN_4(ptr)      ((void *)ALIGN_UP((uintptr_t)(ptr), 4))
+
+#define IS_FILE_OF_TYPE(mode, bits) (((mode) & (FILE_TYPE_MASK)) == (bits))
+#define IS_FILE(mode) IS_FILE_OF_TYPE((mode), (FILE_BITS))
+#define IS_DIR(mode) IS_FILE_OF_TYPE((mode), (DIRECTORY_BITS))
+
+#define S8HEX_TO_U32(buf) ((uint32_t) snhex_to_int((buf), 8))
+#define GET_MODE(hdr)     ((mode_t) S8HEX_TO_U32((hdr)->mode))
+
+#define filename(header) ((const char *)header + sizeof(struct cpio_header))
+
+struct cpio_header {
+       char magic[6];
+       char inode_num[8];
+       char mode[8];
+       char uid[8];
+       char gid[8];
+       char nlink[8];
+       char mtime[8];
+       char filesize[8];
+       char major[8];
+       char minor[8];
+       char ref_major[8];
+       char ref_minor[8];
+       char namesize[8];
+       char chksum[8];
+};
+
+static int
+valid_magic(struct cpio_header *header)
+{
+       return memcmp(header->magic, UKCPIO_MAGIC_NEWC, 6) == 0
+               || memcmp(header->magic, UKCPIO_MAGIC_CRC, 6) == 0;
+}
+
+/**
+ * Function to convert len digits of hexadecimal string loc
+ * to an integer.
+ *
+ * @param buf
+ *  The string character buffer.
+ * @param count
+ *  The size of the buffer.
+ * @return
+ *   The converted unsigned integer value on success.  Returns 0 on error.
+ */
+static unsigned int
+snhex_to_int(const char *buf, size_t count)
+{
+       unsigned int val = 0;
+       size_t i;
+
+       UK_ASSERT(buf);
+
+       for (i = 0; i < count; i++) {
+               val *= 16;
+               if (buf[i] >= '0' && buf[i] <= '9')
+                       val += (buf[i] - '0');
+               else if (buf[i] >= 'A' && buf[i] <= 'F')
+                       val += (buf[i] - 'A') + 10;
+               else if (buf[i] >= 'a' && buf[i] <= 'f')
+                       val += (buf[i] - 'a') + 10;
+               else
+                       return 0;
+       }
+       return val;
+}
+
+/**
+ * Create absolute path with prefix.
+ *
+ * @param prefix
+ *  Prefix to the path.
+ * @param path
+ *  Path to file or directory.
+ * @return
+ *  Returns the absolute path with prefix.
+ */
+static char *
+absolute_path(const char *prefix, const char *path)
+{
+       int add_slash;
+       size_t prefix_len;
+       size_t path_len;
+       size_t abs_path_len;
+       char *abs_path;
+
+       UK_ASSERT(prefix);
+       UK_ASSERT(path);
+
+       prefix_len = strlen(prefix);
+       path_len = strlen(path);
+
+       add_slash = prefix[prefix_len - 1] == '/' ? 0 : 1;
+       abs_path_len = prefix_len + add_slash + path_len + 1;
+
+       abs_path = malloc(abs_path_len);
+
+       if (abs_path == NULL)
+               return NULL;
+
+       memcpy(abs_path, prefix, prefix_len);
+       if (add_slash)
+               abs_path[prefix_len] = '/';
+       memcpy(&abs_path[prefix_len + add_slash], path, path_len);
+
+       abs_path[abs_path_len - 1] = '\0';
+       return abs_path;
+}
+
+/**
+ * Reads the section to the dest from a given a CPIO header.
+ *
+ * @param header_ptr
+ *  Pointer to the CPIO header.
+ * @param dest
+ *  Destination path to extract the current header.
+ * @param last
+ *  The size of the CPIO section.
+ * @return
+ *  Returns 0 on success or one of ukcpio_error enum.
+ */
+static enum ukcpio_error
+read_section(struct cpio_header **header_ptr,
+                               const char *dest, uintptr_t last)
+{
+       enum ukcpio_error error = UKCPIO_SUCCESS;
+       int fd;
+       struct cpio_header *header;
+       char *path_from_root;
+       mode_t header_mode;
+       uint32_t header_filesize;
+       uint32_t header_namesize;
+       char *data_location;
+       uint32_t bytes_to_write;
+       int bytes_written;
+       struct cpio_header *next_header;
+
+       if (strcmp(filename(*header_ptr), "TRAILER!!!") == 0) {
+               *header_ptr = NULL;
+               return UKCPIO_SUCCESS;
+       }
+
+       if (!valid_magic(*header_ptr)) {
+               *header_ptr = NULL;
+               return -UKCPIO_INVALID_HEADER;
+       }
+
+       UK_ASSERT(dest);
+
+       header = *header_ptr;
+       path_from_root = absolute_path(dest, filename(header));
+
+       if (path_from_root == NULL) {
+               *header_ptr = NULL;
+               error = -UKCPIO_NOMEM;
+               goto out;
+       }
+
+       header_mode = GET_MODE(header);
+       header_filesize = S8HEX_TO_U32(header->filesize);
+       header_namesize = S8HEX_TO_U32(header->namesize);
+
+       if ((uintptr_t)header + sizeof(struct cpio_header) > last) {
+               *header_ptr = NULL;
+               error = -UKCPIO_MALFORMED_INPUT;
+               goto out;
+       }
+
+       if (IS_FILE(header_mode) && header_filesize != 0) {
+               uk_pr_info("Creating %s...\n", path_from_root);
+               fd = open(path_from_root, O_CREAT | O_RDWR);
+
+               if (fd < 0) {
+                       *header_ptr = NULL;
+                       error = -UKCPIO_FILE_CREATE_FAILED;
+                       goto out;
+               }
+
+               data_location = (char *)ALIGN_4(
+                               (char *)(header) + sizeof(struct cpio_header)
+                               + header_namesize);
+
+               if ((uintptr_t)data_location + header_filesize > last) {
+                       uk_pr_err("Malformed CPIO input\n");
+                       *header_ptr = NULL;
+                       error = -UKCPIO_MALFORMED_INPUT;
+                       goto out;
+               }
+
+               bytes_to_write = header_filesize;
+               bytes_written = 0;
+
+               while (bytes_to_write > 0) {
+                       bytes_written = write(fd, data_location + bytes_written,
+                                             bytes_to_write);
+                       if (bytes_written < 0) {
+                               uk_pr_warn("Failed to write from CPIO input\n");
+                               *header_ptr = NULL;
+                               error = -UKCPIO_FILE_WRITE_FAILED;
+                               goto out;
+                       }
+                       bytes_to_write -= bytes_written;
+               }
+
+               if (chmod(path_from_root, header_mode & 0777) < 0)
+                       uk_pr_warn("Failed to chmod %s\n", path_from_root);
+
+               if (close(fd) < 0) {
+                       uk_pr_warn("Failed to close CPIO input\n");
+                       *header_ptr = NULL;
+                       error = -UKCPIO_FILE_CLOSE_FAILED;
+                       goto out;
+               }
+       } else if (IS_DIR(header_mode)) {
+               uk_pr_info("Extracting %s...\n", path_from_root);
+               if (strcmp(".", filename(header)) != 0
+                       && mkdir(path_from_root, header_mode & 0777) < 0) {
+                       uk_pr_err("Could not create directory from CPIO\n");
+                       *header_ptr = NULL;
+                       error = -UKCPIO_MKDIR_FAILED;
+                       goto out;
+               }
+       }
+
+       next_header = (struct cpio_header *)ALIGN_4(
+               (char *)header + sizeof(struct cpio_header) + header_namesize
+       );
+
+       next_header = (struct cpio_header *)ALIGN_4(
+               (char *)next_header + header_filesize
+       );
+
+       *header_ptr = next_header;
+
+out:
+       free(path_from_root);
+       return error;
+}
+
+enum ukcpio_error
+ukcpio_extract(const char *dest, void *buf, size_t buflen)
+{
+       enum ukcpio_error error = UKCPIO_SUCCESS;
+       struct cpio_header *header = (struct cpio_header *)(buf);
+       struct cpio_header **header_ptr = &header;
+       uintptr_t end = (uintptr_t)header;
+
+       if (dest == NULL)
+               return -UKCPIO_NODEST;
+
+       while (!error && header) {
+               error = read_section(header_ptr, dest, end + buflen);
+               header = *header_ptr;
+       }
+
+       return error;
+}
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c25c393a03f6d616dae89113ca42b864e3e1c3f8 100644 (file)
@@ -0,0 +1 @@
+ukcpio_extract
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..855db56a80d00afeadd338fe27310f4a167406dc 100644 (file)
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Authors: Robert Hrusecky <roberth@cs.utexas.edu>
+ *          Omar Jamil <omarj2898@gmail.com>
+ *          Sachin Beldona <sachinbeldona@utexas.edu>
+ *
+ * Copyright (c) 2017, The University of Texas at Austin. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __UK_CPIO_H__
+#define __UK_CPIO_H__
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern C {
+#endif /* __cplusplus */
+
+/**
+ * Include also the case of unsupported headers
+ */
+enum ukcpio_error {
+       UKCPIO_SUCCESS = 0,
+       UKCPIO_INVALID_HEADER,
+       UKCPIO_FILE_CREATE_FAILED,
+       UKCPIO_FILE_WRITE_FAILED,
+       UKCPIO_FILE_CHMOD_FAILED,
+       UKCPIO_FILE_CLOSE_FAILED,
+       UKCPIO_MKDIR_FAILED,
+       UKCPIO_MALFORMED_INPUT,
+       UKCPIO_NOMEM,
+       UKCPIO_NODEST
+};
+
+/**
+ * Extracts the given CPIO buffer to the path destination.
+ *
+ * @param dest
+ *  The path location where the buffer will be extracted to.
+ * @param buf
+ *  A pointer to the first header of the CPIO buffer.
+ * @param buflen
+ *  The size of the CPIO buffer.
+ * @return
+ *  Returns 0 on success or one of ukcpio_error enums.
+ */
+enum ukcpio_error
+ukcpio_extract(const char *dest, void *buf, size_t buflen);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __CPIO_H__ */