mirror_ubuntu-kernels/fs/xfs/scrub/xfblob.c
Darrick J. Wong d2bd7eef4f xfs: create a blob array data structure
Create a simple 'blob array' data structure for storage of arbitrarily
sized metadata objects that will be used to reconstruct metadata.  For
the intended usage (temporarily storing extended attribute names and
values) we only have to support storing objects and retrieving them.
Use the xfile abstraction to store the attribute information in memory
that can be swapped out.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
2024-04-15 14:58:53 -07:00

152 lines
2.8 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2021-2024 Oracle. All Rights Reserved.
* Author: Darrick J. Wong <djwong@kernel.org>
*/
#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_shared.h"
#include "xfs_format.h"
#include "scrub/scrub.h"
#include "scrub/xfile.h"
#include "scrub/xfarray.h"
#include "scrub/xfblob.h"
/*
* XFS Blob Storage
* ================
* Stores and retrieves blobs using an xfile. Objects are appended to the file
* and the offset is returned as a magic cookie for retrieval.
*/
#define XB_KEY_MAGIC 0xABAADDAD
struct xb_key {
uint32_t xb_magic; /* XB_KEY_MAGIC */
uint32_t xb_size; /* size of the blob, in bytes */
loff_t xb_offset; /* byte offset of this key */
/* blob comes after here */
} __packed;
/* Initialize a blob storage object. */
int
xfblob_create(
const char *description,
struct xfblob **blobp)
{
struct xfblob *blob;
struct xfile *xfile;
int error;
error = xfile_create(description, 0, &xfile);
if (error)
return error;
blob = kmalloc(sizeof(struct xfblob), XCHK_GFP_FLAGS);
if (!blob) {
error = -ENOMEM;
goto out_xfile;
}
blob->xfile = xfile;
blob->last_offset = PAGE_SIZE;
*blobp = blob;
return 0;
out_xfile:
xfile_destroy(xfile);
return error;
}
/* Destroy a blob storage object. */
void
xfblob_destroy(
struct xfblob *blob)
{
xfile_destroy(blob->xfile);
kfree(blob);
}
/* Retrieve a blob. */
int
xfblob_load(
struct xfblob *blob,
xfblob_cookie cookie,
void *ptr,
uint32_t size)
{
struct xb_key key;
int error;
error = xfile_load(blob->xfile, &key, sizeof(key), cookie);
if (error)
return error;
if (key.xb_magic != XB_KEY_MAGIC || key.xb_offset != cookie) {
ASSERT(0);
return -ENODATA;
}
if (size < key.xb_size) {
ASSERT(0);
return -EFBIG;
}
return xfile_load(blob->xfile, ptr, key.xb_size,
cookie + sizeof(key));
}
/* Store a blob. */
int
xfblob_store(
struct xfblob *blob,
xfblob_cookie *cookie,
const void *ptr,
uint32_t size)
{
struct xb_key key = {
.xb_offset = blob->last_offset,
.xb_magic = XB_KEY_MAGIC,
.xb_size = size,
};
loff_t pos = blob->last_offset;
int error;
error = xfile_store(blob->xfile, &key, sizeof(key), pos);
if (error)
return error;
pos += sizeof(key);
error = xfile_store(blob->xfile, ptr, size, pos);
if (error)
goto out_err;
*cookie = blob->last_offset;
blob->last_offset += sizeof(key) + size;
return 0;
out_err:
xfile_discard(blob->xfile, blob->last_offset, sizeof(key));
return error;
}
/* Free a blob. */
int
xfblob_free(
struct xfblob *blob,
xfblob_cookie cookie)
{
struct xb_key key;
int error;
error = xfile_load(blob->xfile, &key, sizeof(key), cookie);
if (error)
return error;
if (key.xb_magic != XB_KEY_MAGIC || key.xb_offset != cookie) {
ASSERT(0);
return -ENODATA;
}
xfile_discard(blob->xfile, cookie, sizeof(key) + key.xb_size);
return 0;
}