/* SPDX-License-Identifier: GPL-2.0 */ /* * xloop_file_fmt_qcow_cluster.c * * Ported QCOW2 implementation of the QEMU project (GPL-2.0): * Cluster calculation and lookup for the QCOW2 format. * * The copyright (C) 2004-2006 of the original code is owned by Fabrice Bellard. * * Copyright (C) 2019 Manuel Bentele */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include "xloop_file_fmt.h" #include "xloop_file_fmt_qcow_main.h" #include "xloop_file_fmt_qcow_cache.h" #include "xloop_file_fmt_qcow_cluster.h" /* * __xloop_file_fmt_qcow_cluster_l2_load * * @xlo_fmt: QCOW file format * @offset: A guest offset, used to calculate what slice of the L2 * table to load. * @l2_offset: Offset to the L2 table in the image file. * @l2_slice: Location to store the pointer to the L2 slice. * * Loads a L2 slice into memory (L2 slices are the parts of L2 tables * that are loaded by the qcow2 cache). If the slice is in the cache, * the cache is used; otherwise the L2 slice is loaded from the image * file. */ static int __xloop_file_fmt_qcow_cluster_l2_load(struct xloop_file_fmt *xlo_fmt, u64 offset, u64 l2_offset, u64 **l2_slice) { struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; int start_of_slice = xloop_file_fmt_qcow_l2_entry_size(qcow_data) * ( xloop_file_fmt_qcow_offset_to_l2_index(qcow_data, offset) - xloop_file_fmt_qcow_offset_to_l2_slice_index(qcow_data, offset) ); ASSERT(qcow_data->l2_table_cache != NULL); return xloop_file_fmt_qcow_cache_get(xlo_fmt, l2_offset + start_of_slice, (void **) l2_slice); } /* * For a given L2 entry, count the number of contiguous subclusters of * the same type starting from @sc_from. Compressed clusters are * treated as if they were divided into subclusters of size * qcow_data->subcluster_size. * * Return the number of contiguous subclusters and set @type to the * subcluster type. * * If the L2 entry is invalid return -errno and set @type to * QCOW_SUBCLUSTER_INVALID. */ static int __xloop_file_fmt_qcow_get_subcluster_range_type( struct xloop_file_fmt *xlo_fmt, u64 l2_entry, u64 l2_bitmap, unsigned int sc_from, enum xloop_file_fmt_qcow_subcluster_type *type) { struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; u32 val; *type = xloop_file_fmt_qcow_get_subcluster_type(xlo_fmt, l2_entry, l2_bitmap, sc_from); if (*type == QCOW_SUBCLUSTER_INVALID) { return -EINVAL; } else if (!xloop_file_fmt_qcow_has_subclusters(qcow_data) || *type == QCOW_SUBCLUSTER_COMPRESSED) { return qcow_data->subclusters_per_cluster - sc_from; } switch (*type) { case QCOW_SUBCLUSTER_NORMAL: val = l2_bitmap | QCOW_OFLAG_SUB_ALLOC_RANGE(0, sc_from); return __builtin_ctz(~val) - sc_from; case QCOW_SUBCLUSTER_ZERO_PLAIN: case QCOW_SUBCLUSTER_ZERO_ALLOC: val = (l2_bitmap | QCOW_OFLAG_SUB_ZERO_RANGE(0, sc_from)) >> 32; return __builtin_ctz(~val) - sc_from; case QCOW_SUBCLUSTER_UNALLOCATED_PLAIN: case QCOW_SUBCLUSTER_UNALLOCATED_ALLOC: val = ((l2_bitmap >> 32) | l2_bitmap) & ~QCOW_OFLAG_SUB_ALLOC_RANGE(0, sc_from); return __builtin_ctz(val) - sc_from; default: /* not reachable */ ASSERT(false); *type = QCOW_SUBCLUSTER_INVALID; return 0; } } /* * Return the number of contiguous subclusters of the exact same type * in a given L2 slice, starting from cluster @l2_index, subcluster * @sc_index. Allocated subclusters are required to be contiguous in * the image file. * At most @nb_clusters are checked (note that this means clusters, * not subclusters). * Compressed clusters are always processed one by one but for the * purpose of this count they are treated as if they were divided into * subclusters of size qcow_data->subcluster_size. * On failure return -errno and update @l2_index to point to the * invalid entry. */ static int __xloop_file_fmt_qcow_count_contiguous_subclusters( struct xloop_file_fmt *xlo_fmt, int nb_clusters, unsigned int sc_index, u64 *l2_slice, unsigned int *l2_index) { struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; int i, count = 0; bool check_offset = false; u64 expected_offset = 0; enum xloop_file_fmt_qcow_subcluster_type expected_type = QCOW_SUBCLUSTER_NORMAL; enum xloop_file_fmt_qcow_subcluster_type type; ASSERT(*l2_index + nb_clusters <= qcow_data->l2_slice_size); for (i = 0; i < nb_clusters; i++) { unsigned int first_sc = (i == 0) ? sc_index : 0; u64 l2_entry = xloop_file_fmt_qcow_get_l2_entry(qcow_data, l2_slice, *l2_index + i); u64 l2_bitmap = xloop_file_fmt_qcow_get_l2_bitmap(qcow_data, l2_slice, *l2_index + i); int ret = __xloop_file_fmt_qcow_get_subcluster_range_type(xlo_fmt, l2_entry, l2_bitmap, first_sc, &type); if (ret < 0) { *l2_index += i; /* Point to the invalid entry */ return -EIO; } if (i == 0) { if (type == QCOW_SUBCLUSTER_COMPRESSED) { /* Compressed clusters are always processed one by one */ return ret; } expected_type = type; expected_offset = l2_entry & QCOW_L2E_OFFSET_MASK; check_offset = (type == QCOW_SUBCLUSTER_NORMAL || type == QCOW_SUBCLUSTER_ZERO_ALLOC || type == QCOW_SUBCLUSTER_UNALLOCATED_ALLOC); } else if (type != expected_type) { break; } else if (check_offset) { expected_offset += qcow_data->cluster_size; if (expected_offset != (l2_entry & QCOW_L2E_OFFSET_MASK)) { break; } } count += ret; /* Stop if there are type changes before the end of the cluster */ if (first_sc + ret < qcow_data->subclusters_per_cluster) { break; } } return count; } /* * xloop_file_fmt_qcow_get_host_offset * * For a given offset of the virtual disk find the equivalent host * offset in the qcow2 file and store it in *host_offset. Neither * offset needs to be aligned to a cluster boundary. * * If the cluster is unallocated then *host_offset will be 0. * If the cluster is compressed then *host_offset will contain the * complete compressed cluster descriptor. * * On entry, *bytes is the maximum number of contiguous bytes starting at * offset that we are interested in. * * On exit, *bytes is the number of bytes starting at offset that have the same * subcluster type and (if applicable) are stored contiguously in the image * file. The subcluster type is stored in *subcluster_type. * Compressed clusters are always processed one by one. * * Returns 0 on success, -errno in error cases. */ int xloop_file_fmt_qcow_get_host_offset(struct xloop_file_fmt *xlo_fmt, u64 offset, unsigned int *bytes, u64 *host_offset, enum xloop_file_fmt_qcow_subcluster_type *subcluster_type) { struct xloop_file_fmt_qcow_data *qcow_data = xlo_fmt->private_data; unsigned int l2_index, sc_index; u64 l1_index, l2_offset, *l2_slice, l2_entry, l2_bitmap; int sc; unsigned int offset_in_cluster; u64 bytes_available, bytes_needed, nb_clusters; enum xloop_file_fmt_qcow_subcluster_type type; int ret; u64 host_cluster_offset; offset_in_cluster = xloop_file_fmt_qcow_offset_into_cluster(qcow_data, offset); bytes_needed = (u64) *bytes + offset_in_cluster; /* compute how many bytes there are between the start of the cluster * containing offset and the end of the l2 slice that contains * the entry pointing to it */ bytes_available = ((u64)( qcow_data->l2_slice_size - xloop_file_fmt_qcow_offset_to_l2_slice_index(qcow_data, offset)) ) << qcow_data->cluster_bits; if (bytes_needed > bytes_available) { bytes_needed = bytes_available; } *host_offset = 0; /* seek to the l2 offset in the l1 table */ l1_index = xloop_file_fmt_qcow_offset_to_l1_index(qcow_data, offset); if (l1_index >= qcow_data->l1_size) { type = QCOW_SUBCLUSTER_UNALLOCATED_PLAIN; goto out; } l2_offset = qcow_data->l1_table[l1_index] & QCOW_L1E_OFFSET_MASK; if (!l2_offset) { type = QCOW_SUBCLUSTER_UNALLOCATED_PLAIN; goto out; } if (xloop_file_fmt_qcow_offset_into_cluster(qcow_data, l2_offset)) { dev_err_ratelimited(xloop_file_fmt_to_dev(xlo_fmt), "L2 table offset " "%llx unaligned (L1 index: %llx)", l2_offset, l1_index); return -EIO; } /* load the l2 slice in memory */ ret = __xloop_file_fmt_qcow_cluster_l2_load(xlo_fmt, offset, l2_offset, &l2_slice); if (ret < 0) { return ret; } /* find the cluster offset for the given disk offset */ l2_index = xloop_file_fmt_qcow_offset_to_l2_slice_index(qcow_data, offset); sc_index = xloop_file_fmt_qcow_offset_to_sc_index(qcow_data, offset); l2_entry = xloop_file_fmt_qcow_get_l2_entry(qcow_data, l2_slice, l2_index); l2_bitmap = xloop_file_fmt_qcow_get_l2_bitmap(qcow_data, l2_slice, l2_index); nb_clusters = xloop_file_fmt_qcow_size_to_clusters(qcow_data, bytes_needed); /* bytes_needed <= *bytes + offset_in_cluster, both of which are * unsigned integers; the minimum cluster size is 512, so this * assertion is always true */ ASSERT(nb_clusters <= INT_MAX); type = xloop_file_fmt_qcow_get_subcluster_type(xlo_fmt, l2_entry, l2_bitmap, sc_index); if (qcow_data->qcow_version < 3 && ( type == QCOW_SUBCLUSTER_ZERO_PLAIN || type == QCOW_SUBCLUSTER_ZERO_ALLOC)) { dev_err_ratelimited(xloop_file_fmt_to_dev(xlo_fmt), "zero cluster " "entry found in pre-v3 image (L2 offset: %llx, L2 index: %x)\n", l2_offset, l2_index); ret = -EIO; goto fail; } switch (type) { case QCOW_SUBCLUSTER_INVALID: break; /* This is handled by count_contiguous_subclusters() below */ case QCOW_SUBCLUSTER_COMPRESSED: if (xloop_file_fmt_qcow_has_data_file(qcow_data)) { dev_err_ratelimited(xloop_file_fmt_to_dev(xlo_fmt), "compressed " "cluster entry found in image with external data file " "(L2 offset: %llx, L2 index: %x)\n", l2_offset, l2_index); ret = -EIO; goto fail; } *host_offset = l2_entry & QCOW_L2E_COMPRESSED_OFFSET_SIZE_MASK; break; case QCOW_SUBCLUSTER_ZERO_PLAIN: case QCOW_SUBCLUSTER_UNALLOCATED_PLAIN: break; case QCOW_SUBCLUSTER_ZERO_ALLOC: case QCOW_SUBCLUSTER_NORMAL: case QCOW_SUBCLUSTER_UNALLOCATED_ALLOC: host_cluster_offset = l2_entry & QCOW_L2E_OFFSET_MASK; *host_offset = host_cluster_offset + offset_in_cluster; if (xloop_file_fmt_qcow_offset_into_cluster(qcow_data, host_cluster_offset)) { dev_err_ratelimited(xloop_file_fmt_to_dev(xlo_fmt), "cluster " "allocation offset %llx unaligned (L2 offset: %llx, " "L2 index: %x)\n", host_cluster_offset, l2_offset, l2_index); ret = -EIO; goto fail; } if (xloop_file_fmt_qcow_has_data_file(qcow_data) && *host_offset != offset) { dev_err_ratelimited(xloop_file_fmt_to_dev(xlo_fmt), "external " "data file host cluster offset %llx does not match guest " "cluster offset: %llx, L2 index: %x)\n", host_cluster_offset, offset - offset_in_cluster, l2_index); ret = -EIO; goto fail; } break; default: BUG(); } sc = __xloop_file_fmt_qcow_count_contiguous_subclusters(xlo_fmt, nb_clusters, sc_index, l2_slice, &l2_index); if (sc < 0) { dev_err_ratelimited(xloop_file_fmt_to_dev(xlo_fmt), "invalid cluster " "entry found (L2 offset: %#llx, L2 index: %#x)", l2_offset, l2_index); ret = -EIO; goto fail; } xloop_file_fmt_qcow_cache_put(xlo_fmt, (void **) &l2_slice); bytes_available = ((s64) sc + sc_index) << qcow_data->subcluster_bits; out: if (bytes_available > bytes_needed) { bytes_available = bytes_needed; } /* bytes_available <= bytes_needed <= *bytes + offset_in_cluster; * subtracting offset_in_cluster will therefore definitely yield * something not exceeding UINT_MAX */ ASSERT(bytes_available - offset_in_cluster <= UINT_MAX); *bytes = bytes_available - offset_in_cluster; *subcluster_type = type; return 0; fail: xloop_file_fmt_qcow_cache_put(xlo_fmt, (void **) &l2_slice); return ret; }