mirror of
https://github.com/torvalds/linux.git
synced 2024-12-01 16:41:39 +00:00
e47dcf113a
If the extended attributes look bad, try to sift through the rubble to find whatever keys/values we can, stage a new attribute structure in a temporary file and use the atomic extent swapping mechanism to commit the results in bulk. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de>
169 lines
3.2 KiB
C
169 lines
3.2 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;
|
|
}
|
|
|
|
/* How many bytes is this blob storage object consuming? */
|
|
unsigned long long
|
|
xfblob_bytes(
|
|
struct xfblob *blob)
|
|
{
|
|
return xfile_bytes(blob->xfile);
|
|
}
|
|
|
|
/* Drop all the blobs. */
|
|
void
|
|
xfblob_truncate(
|
|
struct xfblob *blob)
|
|
{
|
|
xfile_discard(blob->xfile, PAGE_SIZE, MAX_LFS_FILESIZE - PAGE_SIZE);
|
|
blob->last_offset = PAGE_SIZE;
|
|
}
|