|  | // 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; | 
|  | } |