diff options
Diffstat (limited to 'src/import/qcow2-util.c')
-rw-r--r-- | src/import/qcow2-util.c | 352 |
1 files changed, 0 insertions, 352 deletions
diff --git a/src/import/qcow2-util.c b/src/import/qcow2-util.c deleted file mode 100644 index ee2121cc36..0000000000 --- a/src/import/qcow2-util.c +++ /dev/null @@ -1,352 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering - - systemd 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. - - systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <zlib.h> - -#include "alloc-util.h" -#include "btrfs-util.h" -#include "qcow2-util.h" -#include "sparse-endian.h" -#include "util.h" - -#define QCOW2_MAGIC 0x514649fb - -#define QCOW2_COPIED (1ULL << 63) -#define QCOW2_COMPRESSED (1ULL << 62) -#define QCOW2_ZERO (1ULL << 0) - -typedef struct _packed_ Header { - be32_t magic; - be32_t version; - - be64_t backing_file_offset; - be32_t backing_file_size; - - be32_t cluster_bits; - be64_t size; - be32_t crypt_method; - - be32_t l1_size; - be64_t l1_table_offset; - - be64_t refcount_table_offset; - be32_t refcount_table_clusters; - - be32_t nb_snapshots; - be64_t snapshots_offset; - - /* The remainder is only present on QCOW3 */ - be64_t incompatible_features; - be64_t compatible_features; - be64_t autoclear_features; - - be32_t refcount_order; - be32_t header_length; -} Header; - -#define HEADER_MAGIC(header) be32toh((header)->magic) -#define HEADER_VERSION(header) be32toh((header)->version) -#define HEADER_CLUSTER_BITS(header) be32toh((header)->cluster_bits) -#define HEADER_CLUSTER_SIZE(header) (1ULL << HEADER_CLUSTER_BITS(header)) -#define HEADER_L2_BITS(header) (HEADER_CLUSTER_BITS(header) - 3) -#define HEADER_SIZE(header) be64toh((header)->size) -#define HEADER_CRYPT_METHOD(header) be32toh((header)->crypt_method) -#define HEADER_L1_SIZE(header) be32toh((header)->l1_size) -#define HEADER_L2_SIZE(header) (HEADER_CLUSTER_SIZE(header)/sizeof(uint64_t)) -#define HEADER_L1_TABLE_OFFSET(header) be64toh((header)->l1_table_offset) - -static uint32_t HEADER_HEADER_LENGTH(const Header *h) { - if (HEADER_VERSION(h) < 3) - return offsetof(Header, incompatible_features); - - return be32toh(h->header_length); -} - -static int copy_cluster( - int sfd, uint64_t soffset, - int dfd, uint64_t doffset, - uint64_t cluster_size, - void *buffer) { - - ssize_t l; - int r; - - r = btrfs_clone_range(sfd, soffset, dfd, doffset, cluster_size); - if (r >= 0) - return r; - - l = pread(sfd, buffer, cluster_size, soffset); - if (l < 0) - return -errno; - if ((uint64_t) l != cluster_size) - return -EIO; - - l = pwrite(dfd, buffer, cluster_size, doffset); - if (l < 0) - return -errno; - if ((uint64_t) l != cluster_size) - return -EIO; - - return 0; -} - -static int decompress_cluster( - int sfd, uint64_t soffset, - int dfd, uint64_t doffset, - uint64_t compressed_size, - uint64_t cluster_size, - void *buffer1, - void *buffer2) { - - _cleanup_free_ void *large_buffer = NULL; - z_stream s = {}; - uint64_t sz; - ssize_t l; - int r; - - if (compressed_size > cluster_size) { - /* The usual cluster buffer doesn't suffice, let's - * allocate a larger one, temporarily */ - - large_buffer = malloc(compressed_size); - if (!large_buffer) - return -ENOMEM; - - buffer1 = large_buffer; - } - - l = pread(sfd, buffer1, compressed_size, soffset); - if (l < 0) - return -errno; - if ((uint64_t) l != compressed_size) - return -EIO; - - s.next_in = buffer1; - s.avail_in = compressed_size; - s.next_out = buffer2; - s.avail_out = cluster_size; - - r = inflateInit2(&s, -12); - if (r != Z_OK) - return -EIO; - - r = inflate(&s, Z_FINISH); - sz = (uint8_t*) s.next_out - (uint8_t*) buffer2; - inflateEnd(&s); - if (r != Z_STREAM_END || sz != cluster_size) - return -EIO; - - l = pwrite(dfd, buffer2, cluster_size, doffset); - if (l < 0) - return -errno; - if ((uint64_t) l != cluster_size) - return -EIO; - - return 0; -} - -static int normalize_offset( - const Header *header, - uint64_t p, - uint64_t *ret, - bool *compressed, - uint64_t *compressed_size) { - - uint64_t q; - - q = be64toh(p); - - if (q & QCOW2_COMPRESSED) { - uint64_t sz, csize_shift, csize_mask; - - if (!compressed) - return -EOPNOTSUPP; - - csize_shift = 64 - 2 - (HEADER_CLUSTER_BITS(header) - 8); - csize_mask = (1ULL << (HEADER_CLUSTER_BITS(header) - 8)) - 1; - sz = (((q >> csize_shift) & csize_mask) + 1) * 512 - (q & 511); - q &= ((1ULL << csize_shift) - 1); - - if (compressed_size) - *compressed_size = sz; - - *compressed = true; - - } else { - if (compressed) { - *compressed = false; - *compressed_size = 0; - } - - if (q & QCOW2_ZERO) { - /* We make no distinction between zero blocks and holes */ - *ret = 0; - return 0; - } - - q &= ~QCOW2_COPIED; - } - - *ret = q; - return q > 0; /* returns positive if not a hole */ -} - -static int verify_header(const Header *header) { - assert(header); - - if (HEADER_MAGIC(header) != QCOW2_MAGIC) - return -EBADMSG; - - if (HEADER_VERSION(header) != 2 && - HEADER_VERSION(header) != 3) - return -EOPNOTSUPP; - - if (HEADER_CRYPT_METHOD(header) != 0) - return -EOPNOTSUPP; - - if (HEADER_CLUSTER_BITS(header) < 9) /* 512K */ - return -EBADMSG; - - if (HEADER_CLUSTER_BITS(header) > 21) /* 2MB */ - return -EBADMSG; - - if (HEADER_SIZE(header) % HEADER_CLUSTER_SIZE(header) != 0) - return -EBADMSG; - - if (HEADER_L1_SIZE(header) > 32*1024*1024) /* 32MB */ - return -EBADMSG; - - if (HEADER_VERSION(header) == 3) { - - if (header->incompatible_features != 0) - return -EOPNOTSUPP; - - if (HEADER_HEADER_LENGTH(header) < sizeof(Header)) - return -EBADMSG; - } - - return 0; -} - -int qcow2_convert(int qcow2_fd, int raw_fd) { - _cleanup_free_ void *buffer1 = NULL, *buffer2 = NULL; - _cleanup_free_ be64_t *l1_table = NULL, *l2_table = NULL; - uint64_t sz, i; - Header header; - ssize_t l; - int r; - - l = pread(qcow2_fd, &header, sizeof(header), 0); - if (l < 0) - return -errno; - if (l != sizeof(header)) - return -EIO; - - r = verify_header(&header); - if (r < 0) - return r; - - l1_table = new(be64_t, HEADER_L1_SIZE(&header)); - if (!l1_table) - return -ENOMEM; - - l2_table = malloc(HEADER_CLUSTER_SIZE(&header)); - if (!l2_table) - return -ENOMEM; - - buffer1 = malloc(HEADER_CLUSTER_SIZE(&header)); - if (!buffer1) - return -ENOMEM; - - buffer2 = malloc(HEADER_CLUSTER_SIZE(&header)); - if (!buffer2) - return -ENOMEM; - - /* Empty the file if it exists, we rely on zero bits */ - if (ftruncate(raw_fd, 0) < 0) - return -errno; - - if (ftruncate(raw_fd, HEADER_SIZE(&header)) < 0) - return -errno; - - sz = sizeof(uint64_t) * HEADER_L1_SIZE(&header); - l = pread(qcow2_fd, l1_table, sz, HEADER_L1_TABLE_OFFSET(&header)); - if (l < 0) - return -errno; - if ((uint64_t) l != sz) - return -EIO; - - for (i = 0; i < HEADER_L1_SIZE(&header); i ++) { - uint64_t l2_begin, j; - - r = normalize_offset(&header, l1_table[i], &l2_begin, NULL, NULL); - if (r < 0) - return r; - if (r == 0) - continue; - - l = pread(qcow2_fd, l2_table, HEADER_CLUSTER_SIZE(&header), l2_begin); - if (l < 0) - return -errno; - if ((uint64_t) l != HEADER_CLUSTER_SIZE(&header)) - return -EIO; - - for (j = 0; j < HEADER_L2_SIZE(&header); j++) { - uint64_t data_begin, p, compressed_size; - bool compressed; - - p = ((i << HEADER_L2_BITS(&header)) + j) << HEADER_CLUSTER_BITS(&header); - - r = normalize_offset(&header, l2_table[j], &data_begin, &compressed, &compressed_size); - if (r < 0) - return r; - if (r == 0) - continue; - - if (compressed) - r = decompress_cluster( - qcow2_fd, data_begin, - raw_fd, p, - compressed_size, HEADER_CLUSTER_SIZE(&header), - buffer1, buffer2); - else - r = copy_cluster( - qcow2_fd, data_begin, - raw_fd, p, - HEADER_CLUSTER_SIZE(&header), buffer1); - if (r < 0) - return r; - } - } - - return 0; -} - -int qcow2_detect(int fd) { - be32_t id; - ssize_t l; - - l = pread(fd, &id, sizeof(id), 0); - if (l < 0) - return -errno; - if (l != sizeof(id)) - return -EIO; - - return htobe32(QCOW2_MAGIC) == id; -} |