blob: 3cd678dbbab928697e6c615a4ec769ae1f5dbd93 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2023-2024 Oracle. All Rights Reserved.
* Author: Darrick J. Wong <djwong@kernel.org>
*/
#include "libxfs.h"
#include "libxfs/xfile.h"
#include "libxfs/xfblob.h"
#include "repair/strblobs.h"
/*
* String Blob Structure
* =====================
*
* This data structure wraps the storage of strings with explicit length in an
* xfblob structure. It stores a hashtable of string checksums to provide
* fast(ish) lookups of existing strings to enable deduplication of the strings
* contained within.
*/
struct strblob_hashent {
struct strblob_hashent *next;
xfblob_cookie str_cookie;
unsigned int str_len;
xfs_dahash_t str_hash;
};
struct strblobs {
struct xfblob *strings;
unsigned int nr_buckets;
struct strblob_hashent *buckets[];
};
static inline size_t strblobs_sizeof(unsigned int nr_buckets)
{
return sizeof(struct strblobs) +
(nr_buckets * sizeof(struct strblobs_hashent *));
}
/* Initialize a string blob structure. */
int
strblobs_init(
const char *descr,
unsigned int hash_buckets,
struct strblobs **sblobs)
{
struct strblobs *sb;
int error;
sb = calloc(strblobs_sizeof(hash_buckets), 1);
if (!sb)
return ENOMEM;
error = -xfblob_create(descr, &sb->strings);
if (error)
goto out_free;
sb->nr_buckets = hash_buckets;
*sblobs = sb;
return 0;
out_free:
free(sb);
return error;
}
/* Deconstruct a string blob structure. */
void
strblobs_destroy(
struct strblobs **sblobs)
{
struct strblobs *sb = *sblobs;
struct strblob_hashent *ent, *ent_next;
unsigned int bucket;
for (bucket = 0; bucket < sb->nr_buckets; bucket++) {
ent = sb->buckets[bucket];
while (ent != NULL) {
ent_next = ent->next;
free(ent);
ent = ent_next;
}
}
xfblob_destroy(sb->strings);
free(sb);
*sblobs = NULL;
}
/*
* Search the string hashtable for a matching entry. Sets sets the cookie and
* returns 0 if one is found; ENOENT if there is no match; or a positive errno.
*/
static int
__strblobs_lookup(
struct strblobs *sblobs,
xfblob_cookie *str_cookie,
const unsigned char *str,
unsigned int str_len,
xfs_dahash_t str_hash)
{
struct strblob_hashent *ent;
unsigned char *buf = NULL;
unsigned int bucket;
int error;
bucket = str_hash % sblobs->nr_buckets;
ent = sblobs->buckets[bucket];
for (ent = sblobs->buckets[bucket]; ent != NULL; ent = ent->next) {
if (ent->str_len != str_len || ent->str_hash != str_hash)
continue;
if (!buf) {
buf = malloc(str_len);
if (!buf)
return ENOMEM;
}
error = strblobs_load(sblobs, ent->str_cookie, buf, str_len);
if (error)
goto out;
if (memcmp(str, buf, str_len))
continue;
*str_cookie = ent->str_cookie;
goto out;
}
error = ENOENT;
out:
free(buf);
return error;
}
/*
* Search the string hashtable for a matching entry. Sets sets the cookie and
* returns 0 if one is found; ENOENT if there is no match; or a positive errno.
*/
int
strblobs_lookup(
struct strblobs *sblobs,
xfblob_cookie *str_cookie,
const unsigned char *str,
unsigned int str_len,
xfs_dahash_t str_hash)
{
return __strblobs_lookup(sblobs, str_cookie, str, str_len, str_hash);
}
/* Remember a string in the hashtable. */
static int
strblobs_hash(
struct strblobs *sblobs,
xfblob_cookie str_cookie,
const unsigned char *str,
unsigned int str_len,
xfs_dahash_t str_hash)
{
struct strblob_hashent *ent;
unsigned int bucket;
bucket = str_hash % sblobs->nr_buckets;
ent = malloc(sizeof(struct strblob_hashent));
if (!ent)
return ENOMEM;
ent->str_cookie = str_cookie;
ent->str_len = str_len;
ent->str_hash = str_hash;
ent->next = sblobs->buckets[bucket];
sblobs->buckets[bucket] = ent;
return 0;
}
/* Store a string and return a cookie for its retrieval. */
int
strblobs_store(
struct strblobs *sblobs,
xfblob_cookie *str_cookie,
const unsigned char *str,
unsigned int str_len,
xfs_dahash_t str_hash)
{
int error;
error = __strblobs_lookup(sblobs, str_cookie, str, str_len, str_hash);
if (error != ENOENT)
return error;
error = -xfblob_store(sblobs->strings, str_cookie, str, str_len);
if (error)
return error;
return strblobs_hash(sblobs, *str_cookie, str, str_len, str_hash);
}
/* Retrieve a previously stored string. */
int
strblobs_load(
struct strblobs *sblobs,
xfblob_cookie str_cookie,
unsigned char *str,
unsigned int str_len)
{
return -xfblob_load(sblobs->strings, str_cookie, str, str_len);
}