mirror of
https://github.com/versity/scoutfs.git
synced 2026-02-07 11:10:44 +00:00
scoutfs: print trailing key bytes
The key printing functions only output the key material that's described by the format. We have some callers that need to increment or decrement keys so they expand them to full size keys. This expansion and extra high precision low significance was hidden from the traces. This adds a helper that prints the key material with the format and then appends an encoding of the trailing bytes. The key printer was a huge mess of cases and ifs that made it hard to integrate a sane helper. We also take the opportunity to break it up into zone|type key printer functions. The isolation makes it much clearer to see what's going on. Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
@@ -241,6 +241,7 @@ struct scoutfs_segment_block {
|
||||
#define SCOUTFS_INODE_INDEX_ZONE 1
|
||||
#define SCOUTFS_NODE_ZONE 2
|
||||
#define SCOUTFS_FS_ZONE 3
|
||||
#define SCOUTFS_MAX_ZONE 4 /* power of 2 is efficient */
|
||||
|
||||
/* inode index zone */
|
||||
#define SCOUTFS_INODE_INDEX_SIZE_TYPE 3
|
||||
@@ -264,6 +265,7 @@ struct scoutfs_segment_block {
|
||||
#define SCOUTFS_FILE_EXTENT_TYPE 7
|
||||
#define SCOUTFS_ORPHAN_TYPE 8
|
||||
|
||||
#define SCOUTFS_MAX_TYPE 16 /* power of 2 is efficient */
|
||||
|
||||
/* value is struct scoutfs_inode */
|
||||
struct scoutfs_inode_key {
|
||||
|
||||
416
kmod/src/key.c
416
kmod/src/key.c
@@ -109,6 +109,249 @@ void scoutfs_key_dec(struct scoutfs_key_buf *key)
|
||||
#define snprintf_null(buf, size, fmt, args...) \
|
||||
(snprintf((buf), (size), fmt, ##args) + 1)
|
||||
|
||||
/*
|
||||
* Store a formatted string representing the key in the buffer. The key
|
||||
* must be at least min_len to store the data needed by the format at
|
||||
* all. fmt_len is the length of data that's used by the format. These
|
||||
* are different because we have badly designed keys with variable
|
||||
* length data that isn't described by the key. It's assumed from the
|
||||
* length of the key. Take dirents -- they need to at least have a
|
||||
* dirent struct, but the name length is the rest of the key.
|
||||
*
|
||||
* (XXX And this goes horribly wrong when we pad out dirent keys to max
|
||||
* len to increment at high precision. We'll never see these items used
|
||||
* by real fs code, but temporary keys and range endpoints can be full
|
||||
* precision and we can try and print them and get very confused. We
|
||||
* need to rev the format to include explicit lengths.)
|
||||
*
|
||||
* If the format doesn't cover the entire key then we append more
|
||||
* formatting to represent the trailing bytes: runs of zeros compresesd
|
||||
* to _ and then hex output of non-zero bytes.
|
||||
*/
|
||||
static int snprintf_key(char *buf, size_t size, struct scoutfs_key_buf *key,
|
||||
unsigned min_len, unsigned fmt_len,
|
||||
const char *fmt, ...)
|
||||
|
||||
{
|
||||
va_list args;
|
||||
char *data;
|
||||
char *end;
|
||||
int left;
|
||||
int part;
|
||||
int ret;
|
||||
int nr;
|
||||
|
||||
if (key->key_len < min_len)
|
||||
return snprintf_null(buf, size, "[trunc len %u < min %u]",
|
||||
key->key_len, min_len);
|
||||
|
||||
if (fmt_len == 0)
|
||||
fmt_len = min_len;
|
||||
|
||||
va_start(args, fmt);
|
||||
ret = vsnprintf(buf, size, fmt, args);
|
||||
va_end(args);
|
||||
/* next formatting overwrites null */
|
||||
if (buf) {
|
||||
buf += ret;
|
||||
size -= min_t(int, size, ret);
|
||||
}
|
||||
|
||||
data = key->data + fmt_len;
|
||||
left = key->key_len - fmt_len;
|
||||
|
||||
while (left && (!buf || size > 1)) {
|
||||
/* compress runs of zero bytes to _ */
|
||||
end = memchr_inv(data, 0, left);
|
||||
nr = end ? end - data : left;
|
||||
if (nr) {
|
||||
if (buf) {
|
||||
*(buf++) = '_';
|
||||
size--;
|
||||
}
|
||||
ret++;
|
||||
data += nr;
|
||||
left -= nr;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* hex print non-zero bytes. %ph is limited to 64 bytes
|
||||
* and is buggy in that it still tries to print to buf
|
||||
* past size. (so buf = null, size = 0 crashes instead
|
||||
* of printing the length of the formatted string.)
|
||||
*/
|
||||
end = memchr(data, 0, left);
|
||||
nr = end ? end - data : left;
|
||||
nr = min(nr, 64);
|
||||
|
||||
if (buf)
|
||||
part = snprintf(buf, size, "%*phN", nr, data);
|
||||
else
|
||||
part = nr * 2;
|
||||
if (buf) {
|
||||
buf += part;
|
||||
size -= min_t(int, size, part);
|
||||
}
|
||||
ret += part;
|
||||
|
||||
data += nr;
|
||||
left -= nr;
|
||||
}
|
||||
|
||||
/* always store and include null */
|
||||
if (buf)
|
||||
*buf = '\0';
|
||||
return ret + 1;
|
||||
}
|
||||
|
||||
typedef int (*key_printer_t)(char *buf, struct scoutfs_key_buf *key,
|
||||
size_t size);
|
||||
|
||||
static int pr_ino_idx(char *buf, struct scoutfs_key_buf *key, size_t size)
|
||||
{
|
||||
static char *type_strings[] = {
|
||||
[SCOUTFS_INODE_INDEX_SIZE_TYPE] = "siz",
|
||||
[SCOUTFS_INODE_INDEX_META_SEQ_TYPE] = "msq",
|
||||
[SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE] = "dsq",
|
||||
};
|
||||
struct scoutfs_inode_index_key *ikey = key->data;
|
||||
|
||||
return snprintf_key(buf, size, key,
|
||||
sizeof(struct scoutfs_inode_index_key), 0,
|
||||
"iin.%s.%llu.%u.%llu",
|
||||
type_strings[ikey->type], be64_to_cpu(ikey->major),
|
||||
be32_to_cpu(ikey->minor), be64_to_cpu(ikey->ino));
|
||||
}
|
||||
|
||||
static int pr_free_ext(char *buf, struct scoutfs_key_buf *key, size_t size)
|
||||
{
|
||||
struct scoutfs_free_extent_blkno_key *fkey = key->data;
|
||||
|
||||
static char *type_strings[] = {
|
||||
[SCOUTFS_FREE_EXTENT_BLKNO_TYPE] = "fno",
|
||||
[SCOUTFS_FREE_EXTENT_BLOCKS_TYPE] = "fks",
|
||||
};
|
||||
|
||||
return snprintf_key(buf, size, key,
|
||||
sizeof(struct scoutfs_free_extent_blkno_key), 0,
|
||||
"nod.%llu.%s.%llu.%llu",
|
||||
be64_to_cpu(fkey->node_id),
|
||||
type_strings[fkey->type],
|
||||
be64_to_cpu(fkey->last_blkno),
|
||||
be64_to_cpu(fkey->blocks));
|
||||
}
|
||||
|
||||
static int pr_orphan(char *buf, struct scoutfs_key_buf *key, size_t size)
|
||||
{
|
||||
struct scoutfs_orphan_key *okey = key->data;
|
||||
|
||||
return snprintf_key(buf, size, key,
|
||||
sizeof(struct scoutfs_orphan_key), 0,
|
||||
"nod.%llu.orp.%llu",
|
||||
be64_to_cpu(okey->node_id),
|
||||
be64_to_cpu(okey->ino));
|
||||
}
|
||||
|
||||
static int pr_inode(char *buf, struct scoutfs_key_buf *key, size_t size)
|
||||
{
|
||||
struct scoutfs_inode_key *ikey = key->data;
|
||||
|
||||
return snprintf_key(buf, size, key,
|
||||
sizeof(struct scoutfs_inode_key), 0,
|
||||
"fs.%llu.ino",
|
||||
be64_to_cpu(ikey->ino));
|
||||
}
|
||||
|
||||
static int pr_xattr(char *buf, struct scoutfs_key_buf *key, size_t size)
|
||||
{
|
||||
struct scoutfs_xattr_key *xkey = key->data;
|
||||
int len = (int)key->key_len -
|
||||
offsetof(struct scoutfs_xattr_key, name[1]);
|
||||
|
||||
return snprintf_key(buf, size, key,
|
||||
sizeof(struct scoutfs_xattr_key), key->key_len,
|
||||
"fs.%llu.xat.%.*s",
|
||||
be64_to_cpu(xkey->ino), len, xkey->name);
|
||||
}
|
||||
|
||||
static int pr_dirent(char *buf, struct scoutfs_key_buf *key, size_t size)
|
||||
{
|
||||
struct scoutfs_dirent_key *dkey = key->data;
|
||||
int len = (int)key->key_len - sizeof(struct scoutfs_dirent_key);
|
||||
|
||||
return snprintf_key(buf, size, key,
|
||||
sizeof(struct scoutfs_dirent_key), key->key_len,
|
||||
"fs.%llu.dnt.%.*s",
|
||||
be64_to_cpu(dkey->ino), len, dkey->name);
|
||||
}
|
||||
|
||||
static int pr_readdir(char *buf, struct scoutfs_key_buf *key, size_t size)
|
||||
{
|
||||
struct scoutfs_readdir_key *rkey = key->data;
|
||||
|
||||
return snprintf_key(buf, size, key,
|
||||
sizeof(struct scoutfs_readdir_key), 0,
|
||||
"fs.%llu.rdr.%llu",
|
||||
be64_to_cpu(rkey->ino), be64_to_cpu(rkey->pos));
|
||||
}
|
||||
|
||||
static int pr_link_backref(char *buf, struct scoutfs_key_buf *key, size_t size)
|
||||
{
|
||||
struct scoutfs_link_backref_key *lkey = key->data;
|
||||
int len = (int)key->key_len - sizeof(*lkey);
|
||||
|
||||
return snprintf_key(buf, size, key,
|
||||
sizeof(struct scoutfs_link_backref_key),
|
||||
key->key_len,
|
||||
"fs.%llu.lbr.%llu.%.*s",
|
||||
be64_to_cpu(lkey->ino), be64_to_cpu(lkey->dir_ino),
|
||||
len, lkey->name);
|
||||
}
|
||||
|
||||
static int pr_symlink(char *buf, struct scoutfs_key_buf *key, size_t size)
|
||||
{
|
||||
struct scoutfs_symlink_key *skey = key->data;
|
||||
|
||||
return snprintf_key(buf, size, key,
|
||||
sizeof(struct scoutfs_symlink_key), 0,
|
||||
"fs.%llu.sym",
|
||||
be64_to_cpu(skey->ino));
|
||||
}
|
||||
|
||||
static int pr_file_ext(char *buf, struct scoutfs_key_buf *key, size_t size)
|
||||
{
|
||||
struct scoutfs_file_extent_key *ekey = key->data;
|
||||
|
||||
return snprintf_key(buf, size, key,
|
||||
sizeof(struct scoutfs_file_extent_key), 0,
|
||||
"fs.%llu.ext.%llu.%llu.%llu.%x",
|
||||
be64_to_cpu(ekey->ino),
|
||||
be64_to_cpu(ekey->last_blk_off),
|
||||
be64_to_cpu(ekey->last_blkno),
|
||||
be64_to_cpu(ekey->blocks),
|
||||
ekey->flags);
|
||||
}
|
||||
|
||||
const static key_printer_t key_printers[SCOUTFS_MAX_ZONE][SCOUTFS_MAX_TYPE] = {
|
||||
[SCOUTFS_INODE_INDEX_ZONE][SCOUTFS_INODE_INDEX_SIZE_TYPE] =
|
||||
pr_ino_idx,
|
||||
[SCOUTFS_INODE_INDEX_ZONE][SCOUTFS_INODE_INDEX_META_SEQ_TYPE] =
|
||||
pr_ino_idx,
|
||||
[SCOUTFS_INODE_INDEX_ZONE][SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE] =
|
||||
pr_ino_idx,
|
||||
[SCOUTFS_NODE_ZONE][SCOUTFS_FREE_EXTENT_BLKNO_TYPE] = pr_free_ext,
|
||||
[SCOUTFS_NODE_ZONE][SCOUTFS_FREE_EXTENT_BLOCKS_TYPE] = pr_free_ext,
|
||||
[SCOUTFS_NODE_ZONE][SCOUTFS_ORPHAN_TYPE] = pr_orphan,
|
||||
[SCOUTFS_FS_ZONE][SCOUTFS_INODE_TYPE] = pr_inode,
|
||||
[SCOUTFS_FS_ZONE][SCOUTFS_XATTR_TYPE] = pr_xattr,
|
||||
[SCOUTFS_FS_ZONE][SCOUTFS_DIRENT_TYPE] = pr_dirent,
|
||||
[SCOUTFS_FS_ZONE][SCOUTFS_READDIR_TYPE] = pr_readdir,
|
||||
[SCOUTFS_FS_ZONE][SCOUTFS_LINK_BACKREF_TYPE] = pr_link_backref,
|
||||
[SCOUTFS_FS_ZONE][SCOUTFS_SYMLINK_TYPE] = pr_symlink,
|
||||
[SCOUTFS_FS_ZONE][SCOUTFS_FILE_EXTENT_TYPE] = pr_file_ext,
|
||||
};
|
||||
|
||||
/*
|
||||
* Write the null-terminated string that describes the key to the
|
||||
* buffer. The bytes copied (including the null) is returned. A null
|
||||
@@ -118,168 +361,51 @@ void scoutfs_key_dec(struct scoutfs_key_buf *key)
|
||||
*/
|
||||
int scoutfs_key_str_size(char *buf, struct scoutfs_key_buf *key, size_t size)
|
||||
{
|
||||
struct scoutfs_inode_key *ikey;
|
||||
u8 zone = 0;
|
||||
u8 type = 0;
|
||||
int len;
|
||||
u8 zone;
|
||||
u8 type;
|
||||
|
||||
if (key == NULL || key->data == NULL)
|
||||
return snprintf_null(buf, size, "[NULL]");
|
||||
|
||||
if (key->key_len == 0)
|
||||
return snprintf_null(buf, size, "[0 len]");
|
||||
/* always at least zone, some id, and type */
|
||||
if (key->key_len < (1 + 8 + 1))
|
||||
return snprintf_null(buf, size, "[trunc len %u]", key->key_len);
|
||||
|
||||
zone = *(u8 *)key->data;
|
||||
|
||||
/* handle smaller and unknown zones, fall through to fs types */
|
||||
switch(zone) {
|
||||
case SCOUTFS_INODE_INDEX_ZONE: {
|
||||
/*
|
||||
* each zone's keys always start with the same fields that let
|
||||
* us deref any key to get the type. We chose a few representative
|
||||
* keys from each zone to get the type.
|
||||
*/
|
||||
if (zone == SCOUTFS_INODE_INDEX_ZONE) {
|
||||
struct scoutfs_inode_index_key *ikey = key->data;
|
||||
static char *type_strings[] = {
|
||||
[SCOUTFS_INODE_INDEX_SIZE_TYPE] = "siz",
|
||||
[SCOUTFS_INODE_INDEX_META_SEQ_TYPE] = "msq",
|
||||
[SCOUTFS_INODE_INDEX_DATA_SEQ_TYPE] = "dsq",
|
||||
};
|
||||
|
||||
if (key->key_len < sizeof(struct scoutfs_inode_index_key))
|
||||
break;
|
||||
|
||||
if (type_strings[ikey->type])
|
||||
return snprintf_null(buf, size, "iin.%s.%llu.%u.%llu",
|
||||
type_strings[ikey->type],
|
||||
be64_to_cpu(ikey->major),
|
||||
be32_to_cpu(ikey->minor),
|
||||
be64_to_cpu(ikey->ino));
|
||||
else
|
||||
return snprintf_null(buf, size, "[iin type %u?]",
|
||||
ikey->type);
|
||||
}
|
||||
|
||||
/* node zone keys start with zone, node, type */
|
||||
case SCOUTFS_NODE_ZONE: {
|
||||
type = ikey->type;
|
||||
} else if (zone == SCOUTFS_NODE_ZONE) {
|
||||
struct scoutfs_free_extent_blkno_key *fkey = key->data;
|
||||
|
||||
static char *type_strings[] = {
|
||||
[SCOUTFS_FREE_EXTENT_BLKNO_TYPE] = "fno",
|
||||
[SCOUTFS_FREE_EXTENT_BLOCKS_TYPE] = "fks",
|
||||
};
|
||||
|
||||
switch(fkey->type) {
|
||||
case SCOUTFS_ORPHAN_TYPE: {
|
||||
struct scoutfs_orphan_key *okey = key->data;
|
||||
|
||||
if (key->key_len < sizeof(struct scoutfs_orphan_key))
|
||||
break;
|
||||
return snprintf_null(buf, size, "nod.%llu.orp.%llu",
|
||||
be64_to_cpu(okey->node_id),
|
||||
be64_to_cpu(okey->ino));
|
||||
}
|
||||
|
||||
case SCOUTFS_FREE_EXTENT_BLKNO_TYPE:
|
||||
case SCOUTFS_FREE_EXTENT_BLOCKS_TYPE:
|
||||
return snprintf_null(buf, size, "nod.%llu.%s.%llu.%llu",
|
||||
be64_to_cpu(fkey->node_id),
|
||||
type_strings[fkey->type],
|
||||
be64_to_cpu(fkey->last_blkno),
|
||||
be64_to_cpu(fkey->blocks));
|
||||
default:
|
||||
return snprintf_null(buf, size, "[nod type %u?]",
|
||||
fkey->type);
|
||||
}
|
||||
}
|
||||
|
||||
case SCOUTFS_FS_ZONE:
|
||||
break;
|
||||
|
||||
default:
|
||||
return snprintf_null(buf, size, "[zone %u?]", zone);
|
||||
}
|
||||
|
||||
/* everything in the fs tree starts with zone, ino, type */
|
||||
ikey = key->data;
|
||||
switch(ikey->type) {
|
||||
case SCOUTFS_INODE_TYPE: {
|
||||
type = fkey->type;
|
||||
} else if (zone == SCOUTFS_FS_ZONE) {
|
||||
struct scoutfs_inode_key *ikey = key->data;
|
||||
|
||||
if (key->key_len < sizeof(struct scoutfs_inode_key))
|
||||
break;
|
||||
|
||||
return snprintf_null(buf, size, "fs.%llu.ino",
|
||||
be64_to_cpu(ikey->ino));
|
||||
type = ikey->type;
|
||||
} else {
|
||||
type = 255;
|
||||
}
|
||||
|
||||
case SCOUTFS_XATTR_TYPE: {
|
||||
struct scoutfs_xattr_key *xkey = key->data;
|
||||
|
||||
len = (int)key->key_len - offsetof(struct scoutfs_xattr_key,
|
||||
name[1]);
|
||||
if (len <= 0)
|
||||
break;
|
||||
|
||||
return snprintf_null(buf, size, "fs.%llu.xat.%.*s",
|
||||
be64_to_cpu(xkey->ino), len, xkey->name);
|
||||
if (zone > SCOUTFS_MAX_ZONE || type > SCOUTFS_MAX_TYPE ||
|
||||
key_printers[zone][type] == NULL) {
|
||||
return snprintf_null(buf, size, "[unk zone %u type %u]",
|
||||
zone, type);
|
||||
}
|
||||
|
||||
case SCOUTFS_DIRENT_TYPE: {
|
||||
struct scoutfs_dirent_key *dkey = key->data;
|
||||
|
||||
len = (int)key->key_len - sizeof(struct scoutfs_dirent_key);
|
||||
if (len <= 0)
|
||||
break;
|
||||
|
||||
return snprintf_null(buf, size, "fs.%llu.dnt.%.*s",
|
||||
be64_to_cpu(dkey->ino), len, dkey->name);
|
||||
}
|
||||
|
||||
case SCOUTFS_READDIR_TYPE: {
|
||||
struct scoutfs_readdir_key *rkey = key->data;
|
||||
|
||||
return snprintf_null(buf, size, "fs.%llu.rdr.%llu",
|
||||
be64_to_cpu(rkey->ino),
|
||||
be64_to_cpu(rkey->pos));
|
||||
}
|
||||
|
||||
case SCOUTFS_LINK_BACKREF_TYPE: {
|
||||
struct scoutfs_link_backref_key *lkey = key->data;
|
||||
|
||||
len = (int)key->key_len - sizeof(*lkey);
|
||||
if (len <= 0)
|
||||
break;
|
||||
|
||||
return snprintf_null(buf, size, "fs.%llu.lbr.%llu.%.*s",
|
||||
be64_to_cpu(lkey->ino),
|
||||
be64_to_cpu(lkey->dir_ino), len,
|
||||
lkey->name);
|
||||
}
|
||||
|
||||
case SCOUTFS_SYMLINK_TYPE: {
|
||||
struct scoutfs_symlink_key *skey = key->data;
|
||||
|
||||
return snprintf_null(buf, size, "fs.%llu.sym",
|
||||
be64_to_cpu(skey->ino));
|
||||
}
|
||||
|
||||
case SCOUTFS_FILE_EXTENT_TYPE: {
|
||||
struct scoutfs_file_extent_key *ekey = key->data;
|
||||
|
||||
return snprintf_null(buf, size, "fs.%llu.ext.%llu.%llu.%llu.%x",
|
||||
be64_to_cpu(ekey->ino),
|
||||
be64_to_cpu(ekey->last_blk_off),
|
||||
be64_to_cpu(ekey->last_blkno),
|
||||
be64_to_cpu(ekey->blocks),
|
||||
ekey->flags);
|
||||
}
|
||||
|
||||
default:
|
||||
return snprintf_null(buf, size, "[fs type %u?]", type);
|
||||
}
|
||||
|
||||
return snprintf_null(buf, size, "[fs type %u trunc len %u]",
|
||||
type, key->key_len);
|
||||
return key_printers[zone][type](buf, key, size);
|
||||
}
|
||||
|
||||
/*
|
||||
* A null buf can be set to find the length of the formatted string.
|
||||
* Callers never have a pre-existing buffer whose size they need to be
|
||||
* careful for. For a given static string they're first calling with a
|
||||
* null buf to find out the formatted length without storing anything.
|
||||
* Then they're called again with a buffer of that allocation size. As
|
||||
* long as the formatting is consistent this pattern won't overflow.
|
||||
*/
|
||||
int scoutfs_key_str(char *buf, struct scoutfs_key_buf *key)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user