diff options
Diffstat (limited to 'libbcachefs/dirent.c')
-rw-r--r-- | libbcachefs/dirent.c | 63 |
1 files changed, 39 insertions, 24 deletions
diff --git a/libbcachefs/dirent.c b/libbcachefs/dirent.c index 503f0dc4..e2978bab 100644 --- a/libbcachefs/dirent.c +++ b/libbcachefs/dirent.c @@ -20,6 +20,11 @@ unsigned bch2_dirent_name_bytes(struct bkey_s_c_dirent d) return len; } +static unsigned dirent_val_u64s(unsigned len) +{ + return DIV_ROUND_UP(sizeof(struct bch_dirent) + len, sizeof(u64)); +} + static u64 bch2_dirent_hash(const struct bch_hash_info *info, const struct qstr *name) { @@ -64,7 +69,7 @@ static bool dirent_cmp_bkey(struct bkey_s_c _l, struct bkey_s_c _r) return l_len - r_len ?: memcmp(l.v->d_name, r.v->d_name, l_len); } -static const struct bch_hash_desc dirent_hash_desc = { +const struct bch_hash_desc bch2_dirent_hash_desc = { .btree_id = BTREE_ID_DIRENTS, .key_type = BCH_DIRENT, .whiteout_type = BCH_DIRENT_WHITEOUT, @@ -77,12 +82,30 @@ static const struct bch_hash_desc dirent_hash_desc = { static const char *bch2_dirent_invalid(const struct bch_fs *c, struct bkey_s_c k) { + struct bkey_s_c_dirent d; + unsigned len; + switch (k.k->type) { case BCH_DIRENT: - return bkey_val_bytes(k.k) < sizeof(struct bch_dirent) - ? "value too small" - : NULL; + if (bkey_val_bytes(k.k) < sizeof(struct bch_dirent)) + return "value too small"; + + d = bkey_s_c_to_dirent(k); + len = bch2_dirent_name_bytes(d); + + if (!len) + return "empty name"; + + if (bkey_val_u64s(k.k) > dirent_val_u64s(len)) + return "value too big"; + + if (len > NAME_MAX) + return "dirent name too big"; + if (memchr(d.v->d_name, '/', len)) + return "dirent name has invalid characters"; + + return NULL; case BCH_DIRENT_WHITEOUT: return bkey_val_bytes(k.k) != 0 ? "value size should be zero" @@ -97,21 +120,15 @@ static void bch2_dirent_to_text(struct bch_fs *c, char *buf, size_t size, struct bkey_s_c k) { struct bkey_s_c_dirent d; + size_t n = 0; switch (k.k->type) { case BCH_DIRENT: d = bkey_s_c_to_dirent(k); - if (size) { - unsigned n = min_t(unsigned, size, - bch2_dirent_name_bytes(d)); - memcpy(buf, d.v->d_name, n); - buf[size - 1] = '\0'; - buf += n; - size -= n; - } - - scnprintf(buf, size, " -> %llu", d.v->d_inum); + n += bch_scnmemcpy(buf + n, size - n, d.v->d_name, + bch2_dirent_name_bytes(d)); + n += scnprintf(buf + n, size - n, " -> %llu", d.v->d_inum); break; case BCH_DIRENT_WHITEOUT: scnprintf(buf, size, "whiteout"); @@ -128,9 +145,7 @@ static struct bkey_i_dirent *dirent_create_key(u8 type, const struct qstr *name, u64 dst) { struct bkey_i_dirent *dirent; - unsigned u64s = BKEY_U64s + - DIV_ROUND_UP(sizeof(struct bch_dirent) + name->len, - sizeof(u64)); + unsigned u64s = BKEY_U64s + dirent_val_u64s(name->len); dirent = kmalloc(u64s * sizeof(u64), GFP_NOFS); if (!dirent) @@ -163,7 +178,7 @@ int bch2_dirent_create(struct bch_fs *c, u64 dir_inum, if (!dirent) return -ENOMEM; - ret = bch2_hash_set(dirent_hash_desc, hash_info, c, dir_inum, + ret = bch2_hash_set(bch2_dirent_hash_desc, hash_info, c, dir_inum, journal_seq, &dirent->k_i, flags); kfree(dirent); @@ -223,13 +238,13 @@ retry: * from the original hashed position (like we do when creating dirents, * in bch_hash_set) - we never move existing dirents to different slot: */ - old_src = bch2_hash_lookup_at(dirent_hash_desc, + old_src = bch2_hash_lookup_at(bch2_dirent_hash_desc, &src_ei->str_hash, &src_iter, src_name); if ((ret = btree_iter_err(old_src))) goto err; - ret = bch2_hash_needs_whiteout(dirent_hash_desc, + ret = bch2_hash_needs_whiteout(bch2_dirent_hash_desc, &src_ei->str_hash, &whiteout_iter, &src_iter); if (ret < 0) @@ -242,8 +257,8 @@ retry: * to do that check for us for correctness: */ old_dst = mode == BCH_RENAME - ? bch2_hash_hole_at(dirent_hash_desc, &dst_iter) - : bch2_hash_lookup_at(dirent_hash_desc, + ? bch2_hash_hole_at(bch2_dirent_hash_desc, &dst_iter) + : bch2_hash_lookup_at(bch2_dirent_hash_desc, &dst_ei->str_hash, &dst_iter, dst_name); if ((ret = btree_iter_err(old_dst))) @@ -330,7 +345,7 @@ int bch2_dirent_delete(struct bch_fs *c, u64 dir_inum, const struct qstr *name, u64 *journal_seq) { - return bch2_hash_delete(dirent_hash_desc, hash_info, + return bch2_hash_delete(bch2_dirent_hash_desc, hash_info, c, dir_inum, journal_seq, name); } @@ -342,7 +357,7 @@ u64 bch2_dirent_lookup(struct bch_fs *c, u64 dir_inum, struct bkey_s_c k; u64 inum; - k = bch2_hash_lookup(dirent_hash_desc, hash_info, c, + k = bch2_hash_lookup(bch2_dirent_hash_desc, hash_info, c, dir_inum, &iter, name); if (IS_ERR(k.k)) { bch2_btree_iter_unlock(&iter); |