// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2022 Oracle. All Rights Reserved. * Author: Darrick J. Wong */ #include "xfs.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" #include "scrub/scrub.h" #include "scrub/xfarray.h" #include "scrub/xfblob.h" #include "scrub/xfile.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( struct xfs_mount *mp, const char *description, struct xfblob **blobp) { struct xfblob *blob; struct xfile *xfile; int error; error = xfile_create(mp, 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_obj_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_obj_load(blob->xfile, ptr, key.xb_size, cookie + sizeof(key)); } /* Store a blob. */ int xfblob_store( struct xfblob *blob, xfblob_cookie *cookie, 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_obj_store(blob->xfile, &key, sizeof(key), pos); if (error) return error; pos += sizeof(key); error = xfile_obj_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_obj_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? */ long long xfblob_bytes( struct xfblob *blob) { struct xfile_stat statbuf; int error; error = xfile_stat(blob->xfile, &statbuf); if (error) return error; return statbuf.bytes; } /* Drop all the blobs. */ void xfblob_truncate( struct xfblob *blob) { xfile_discard(blob->xfile, 0, MAX_LFS_FILESIZE); blob->last_offset = 0; }