diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2017-01-08 00:13:18 -0900 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@gmail.com> | 2017-01-20 09:07:08 -0900 |
commit | b33fc8298f7e13226b9895abc57c9bfce5e3fa2d (patch) | |
tree | a3d2a5a909b6372f7777c1c5c18cef5f81d123a9 /libbcache/inode.c | |
parent | 7f4191a202ea4558ca2d5eb8a47daea33c9999c7 (diff) |
bcache in userspace; userspace fsck
Diffstat (limited to 'libbcache/inode.c')
-rw-r--r-- | libbcache/inode.c | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/libbcache/inode.c b/libbcache/inode.c new file mode 100644 index 00000000..d36de43c --- /dev/null +++ b/libbcache/inode.c @@ -0,0 +1,283 @@ + +#include "bcache.h" +#include "bkey_methods.h" +#include "btree_update.h" +#include "extents.h" +#include "inode.h" +#include "io.h" +#include "keylist.h" + +ssize_t bch_inode_status(char *buf, size_t len, const struct bkey *k) +{ + if (k->p.offset) + return scnprintf(buf, len, "offset nonzero: %llu", k->p.offset); + + if (k->size) + return scnprintf(buf, len, "size nonzero: %u", k->size); + + switch (k->type) { + case KEY_TYPE_DELETED: + return scnprintf(buf, len, "deleted"); + case KEY_TYPE_DISCARD: + return scnprintf(buf, len, "discarded"); + case KEY_TYPE_ERROR: + return scnprintf(buf, len, "error"); + case KEY_TYPE_COOKIE: + return scnprintf(buf, len, "cookie"); + + case BCH_INODE_FS: + if (bkey_val_bytes(k) != sizeof(struct bch_inode)) + return scnprintf(buf, len, "bad size: %zu", + bkey_val_bytes(k)); + + if (k->p.inode < BLOCKDEV_INODE_MAX) + return scnprintf(buf, len, + "fs inode in blockdev range: %llu", + k->p.inode); + return 0; + + case BCH_INODE_BLOCKDEV: + if (bkey_val_bytes(k) != sizeof(struct bch_inode_blockdev)) + return scnprintf(buf, len, "bad size: %zu", + bkey_val_bytes(k)); + + if (k->p.inode >= BLOCKDEV_INODE_MAX) + return scnprintf(buf, len, + "blockdev inode in fs range: %llu", + k->p.inode); + return 0; + + default: + return scnprintf(buf, len, "unknown inode type: %u", k->type); + } +} + +static const char *bch_inode_invalid(const struct cache_set *c, + struct bkey_s_c k) +{ + if (k.k->p.offset) + return "nonzero offset"; + + switch (k.k->type) { + case BCH_INODE_FS: { + struct bkey_s_c_inode inode = bkey_s_c_to_inode(k); + + if (bkey_val_bytes(k.k) != sizeof(struct bch_inode)) + return "incorrect value size"; + + if (k.k->p.inode < BLOCKDEV_INODE_MAX) + return "fs inode in blockdev range"; + + if (INODE_STR_HASH_TYPE(inode.v) >= BCH_STR_HASH_NR) + return "invalid str hash type"; + + return NULL; + } + case BCH_INODE_BLOCKDEV: + if (bkey_val_bytes(k.k) != sizeof(struct bch_inode_blockdev)) + return "incorrect value size"; + + if (k.k->p.inode >= BLOCKDEV_INODE_MAX) + return "blockdev inode in fs range"; + + return NULL; + default: + return "invalid type"; + } +} + +static void bch_inode_to_text(struct cache_set *c, char *buf, + size_t size, struct bkey_s_c k) +{ + struct bkey_s_c_inode inode; + + switch (k.k->type) { + case BCH_INODE_FS: + inode = bkey_s_c_to_inode(k); + + scnprintf(buf, size, "i_size %llu", inode.v->i_size); + break; + } +} + +const struct bkey_ops bch_bkey_inode_ops = { + .key_invalid = bch_inode_invalid, + .val_to_text = bch_inode_to_text, +}; + +int bch_inode_create(struct cache_set *c, struct bkey_i *inode, + u64 min, u64 max, u64 *hint) +{ + struct btree_iter iter; + bool searched_from_start = false; + int ret; + + if (!max) + max = ULLONG_MAX; + + if (c->opts.inodes_32bit) + max = min_t(u64, max, U32_MAX); + + if (*hint >= max || *hint < min) + *hint = min; + + if (*hint == min) + searched_from_start = true; +again: + bch_btree_iter_init_intent(&iter, c, BTREE_ID_INODES, POS(*hint, 0)); + + while (1) { + struct bkey_s_c k = bch_btree_iter_peek_with_holes(&iter); + + ret = btree_iter_err(k); + if (ret) { + bch_btree_iter_unlock(&iter); + return ret; + } + + if (k.k->type < BCH_INODE_FS) { + inode->k.p = k.k->p; + + pr_debug("inserting inode %llu (size %u)", + inode->k.p.inode, inode->k.u64s); + + ret = bch_btree_insert_at(c, NULL, NULL, NULL, + BTREE_INSERT_ATOMIC, + BTREE_INSERT_ENTRY(&iter, inode)); + + if (ret == -EINTR) + continue; + + bch_btree_iter_unlock(&iter); + if (!ret) + *hint = k.k->p.inode + 1; + + return ret; + } else { + if (iter.pos.inode == max) + break; + /* slot used */ + bch_btree_iter_advance_pos(&iter); + } + } + bch_btree_iter_unlock(&iter); + + if (!searched_from_start) { + /* Retry from start */ + *hint = min; + searched_from_start = true; + goto again; + } + + return -ENOSPC; +} + +int bch_inode_truncate(struct cache_set *c, u64 inode_nr, u64 new_size, + struct extent_insert_hook *hook, u64 *journal_seq) +{ + return bch_discard(c, POS(inode_nr, new_size), POS(inode_nr + 1, 0), + 0, NULL, hook, journal_seq); +} + +int bch_inode_rm(struct cache_set *c, u64 inode_nr) +{ + struct bkey_i delete; + int ret; + + ret = bch_inode_truncate(c, inode_nr, 0, NULL, NULL); + if (ret < 0) + return ret; + + ret = bch_btree_delete_range(c, BTREE_ID_XATTRS, + POS(inode_nr, 0), + POS(inode_nr + 1, 0), + 0, NULL, NULL, NULL); + if (ret < 0) + return ret; + + /* + * If this was a directory, there shouldn't be any real dirents left - + * but there could be whiteouts (from hash collisions) that we should + * delete: + * + * XXX: the dirent could ideally would delete whitouts when they're no + * longer needed + */ + ret = bch_btree_delete_range(c, BTREE_ID_DIRENTS, + POS(inode_nr, 0), + POS(inode_nr + 1, 0), + 0, NULL, NULL, NULL); + if (ret < 0) + return ret; + + bkey_init(&delete.k); + delete.k.p.inode = inode_nr; + + return bch_btree_insert(c, BTREE_ID_INODES, &delete, NULL, + NULL, NULL, BTREE_INSERT_NOFAIL); +} + +int bch_inode_update(struct cache_set *c, struct bkey_i *inode, + u64 *journal_seq) +{ + return bch_btree_update(c, BTREE_ID_INODES, inode, journal_seq); +} + +int bch_inode_find_by_inum(struct cache_set *c, u64 inode_nr, + struct bkey_i_inode *inode) +{ + struct btree_iter iter; + struct bkey_s_c k; + int ret = -ENOENT; + + for_each_btree_key_with_holes(&iter, c, BTREE_ID_INODES, + POS(inode_nr, 0), k) { + switch (k.k->type) { + case BCH_INODE_FS: + ret = 0; + bkey_reassemble(&inode->k_i, k); + break; + default: + /* hole, not found */ + break; + } + + break; + + } + bch_btree_iter_unlock(&iter); + + return ret; +} + +int bch_cached_dev_inode_find_by_uuid(struct cache_set *c, uuid_le *uuid, + struct bkey_i_inode_blockdev *ret) +{ + struct btree_iter iter; + struct bkey_s_c k; + + for_each_btree_key(&iter, c, BTREE_ID_INODES, POS(0, 0), k) { + if (k.k->p.inode >= BLOCKDEV_INODE_MAX) + break; + + if (k.k->type == BCH_INODE_BLOCKDEV) { + struct bkey_s_c_inode_blockdev inode = + bkey_s_c_to_inode_blockdev(k); + + pr_debug("found inode %llu: %pU (u64s %u)", + inode.k->p.inode, inode.v->i_uuid.b, + inode.k->u64s); + + if (CACHED_DEV(inode.v) && + !memcmp(uuid, &inode.v->i_uuid, 16)) { + bkey_reassemble(&ret->k_i, k); + bch_btree_iter_unlock(&iter); + return 0; + } + } + + bch_btree_iter_cond_resched(&iter); + } + bch_btree_iter_unlock(&iter); + return -ENOENT; +} |