File diskdump-Support-flattened-diskdump-format.patch of Package libkdumpfile.36085
From: Petr Tesarik <petr@tesarici.cz>
Date: Mon, 6 Nov 2023 11:46:03 +0100
Subject: diskdump: Support flattened diskdump format
References: bsc#1223399
Upstream: merged
Git-commit: ee81ef499225feb4d8cbfa51a86123e036813297
Allow reading flattened diskdump files directly.
The mapping from rearranged file offsets to flattened file offsets is
implemented using libaddrxlat's addrxlat_map_t, which is normally used to
map an address to a translation method. Here it maps the rearranged file
offset to an index into an array of flattened file offset. This is a hack,
but it allows to reuse the non-trivial logic in addrxlat_map_set(), which
avoids the need to walk all segments in case some (rearranged) offset was
written more than once.
[ ptesarik: Drop tests and NEWS. Tests would require backporting
many additional patches, and NEWS is pointless. ]
Signed-off-by: Petr Tesarik <ptesarik@suse.com>
---
src/kdumpfile/diskdump.c | 303 ++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 289 insertions(+), 14 deletions(-)
create mode 100755 tests/diskdump-flat-raw
--- a/src/kdumpfile/diskdump.c
+++ b/src/kdumpfile/diskdump.c
@@ -47,6 +47,27 @@
/** @cond TARGET_ABI */
+#define MDF_SIGNATURE "makedumpfile"
+#define MDF_SIG_LEN 16
+#define MDF_TYPE_FLAT_HEADER 1
+#define MDF_VERSION_FLAT_HEADER 1
+#define MDF_HEADER_SIZE 4096
+
+/* Flattened format header. */
+struct makedumpfile_header {
+ char signature[MDF_SIG_LEN];
+ int64_t type;
+ int64_t version;
+} __attribute__((packed));
+
+/* Flattened segment header */
+struct makedumpfile_data_header {
+ int64_t offset;
+ int64_t buf_size;
+} __attribute__((packed));
+
+#define MDF_OFFSET_END_FLAG (-(int64_t)1)
+
/* The header is architecture-dependent, unfortunately */
struct disk_dump_header_32 {
char signature[SIG_LEN]; /* = "DISKDUMP" */
@@ -168,6 +189,12 @@ struct disk_dump_priv {
/** Overridden methods for arch.page_size attribute. */
struct attr_override page_size_override;
int cbuf_slot; /**< Compressed data per-context slot. */
+
+ /** File offset mapping for flattened files. */
+ addrxlat_map_t *flatmap;
+
+ /** Differences between flattened and rearranged file offsets. */
+ off_t *flatoffs;
};
struct setup_data {
@@ -190,6 +217,120 @@ struct setup_data {
static void diskdump_cleanup(struct kdump_shared *shared);
+/** Read buffer from a flattened dump file.
+ * @param ctx Dump file object.
+ * @param buf Target I/O buffer.
+ * @param len Length of data.
+ * @param pos File position.
+ * @returns Error status.
+ *
+ * Read data from the flattened segment(s) which contain(s) @p len bytes
+ * at position @p pos after rearrangement.
+ */
+static kdump_status
+flattened_pread(kdump_ctx_t *ctx, void *buf, size_t len, off_t pos)
+{
+ struct disk_dump_priv *ddp = ctx->shared->fmtdata;
+ const addrxlat_range_t *range, *end;
+ off_t off;
+
+ range = addrxlat_map_ranges(ddp->flatmap);
+ end = range + addrxlat_map_len(ddp->flatmap);
+ for (off = pos; range < end && off > range->endoff; ++range)
+ off -= range->endoff + 1;
+ while (range < end && len) {
+ size_t seglen;
+
+ seglen = range->endoff + 1 - off;
+ if (seglen > len)
+ seglen = len;
+
+ if (range->meth != ADDRXLAT_SYS_METH_NONE) {
+ unsigned segidx = range->meth;
+ kdump_status ret;
+
+ ret = fcache_pread(ctx->shared->fcache, buf, seglen,
+ pos + ddp->flatoffs[segidx]);
+ if (ret != KDUMP_OK)
+ return ret;
+ } else
+ memset(buf, 0, seglen);
+
+ buf += seglen;
+ len -= seglen;
+ pos += seglen;
+ ++range;
+ off = 0;
+ }
+
+ if (len)
+ memset(buf, 0, len);
+ return KDUMP_OK;
+}
+
+/** Read buffer from a diskdump file.
+ * @param ctx Dump file object.
+ * @param buf Target I/O buffer.
+ * @param len Length of data.
+ * @param pos File position.
+ * @returns Error status.
+ *
+ * Read data from a diskdump file. If the file is flattened, interpret
+ * @p pos as if it was already rearranged.
+ */
+static inline kdump_status
+diskdump_pread(kdump_ctx_t *ctx, void *buf, size_t len, off_t pos)
+{
+ struct disk_dump_priv *ddp = ctx->shared->fmtdata;
+
+ return ddp->flatmap
+ ? flattened_pread(ctx, buf, len, pos)
+ : fcache_pread(ctx->shared->fcache, buf, len, pos);
+}
+
+/** Get a contiguous data chunk from a flattened dump file.
+ * @param ctx Dump file object.
+ * @param fch File cache chunk, updated on success.
+ * @param len Length of data.
+ * @param pos File position.
+ * @returns Error status.
+ *
+ * Get a contiguous data chunk from a flattened dump file. Currently, this is
+ * implemented using a dynamically allocated buffer, even if the underlying
+ * file cache buffer might be used.
+ */
+static inline kdump_status
+flattened_get_chunk(kdump_ctx_t *ctx, struct fcache_chunk *fch,
+ size_t len, off_t pos)
+{
+ fch->data = malloc(len);
+ if (!fch->data)
+ return KDUMP_ERR_SYSTEM;
+ fch->nent = 0;
+ return flattened_pread(ctx, fch->data, len, pos);
+}
+
+/** Get a contiguous data chunk from a diskdump file.
+ * @param ctx Dump file object.
+ * @param fch File cache chunk, updated on success.
+ * @param len Length of data.
+ * @param pos File position.
+ * @returns Error status.
+ *
+ * Get a contiguous data chunk from a diskdump file. If the file is
+ * flattened, interpret @p pos as if it was already rearranged.
+ */
+static inline kdump_status
+diskdump_get_chunk(kdump_ctx_t *ctx, struct fcache_chunk *fch,
+ size_t len, off_t pos)
+{
+ struct disk_dump_priv *ddp = ctx->shared->fmtdata;
+
+ return ddp->flatmap
+ ? flattened_get_chunk(ctx, fch, len, pos)
+ : fcache_get_chunk(ctx->shared->fcache, fch, len, pos);
+}
+
/** Add a new PFN region.
* @param ctx Dump file context.
* @param rgn PFN region.
@@ -382,7 +523,7 @@ diskdump_read_page(kdump_ctx_t *ctx, str
}
mutex_lock(&ctx->shared->cache_lock);
- ret = fcache_pread(ctx->shared->fcache, &pd, sizeof pd, pd_pos);
+ ret = diskdump_pread(ctx, &pd, sizeof pd, pd_pos);
mutex_unlock(&ctx->shared->cache_lock);
if (ret != KDUMP_OK)
return set_error(ctx, ret,
@@ -410,7 +551,7 @@ diskdump_read_page(kdump_ctx_t *ctx, str
/* read page data */
mutex_lock(&ctx->shared->cache_lock);
- ret = fcache_pread(ctx->shared->fcache, buf, pd.size, pd.offset);
+ ret = diskdump_pread(ctx, buf, pd.size, pd.offset);
mutex_unlock(&ctx->shared->cache_lock);
if (ret != KDUMP_OK)
return set_error(ctx, ret,
@@ -509,7 +650,7 @@ read_vmcoreinfo(kdump_ctx_t *ctx, off_t
kdump_attr_value_t val;
kdump_status ret;
- ret = fcache_get_chunk(ctx->shared->fcache, &fch, size, off);
+ ret = diskdump_get_chunk(ctx, &fch, size, off);
if (ret != KDUMP_OK)
return set_error(ctx, ret,
"Cannot read %zu VMCOREINFO bytes at %llu",
@@ -535,7 +676,7 @@ read_notes(kdump_ctx_t *ctx, off_t off,
struct fcache_chunk fch;
kdump_status ret;
- ret = fcache_get_chunk(ctx->shared->fcache, &fch, size, off);
+ ret = diskdump_get_chunk(ctx, &fch, size, off);
if (ret != KDUMP_OK)
return set_error(ctx, ret,
"Cannot read %zu note bytes at %llu",
@@ -632,7 +773,7 @@ read_bitmap(kdump_ctx_t *ctx, int32_t su
if (get_max_pfn(ctx) > max_bitmap_pfn)
set_max_pfn(ctx, max_bitmap_pfn);
- ret = fcache_get_chunk(ctx->shared->fcache, &fch, bitmapsize, off);
+ ret = diskdump_get_chunk(ctx, &fch, bitmapsize, off);
if (ret != KDUMP_OK)
return set_error(ctx, ret,
"Cannot read %zu bytes of page bitmap"
@@ -707,8 +848,8 @@ read_sub_hdr_32(struct setup_data *sdp,
if (header_version < 1)
return KDUMP_OK;
- ret = fcache_pread(ctx->shared->fcache, &subhdr, sizeof subhdr,
- get_page_size(ctx));
+ ret = diskdump_pread(ctx, &subhdr, sizeof subhdr,
+ get_page_size(ctx));
if (ret != KDUMP_OK)
return set_error(ctx, ret,
"Cannot read subheader");
@@ -790,8 +931,8 @@ read_sub_hdr_64(struct setup_data *sdp,
if (header_version < 1)
return KDUMP_OK;
- ret = fcache_pread(ctx->shared->fcache, &subhdr, sizeof subhdr,
- get_page_size(ctx));
+ ret = diskdump_pread(ctx, &subhdr, sizeof subhdr,
+ get_page_size(ctx));
if (ret != KDUMP_OK)
return set_error(ctx, ret,
"Cannot read subheader");
@@ -940,32 +1081,162 @@ open_common(kdump_ctx_t *ctx, void *hdr)
return ret;
}
+#define FLATOFFS_ALLOC_INC 32
+
+/** Initialize flattened dump maps for one file.
+ * @param ctx Dump file object.
+ * @returns Error status.
+ *
+ * Read all flattened segment headers and initialize
+ * @p flatmap and @p flatoffs.
+ */
+static kdump_status
+init_flattened_file(kdump_ctx_t *ctx)
+{
+ struct disk_dump_priv *ddp = ctx->shared->fmtdata;
+ struct makedumpfile_data_header hdr;
+ addrxlat_range_t range;
+ int64_t pos, size;
+ unsigned segidx;
+ off_t flatpos;
+ kdump_status status;
+
+ ddp->flatmap = addrxlat_map_new();
+ if (!ddp->flatmap)
+ return set_error(ctx, KDUMP_ERR_SYSTEM,
+ "Cannot allocate %s", "flattened map");
+
+ segidx = 0;
+ flatpos = MDF_HEADER_SIZE;
+ for (;;) {
+ status = fcache_pread(ctx->shared->fcache, &hdr, sizeof(hdr),
+ flatpos);
+ if (status != KDUMP_OK)
+ return set_error(ctx, status,
+ "Cannot read flattened header at %llu",
+ (unsigned long long) flatpos);
+ pos = be64toh(hdr.offset);
+ if (pos == MDF_OFFSET_END_FLAG)
+ break;
+ if (pos < 0)
+ return set_error(ctx, KDUMP_ERR_CORRUPT,
+ "Wrong flattened %s %"PRId64" at %llu",
+ "offset", pos,
+ (unsigned long long) flatpos);
+ size = be64toh(hdr.buf_size);
+ if (size <= 0)
+ return set_error(ctx, KDUMP_ERR_CORRUPT,
+ "Wrong flattened %s %"PRId64" at %llu",
+ "segment size", size,
+ (unsigned long long) flatpos);
+
+ if ((segidx % FLATOFFS_ALLOC_INC) == 0) {
+ unsigned newlen = segidx + FLATOFFS_ALLOC_INC;
+ off_t *newbuf;
+
+ newbuf = realloc(ddp->flatoffs,
+ sizeof(*ddp->flatoffs) * newlen);
+ if (!newbuf)
+ return set_error(ctx, KDUMP_ERR_SYSTEM,
+ "Cannot allocate %s",
+ "flattened offset array");
+ ddp->flatoffs = newbuf;
+ }
+ flatpos += sizeof(hdr);
+ ddp->flatoffs[segidx] = flatpos - pos;
+
+ range.endoff = size - 1;
+ range.meth = segidx;
+ if (addrxlat_map_set(ddp->flatmap, pos, &range) != ADDRXLAT_OK)
+ return set_error(ctx, KDUMP_ERR_SYSTEM,
+ "Cannot allocate %s",
+ "flattened map entry");
+
+ ++segidx;
+ flatpos += size;
+ }
+ return KDUMP_OK;
+}
+
+/** Initialize flattened dump maps for all files.
+ * @param ctx Dump file object.
+ * @returns Error status.
+ *
+ * Initialize flattened dump maps for all files.
+ */
+static kdump_status
+init_flattened_maps(kdump_ctx_t *ctx)
+{
+ kdump_status status;
+
+ status = init_flattened_file(ctx);
+ if (status != KDUMP_OK)
+ return set_error(ctx, status,
+ "Cannot rearrange file");
+
+ return KDUMP_OK;
+}
+
static kdump_status
diskdump_probe(kdump_ctx_t *ctx)
{
+ static const char magic_flattened[MDF_SIG_LEN] = MDF_SIGNATURE;
static const char magic_diskdump[] =
{ 'D', 'I', 'S', 'K', 'D', 'U', 'M', 'P' };
static const char magic_kdump[] =
{ 'K', 'D', 'U', 'M', 'P', ' ', ' ', ' ' };
char hdr[sizeof(struct disk_dump_header_64)];
+ char desc[32];
kdump_status status;
status = fcache_pread(ctx->shared->fcache, hdr, sizeof hdr, 0);
if (status != KDUMP_OK)
return set_error(ctx, status, "Cannot read dump header");
+ if (!memcmp(hdr, magic_flattened, sizeof magic_flattened)) {
+ struct makedumpfile_header *flathdr =
+ (struct makedumpfile_header*) hdr;
+
+ if (be64toh(flathdr->type) != MDF_TYPE_FLAT_HEADER)
+ return set_error(ctx, KDUMP_ERR_NOTIMPL,
+ "Unknown flattened %s: %" PRId64 "\n",
+ "type", be64toh(flathdr->type));
+ if (be64toh(flathdr->version) != MDF_VERSION_FLAT_HEADER)
+ return set_error(ctx, KDUMP_ERR_NOTIMPL,
+ "Unknown flattened %s: %" PRId64 "\n",
+ "version", be64toh(flathdr->version));
+
+ status = init_private(ctx);
+ if (status != KDUMP_OK)
+ return status;
+
+ status = init_flattened_maps(ctx);
+ if (status != KDUMP_OK)
+ return status;
+
+ status = flattened_pread(ctx, &hdr, sizeof hdr, 0);
+ if (status != KDUMP_OK)
+ return set_error(ctx, status, "Cannot read dump header");
+ strcpy(desc, "Flattened ");
+ } else
+ desc[0] = '\0';
+
if (!memcmp(hdr, magic_diskdump, sizeof magic_diskdump))
- set_file_description(ctx, "Diskdump");
+ strcat(desc, "Diskdump");
else if (!memcmp(hdr, magic_kdump, sizeof magic_kdump))
- set_file_description(ctx, "Compressed KDUMP");
+ strcat(desc, "Compressed KDUMP");
else
return set_error(ctx, KDUMP_NOPROBE,
"Unrecognized diskdump signature");
- status = init_private(ctx);
- if (status != KDUMP_OK)
- return status;
+ set_file_description(ctx, desc);
+
+ if (!ctx->shared->fmtdata) {
+ status = init_private(ctx);
+ if (status != KDUMP_OK)
+ return status;
+ }
return open_common(ctx, hdr);
}
@@ -989,6 +1260,10 @@ diskdump_cleanup(struct kdump_shared *sh
free(ddp->pfn_rgn);
if (ddp->cbuf_slot >= 0)
per_ctx_free(shared, ddp->cbuf_slot);
+ if (ddp->flatmap)
+ addrxlat_map_decref(ddp->flatmap);
+ if (ddp->flatoffs)
+ free(ddp->flatoffs);
free(ddp);
shared->fmtdata = NULL;
}