diff options
Diffstat (limited to 'fs/ubifs/tnc.c')
| -rw-r--r-- | fs/ubifs/tnc.c | 663 |
1 files changed, 448 insertions, 215 deletions
diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c index 349f31a30f40..33946b518148 100644 --- a/fs/ubifs/tnc.c +++ b/fs/ubifs/tnc.c @@ -1,21 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * This file is part of UBIFS. * * Copyright (C) 2006-2008 Nokia Corporation. * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * * Authors: Adrian Hunter * Artem Bityutskiy (Битюцкий Артём) */ @@ -34,6 +22,11 @@ #include <linux/slab.h> #include "ubifs.h" +static int try_read_node(const struct ubifs_info *c, void *buf, int type, + struct ubifs_zbranch *zbr); +static int fallible_read_node(struct ubifs_info *c, const union ubifs_key *key, + struct ubifs_zbranch *zbr, void *node); + /* * Returned codes of 'matches_name()' and 'fallible_matches_name()' functions. * @NAME_LESS: name corresponding to the first argument is less than second @@ -51,6 +44,34 @@ enum { NOT_ON_MEDIA = 3, }; +static void do_insert_old_idx(struct ubifs_info *c, + struct ubifs_old_idx *old_idx) +{ + struct ubifs_old_idx *o; + struct rb_node **p, *parent = NULL; + + p = &c->old_idx.rb_node; + while (*p) { + parent = *p; + o = rb_entry(parent, struct ubifs_old_idx, rb); + if (old_idx->lnum < o->lnum) + p = &(*p)->rb_left; + else if (old_idx->lnum > o->lnum) + p = &(*p)->rb_right; + else if (old_idx->offs < o->offs) + p = &(*p)->rb_left; + else if (old_idx->offs > o->offs) + p = &(*p)->rb_right; + else { + ubifs_err(c, "old idx added twice!"); + kfree(old_idx); + return; + } + } + rb_link_node(&old_idx->rb, parent, p); + rb_insert_color(&old_idx->rb, &c->old_idx); +} + /** * insert_old_idx - record an index node obsoleted since the last commit start. * @c: UBIFS file-system description object @@ -76,35 +97,15 @@ enum { */ static int insert_old_idx(struct ubifs_info *c, int lnum, int offs) { - struct ubifs_old_idx *old_idx, *o; - struct rb_node **p, *parent = NULL; + struct ubifs_old_idx *old_idx; old_idx = kmalloc(sizeof(struct ubifs_old_idx), GFP_NOFS); if (unlikely(!old_idx)) return -ENOMEM; old_idx->lnum = lnum; old_idx->offs = offs; + do_insert_old_idx(c, old_idx); - p = &c->old_idx.rb_node; - while (*p) { - parent = *p; - o = rb_entry(parent, struct ubifs_old_idx, rb); - if (lnum < o->lnum) - p = &(*p)->rb_left; - else if (lnum > o->lnum) - p = &(*p)->rb_right; - else if (offs < o->offs) - p = &(*p)->rb_left; - else if (offs > o->offs) - p = &(*p)->rb_right; - else { - ubifs_err("old idx added twice!"); - kfree(old_idx); - return 0; - } - } - rb_link_node(&old_idx->rb, parent, p); - rb_insert_color(&old_idx->rb, &c->old_idx); return 0; } @@ -178,27 +179,11 @@ static int ins_clr_old_idx_znode(struct ubifs_info *c, */ void destroy_old_idx(struct ubifs_info *c) { - struct rb_node *this = c->old_idx.rb_node; - struct ubifs_old_idx *old_idx; + struct ubifs_old_idx *old_idx, *n; - while (this) { - if (this->rb_left) { - this = this->rb_left; - continue; - } else if (this->rb_right) { - this = this->rb_right; - continue; - } - old_idx = rb_entry(this, struct ubifs_old_idx, rb); - this = rb_parent(this); - if (this) { - if (this->rb_left == &old_idx->rb) - this->rb_left = NULL; - else - this->rb_right = NULL; - } + rbtree_postorder_for_each_entry_safe(old_idx, n, &c->old_idx, rb) kfree(old_idx); - } + c->old_idx = RB_ROOT; } @@ -214,32 +199,14 @@ static struct ubifs_znode *copy_znode(struct ubifs_info *c, { struct ubifs_znode *zn; - zn = kmalloc(c->max_znode_sz, GFP_NOFS); + zn = kmemdup(znode, c->max_znode_sz, GFP_NOFS); if (unlikely(!zn)) return ERR_PTR(-ENOMEM); - memcpy(zn, znode, c->max_znode_sz); zn->cnext = NULL; __set_bit(DIRTY_ZNODE, &zn->flags); __clear_bit(COW_ZNODE, &zn->flags); - ubifs_assert(!ubifs_zn_obsolete(znode)); - __set_bit(OBSOLETE_ZNODE, &znode->flags); - - if (znode->level != 0) { - int i; - const int n = zn->child_cnt; - - /* The children now have new parent */ - for (i = 0; i < n; i++) { - struct ubifs_zbranch *zbr = &zn->zbranch[i]; - - if (zbr->znode) - zbr->znode->parent = zn; - } - } - - atomic_long_inc(&c->dirty_zn_cnt); return zn; } @@ -258,6 +225,42 @@ static int add_idx_dirt(struct ubifs_info *c, int lnum, int dirt) } /** + * replace_znode - replace old znode with new znode. + * @c: UBIFS file-system description object + * @new_zn: new znode + * @old_zn: old znode + * @zbr: the branch of parent znode + * + * Replace old znode with new znode in TNC. + */ +static void replace_znode(struct ubifs_info *c, struct ubifs_znode *new_zn, + struct ubifs_znode *old_zn, struct ubifs_zbranch *zbr) +{ + ubifs_assert(c, !ubifs_zn_obsolete(old_zn)); + __set_bit(OBSOLETE_ZNODE, &old_zn->flags); + + if (old_zn->level != 0) { + int i; + const int n = new_zn->child_cnt; + + /* The children now have new parent */ + for (i = 0; i < n; i++) { + struct ubifs_zbranch *child = &new_zn->zbranch[i]; + + if (child->znode) + child->znode->parent = new_zn; + } + } + + zbr->znode = new_zn; + zbr->lnum = 0; + zbr->offs = 0; + zbr->len = 0; + + atomic_long_inc(&c->dirty_zn_cnt); +} + +/** * dirty_cow_znode - ensure a znode is not being committed. * @c: UBIFS file-system description object * @zbr: branch of znode to check @@ -289,21 +292,32 @@ static struct ubifs_znode *dirty_cow_znode(struct ubifs_info *c, return zn; if (zbr->len) { - err = insert_old_idx(c, zbr->lnum, zbr->offs); - if (unlikely(err)) - return ERR_PTR(err); + struct ubifs_old_idx *old_idx; + + old_idx = kmalloc(sizeof(struct ubifs_old_idx), GFP_NOFS); + if (unlikely(!old_idx)) { + err = -ENOMEM; + goto out; + } + old_idx->lnum = zbr->lnum; + old_idx->offs = zbr->offs; + err = add_idx_dirt(c, zbr->lnum, zbr->len); - } else - err = 0; + if (err) { + kfree(old_idx); + goto out; + } - zbr->znode = zn; - zbr->lnum = 0; - zbr->offs = 0; - zbr->len = 0; + do_insert_old_idx(c, old_idx); + } + + replace_znode(c, zn, znode, zbr); - if (unlikely(err)) - return ERR_PTR(err); return zn; + +out: + kfree(zn); + return ERR_PTR(err); } /** @@ -333,14 +347,14 @@ static int lnc_add(struct ubifs_info *c, struct ubifs_zbranch *zbr, void *lnc_node; const struct ubifs_dent_node *dent = node; - ubifs_assert(!zbr->leaf); - ubifs_assert(zbr->len != 0); - ubifs_assert(is_hash_key(c, &zbr->key)); + ubifs_assert(c, !zbr->leaf); + ubifs_assert(c, zbr->len != 0); + ubifs_assert(c, is_hash_key(c, &zbr->key)); err = ubifs_validate_entry(c, dent); if (err) { dump_stack(); - ubifs_dump_node(c, dent); + ubifs_dump_node(c, dent, zbr->len); return err; } @@ -367,13 +381,13 @@ static int lnc_add_directly(struct ubifs_info *c, struct ubifs_zbranch *zbr, { int err; - ubifs_assert(!zbr->leaf); - ubifs_assert(zbr->len != 0); + ubifs_assert(c, !zbr->leaf); + ubifs_assert(c, zbr->len != 0); err = ubifs_validate_entry(c, node); if (err) { dump_stack(); - ubifs_dump_node(c, node); + ubifs_dump_node(c, node, zbr->len); return err; } @@ -384,7 +398,6 @@ static int lnc_add_directly(struct ubifs_info *c, struct ubifs_zbranch *zbr, /** * lnc_free - remove a leaf node from the leaf node cache. * @zbr: zbranch of leaf node - * @node: leaf node */ static void lnc_free(struct ubifs_zbranch *zbr) { @@ -395,31 +408,43 @@ static void lnc_free(struct ubifs_zbranch *zbr) } /** - * tnc_read_node_nm - read a "hashed" leaf node. + * tnc_read_hashed_node - read a "hashed" leaf node. * @c: UBIFS file-system description object * @zbr: key and position of the node * @node: node is returned here * * This function reads a "hashed" node defined by @zbr from the leaf node cache * (in it is there) or from the hash media, in which case the node is also - * added to LNC. Returns zero in case of success or a negative negative error + * added to LNC. Returns zero in case of success or a negative error * code in case of failure. */ -static int tnc_read_node_nm(struct ubifs_info *c, struct ubifs_zbranch *zbr, - void *node) +static int tnc_read_hashed_node(struct ubifs_info *c, struct ubifs_zbranch *zbr, + void *node) { int err; - ubifs_assert(is_hash_key(c, &zbr->key)); + ubifs_assert(c, is_hash_key(c, &zbr->key)); if (zbr->leaf) { /* Read from the leaf node cache */ - ubifs_assert(zbr->len != 0); + ubifs_assert(c, zbr->len != 0); memcpy(node, zbr->leaf, zbr->len); return 0; } - err = ubifs_tnc_read_node(c, zbr, node); + if (c->replaying) { + err = fallible_read_node(c, &zbr->key, zbr, node); + /* + * When the node was not found, return -ENOENT, 0 otherwise. + * Negative return codes stay as-is. + */ + if (err == 0) + err = -ENOENT; + else if (err == 1) + err = 0; + } else { + err = ubifs_tnc_read_node(c, zbr, node); + } if (err) return err; @@ -433,9 +458,7 @@ static int tnc_read_node_nm(struct ubifs_info *c, struct ubifs_zbranch *zbr, * @c: UBIFS file-system description object * @buf: buffer to read to * @type: node type - * @len: node length (not aligned) - * @lnum: LEB number of node to read - * @offs: offset of node to read + * @zbr: the zbranch describing the node to read * * This function tries to read a node of known type and length, checks it and * stores it in @buf. This function returns %1 if a node is present and %0 if @@ -453,8 +476,11 @@ static int tnc_read_node_nm(struct ubifs_info *c, struct ubifs_zbranch *zbr, * journal nodes may potentially be corrupted, so checking is required. */ static int try_read_node(const struct ubifs_info *c, void *buf, int type, - int len, int lnum, int offs) + struct ubifs_zbranch *zbr) { + int len = zbr->len; + int lnum = zbr->lnum; + int offs = zbr->offs; int err, node_len; struct ubifs_ch *ch = buf; uint32_t crc, node_crc; @@ -463,7 +489,7 @@ static int try_read_node(const struct ubifs_info *c, void *buf, int type, err = ubifs_leb_read(c, lnum, buf, offs, len, 1); if (err) { - ubifs_err("cannot read node type %d from LEB %d:%d, error %d", + ubifs_err(c, "cannot read node type %d from LEB %d:%d, error %d", type, lnum, offs, err); return err; } @@ -478,14 +504,19 @@ static int try_read_node(const struct ubifs_info *c, void *buf, int type, if (node_len != len) return 0; - if (type == UBIFS_DATA_NODE && c->no_chk_data_crc && !c->mounting && - !c->remounting_rw) - return 1; + if (type != UBIFS_DATA_NODE || !c->no_chk_data_crc || c->mounting || + c->remounting_rw) { + crc = crc32(UBIFS_CRC32_INIT, buf + 8, node_len - 8); + node_crc = le32_to_cpu(ch->crc); + if (crc != node_crc) + return 0; + } - crc = crc32(UBIFS_CRC32_INIT, buf + 8, node_len - 8); - node_crc = le32_to_cpu(ch->crc); - if (crc != node_crc) + err = ubifs_node_check_hash(c, buf, zbr->hash); + if (err) { + ubifs_bad_hash(c, buf, zbr->hash, lnum, offs); return 0; + } return 1; } @@ -507,8 +538,7 @@ static int fallible_read_node(struct ubifs_info *c, const union ubifs_key *key, dbg_tnck(key, "LEB %d:%d, key ", zbr->lnum, zbr->offs); - ret = try_read_node(c, node, key_type(c, key), zbr->len, zbr->lnum, - zbr->offs); + ret = try_read_node(c, node, key_type(c, key), zbr); if (ret == 1) { union ubifs_key node_key; struct ubifs_dent_node *dent = node; @@ -536,7 +566,7 @@ static int fallible_read_node(struct ubifs_info *c, const union ubifs_key *key, * of failure, a negative error code is returned. */ static int matches_name(struct ubifs_info *c, struct ubifs_zbranch *zbr, - const struct qstr *nm) + const struct fscrypt_name *nm) { struct ubifs_dent_node *dent; int nlen, err; @@ -559,11 +589,11 @@ static int matches_name(struct ubifs_info *c, struct ubifs_zbranch *zbr, dent = zbr->leaf; nlen = le16_to_cpu(dent->nlen); - err = memcmp(dent->name, nm->name, min_t(int, nlen, nm->len)); + err = memcmp(dent->name, fname_name(nm), min_t(int, nlen, fname_len(nm))); if (err == 0) { - if (nlen == nm->len) + if (nlen == fname_len(nm)) return NAME_MATCHES; - else if (nlen < nm->len) + else if (nlen < fname_len(nm)) return NAME_LESS; else return NAME_GREATER; @@ -706,7 +736,7 @@ static int tnc_prev(struct ubifs_info *c, struct ubifs_znode **zn, int *n) */ static int resolve_collision(struct ubifs_info *c, const union ubifs_key *key, struct ubifs_znode **zn, int *n, - const struct qstr *nm) + const struct fscrypt_name *nm) { int err; @@ -721,7 +751,7 @@ static int resolve_collision(struct ubifs_info *c, const union ubifs_key *key, while (1) { err = tnc_prev(c, zn, n); if (err == -ENOENT) { - ubifs_assert(*n == 0); + ubifs_assert(c, *n == 0); *n = -1; return 0; } @@ -761,12 +791,12 @@ static int resolve_collision(struct ubifs_info *c, const union ubifs_key *key, err = tnc_next(c, zn, n); if (err) { /* Should be impossible */ - ubifs_assert(0); + ubifs_assert(c, 0); if (err == -ENOENT) err = -EINVAL; return err; } - ubifs_assert(*n == 0); + ubifs_assert(c, *n == 0); *n = -1; } return 0; @@ -778,7 +808,7 @@ static int resolve_collision(struct ubifs_info *c, const union ubifs_key *key, return 0; if (err == NAME_MATCHES) return 1; - ubifs_assert(err == NAME_GREATER); + ubifs_assert(c, err == NAME_GREATER); } } else { int nn = *n; @@ -802,7 +832,7 @@ static int resolve_collision(struct ubifs_info *c, const union ubifs_key *key, *n = nn; if (err == NAME_MATCHES) return 1; - ubifs_assert(err == NAME_LESS); + ubifs_assert(c, err == NAME_LESS); } } } @@ -824,7 +854,7 @@ static int resolve_collision(struct ubifs_info *c, const union ubifs_key *key, */ static int fallible_matches_name(struct ubifs_info *c, struct ubifs_zbranch *zbr, - const struct qstr *nm) + const struct fscrypt_name *nm) { struct ubifs_dent_node *dent; int nlen, err; @@ -843,7 +873,7 @@ static int fallible_matches_name(struct ubifs_info *c, err = NOT_ON_MEDIA; goto out_free; } - ubifs_assert(err == 1); + ubifs_assert(c, err == 1); err = lnc_add_directly(c, zbr, dent); if (err) @@ -852,11 +882,11 @@ static int fallible_matches_name(struct ubifs_info *c, dent = zbr->leaf; nlen = le16_to_cpu(dent->nlen); - err = memcmp(dent->name, nm->name, min_t(int, nlen, nm->len)); + err = memcmp(dent->name, fname_name(nm), min_t(int, nlen, fname_len(nm))); if (err == 0) { - if (nlen == nm->len) + if (nlen == fname_len(nm)) return NAME_MATCHES; - else if (nlen < nm->len) + else if (nlen < fname_len(nm)) return NAME_LESS; else return NAME_GREATER; @@ -895,10 +925,11 @@ out_free: static int fallible_resolve_collision(struct ubifs_info *c, const union ubifs_key *key, struct ubifs_znode **zn, int *n, - const struct qstr *nm, int adding) + const struct fscrypt_name *nm, + int adding) { struct ubifs_znode *o_znode = NULL, *znode = *zn; - int uninitialized_var(o_n), err, cmp, unsure = 0, nn = *n; + int o_n, err, cmp, unsure = 0, nn = *n; cmp = fallible_matches_name(c, &znode->zbranch[nn], nm); if (unlikely(cmp < 0)) @@ -922,7 +953,7 @@ static int fallible_resolve_collision(struct ubifs_info *c, while (1) { err = tnc_prev(c, zn, n); if (err == -ENOENT) { - ubifs_assert(*n == 0); + ubifs_assert(c, *n == 0); *n = -1; break; } @@ -934,12 +965,12 @@ static int fallible_resolve_collision(struct ubifs_info *c, err = tnc_next(c, zn, n); if (err) { /* Should be impossible */ - ubifs_assert(0); + ubifs_assert(c, 0); if (err == -ENOENT) err = -EINVAL; return err; } - ubifs_assert(*n == 0); + ubifs_assert(c, *n == 0); *n = -1; } break; @@ -1099,12 +1130,13 @@ static struct ubifs_znode *dirty_cow_bottom_up(struct ubifs_info *c, struct ubifs_znode *zp; int *path = c->bottom_up_buf, p = 0; - ubifs_assert(c->zroot.znode); - ubifs_assert(znode); + ubifs_assert(c, c->zroot.znode); + ubifs_assert(c, znode); if (c->zroot.znode->level > BOTTOM_UP_HEIGHT) { kfree(c->bottom_up_buf); - c->bottom_up_buf = kmalloc(c->zroot.znode->level * sizeof(int), - GFP_NOFS); + c->bottom_up_buf = kmalloc_array(c->zroot.znode->level, + sizeof(int), + GFP_NOFS); if (!c->bottom_up_buf) return ERR_PTR(-ENOMEM); path = c->bottom_up_buf; @@ -1118,7 +1150,7 @@ static struct ubifs_znode *dirty_cow_bottom_up(struct ubifs_info *c, if (!zp) break; n = znode->iip; - ubifs_assert(p < c->zroot.znode->level); + ubifs_assert(c, p < c->zroot.znode->level); path[p++] = n; if (!zp->cnext && ubifs_zn_dirty(znode)) break; @@ -1132,18 +1164,18 @@ static struct ubifs_znode *dirty_cow_bottom_up(struct ubifs_info *c, zp = znode->parent; if (zp) { - ubifs_assert(path[p - 1] >= 0); - ubifs_assert(path[p - 1] < zp->child_cnt); + ubifs_assert(c, path[p - 1] >= 0); + ubifs_assert(c, path[p - 1] < zp->child_cnt); zbr = &zp->zbranch[path[--p]]; znode = dirty_cow_znode(c, zbr); } else { - ubifs_assert(znode == c->zroot.znode); + ubifs_assert(c, znode == c->zroot.znode); znode = dirty_cow_znode(c, &c->zroot); } if (IS_ERR(znode) || !p) break; - ubifs_assert(path[p - 1] >= 0); - ubifs_assert(path[p - 1] < znode->child_cnt); + ubifs_assert(c, path[p - 1] >= 0); + ubifs_assert(c, path[p - 1] < znode->child_cnt); znode = znode->zbranch[path[p - 1]].znode; } @@ -1163,8 +1195,8 @@ static struct ubifs_znode *dirty_cow_bottom_up(struct ubifs_info *c, * o exact match, i.e. the found zero-level znode contains key @key, then %1 * is returned and slot number of the matched branch is stored in @n; * o not exact match, which means that zero-level znode does not contain - * @key, then %0 is returned and slot number of the closest branch is stored - * in @n; + * @key, then %0 is returned and slot number of the closest branch or %-1 + * is stored in @n; In this case calling tnc_next() is mandatory. * o @key is so small that it is even less than the lowest key of the * leftmost zero-level node, then %0 is returned and %0 is stored in @n. * @@ -1177,10 +1209,10 @@ int ubifs_lookup_level0(struct ubifs_info *c, const union ubifs_key *key, { int err, exact; struct ubifs_znode *znode; - unsigned long time = get_seconds(); + time64_t time = ktime_get_seconds(); dbg_tnck(key, "search key "); - ubifs_assert(key_type(c, key) < UBIFS_INVALID_KEY); + ubifs_assert(c, key_type(c, key) < UBIFS_INVALID_KEY); znode = c->zroot.znode; if (unlikely(!znode)) { @@ -1313,7 +1345,7 @@ static int lookup_level0_dirty(struct ubifs_info *c, const union ubifs_key *key, { int err, exact; struct ubifs_znode *znode; - unsigned long time = get_seconds(); + time64_t time = ktime_get_seconds(); dbg_tnck(key, "search and dirty key "); @@ -1470,7 +1502,7 @@ again: * In this case the leaf node cache gets used, so we pass the * address of the zbranch and keep the mutex locked */ - err = tnc_read_node_nm(c, zt, node); + err = tnc_read_hashed_node(c, zt, node); goto out; } if (safely) { @@ -1519,8 +1551,8 @@ out: */ int ubifs_tnc_get_bu_keys(struct ubifs_info *c, struct bu_info *bu) { - int n, err = 0, lnum = -1, uninitialized_var(offs); - int uninitialized_var(len); + int n, err = 0, lnum = -1, offs; + int len; unsigned int block = key_block(c, &bu->key); struct ubifs_znode *znode; @@ -1656,9 +1688,9 @@ static int read_wbuf(struct ubifs_wbuf *wbuf, void *buf, int len, int lnum, int rlen, overlap; dbg_io("LEB %d:%d, length %d", lnum, offs, len); - ubifs_assert(wbuf && lnum >= 0 && lnum < c->leb_cnt && offs >= 0); - ubifs_assert(!(offs & 7) && offs < c->leb_size); - ubifs_assert(offs + len <= c->leb_size); + ubifs_assert(c, wbuf && lnum >= 0 && lnum < c->leb_cnt && offs >= 0); + ubifs_assert(c, !(offs & 7) && offs < c->leb_size); + ubifs_assert(c, offs + len <= c->leb_size); spin_lock(&wbuf->lock); overlap = (lnum == wbuf->lnum && offs + len > wbuf->offs); @@ -1700,27 +1732,33 @@ static int validate_data_node(struct ubifs_info *c, void *buf, int err, len; if (ch->node_type != UBIFS_DATA_NODE) { - ubifs_err("bad node type (%d but expected %d)", + ubifs_err(c, "bad node type (%d but expected %d)", ch->node_type, UBIFS_DATA_NODE); goto out_err; } - err = ubifs_check_node(c, buf, zbr->lnum, zbr->offs, 0, 0); + err = ubifs_check_node(c, buf, zbr->len, zbr->lnum, zbr->offs, 0, 0); if (err) { - ubifs_err("expected node type %d", UBIFS_DATA_NODE); + ubifs_err(c, "expected node type %d", UBIFS_DATA_NODE); goto out; } + err = ubifs_node_check_hash(c, buf, zbr->hash); + if (err) { + ubifs_bad_hash(c, buf, zbr->hash, zbr->lnum, zbr->offs); + return err; + } + len = le32_to_cpu(ch->len); if (len != zbr->len) { - ubifs_err("bad node length %d, expected %d", len, zbr->len); + ubifs_err(c, "bad node length %d, expected %d", len, zbr->len); goto out_err; } /* Make sure the key of the read node is correct */ key_read(c, buf + UBIFS_KEY_OFFSET, &key1); if (!keys_eq(c, &zbr->key, &key1)) { - ubifs_err("bad key in node at LEB %d:%d", + ubifs_err(c, "bad key in node at LEB %d:%d", zbr->lnum, zbr->offs); dbg_tnck(&zbr->key, "looked for key "); dbg_tnck(&key1, "found node's key "); @@ -1732,8 +1770,8 @@ static int validate_data_node(struct ubifs_info *c, void *buf, out_err: err = -EINVAL; out: - ubifs_err("bad node at LEB %d:%d", zbr->lnum, zbr->offs); - ubifs_dump_node(c, buf); + ubifs_err(c, "bad node at LEB %d:%d", zbr->lnum, zbr->offs); + ubifs_dump_node(c, buf, zbr->len); dump_stack(); return err; } @@ -1757,7 +1795,7 @@ int ubifs_tnc_bulk_read(struct ubifs_info *c, struct bu_info *bu) len = bu->zbranch[bu->cnt - 1].offs; len += bu->zbranch[bu->cnt - 1].len - offs; if (len > bu->buf_len) { - ubifs_err("buffer too small %d vs %d", bu->buf_len, len); + ubifs_err(c, "buffer too small %d vs %d", bu->buf_len, len); return -EINVAL; } @@ -1773,7 +1811,7 @@ int ubifs_tnc_bulk_read(struct ubifs_info *c, struct bu_info *bu) return -EAGAIN; if (err && err != -EBADMSG) { - ubifs_err("failed to read from LEB %d:%d, error %d", + ubifs_err(c, "failed to read from LEB %d:%d, error %d", lnum, offs, err); dump_stack(); dbg_tnck(&bu->key, "key "); @@ -1799,19 +1837,19 @@ int ubifs_tnc_bulk_read(struct ubifs_info *c, struct bu_info *bu) * @node: the node is returned here * @nm: node name * - * This function look up and reads a node which contains name hash in the key. + * This function looks up and reads a node which contains name hash in the key. * Since the hash may have collisions, there may be many nodes with the same * key, so we have to sequentially look to all of them until the needed one is * found. This function returns zero in case of success, %-ENOENT if the node * was not found, and a negative error code in case of failure. */ static int do_lookup_nm(struct ubifs_info *c, const union ubifs_key *key, - void *node, const struct qstr *nm) + void *node, const struct fscrypt_name *nm) { int found, n, err; struct ubifs_znode *znode; - dbg_tnck(key, "name '%.*s' key ", nm->len, nm->name); + dbg_tnck(key, "key "); mutex_lock(&c->tnc_mutex); found = ubifs_lookup_level0(c, key, &znode, &n); if (!found) { @@ -1822,7 +1860,7 @@ static int do_lookup_nm(struct ubifs_info *c, const union ubifs_key *key, goto out_unlock; } - ubifs_assert(n >= 0); + ubifs_assert(c, n >= 0); err = resolve_collision(c, key, &znode, &n, nm); dbg_tnc("rc returned %d, znode %p, n %d", err, znode, n); @@ -1833,7 +1871,7 @@ static int do_lookup_nm(struct ubifs_info *c, const union ubifs_key *key, goto out_unlock; } - err = tnc_read_node_nm(c, &znode->zbranch[n], node); + err = tnc_read_hashed_node(c, &znode->zbranch[n], node); out_unlock: mutex_unlock(&c->tnc_mutex); @@ -1847,14 +1885,14 @@ out_unlock: * @node: the node is returned here * @nm: node name * - * This function look up and reads a node which contains name hash in the key. + * This function looks up and reads a node which contains name hash in the key. * Since the hash may have collisions, there may be many nodes with the same * key, so we have to sequentially look to all of them until the needed one is * found. This function returns zero in case of success, %-ENOENT if the node * was not found, and a negative error code in case of failure. */ int ubifs_tnc_lookup_nm(struct ubifs_info *c, const union ubifs_key *key, - void *node, const struct qstr *nm) + void *node, const struct fscrypt_name *nm) { int err, len; const struct ubifs_dent_node *dent = node; @@ -1868,16 +1906,121 @@ int ubifs_tnc_lookup_nm(struct ubifs_info *c, const union ubifs_key *key, return err; len = le16_to_cpu(dent->nlen); - if (nm->len == len && !memcmp(dent->name, nm->name, len)) + if (fname_len(nm) == len && !memcmp(dent->name, fname_name(nm), len)) return 0; /* * Unluckily, there are hash collisions and we have to iterate over * them look at each direntry with colliding name hash sequentially. */ + return do_lookup_nm(c, key, node, nm); } +static int search_dh_cookie(struct ubifs_info *c, const union ubifs_key *key, + struct ubifs_dent_node *dent, uint32_t cookie, + struct ubifs_znode **zn, int *n, int exact) +{ + int err; + struct ubifs_znode *znode = *zn; + struct ubifs_zbranch *zbr; + union ubifs_key *dkey; + + if (!exact) { + err = tnc_next(c, &znode, n); + if (err) + return err; + } + + for (;;) { + zbr = &znode->zbranch[*n]; + dkey = &zbr->key; + + if (key_inum(c, dkey) != key_inum(c, key) || + key_type(c, dkey) != key_type(c, key)) { + return -ENOENT; + } + + err = tnc_read_hashed_node(c, zbr, dent); + if (err) + return err; + + if (key_hash(c, key) == key_hash(c, dkey) && + le32_to_cpu(dent->cookie) == cookie) { + *zn = znode; + return 0; + } + + err = tnc_next(c, &znode, n); + if (err) + return err; + } +} + +static int do_lookup_dh(struct ubifs_info *c, const union ubifs_key *key, + struct ubifs_dent_node *dent, uint32_t cookie) +{ + int n, err; + struct ubifs_znode *znode; + union ubifs_key start_key; + + ubifs_assert(c, is_hash_key(c, key)); + + lowest_dent_key(c, &start_key, key_inum(c, key)); + + mutex_lock(&c->tnc_mutex); + err = ubifs_lookup_level0(c, &start_key, &znode, &n); + if (unlikely(err < 0)) + goto out_unlock; + + err = search_dh_cookie(c, key, dent, cookie, &znode, &n, err); + +out_unlock: + mutex_unlock(&c->tnc_mutex); + return err; +} + +/** + * ubifs_tnc_lookup_dh - look up a "double hashed" node. + * @c: UBIFS file-system description object + * @key: node key to lookup + * @node: the node is returned here + * @cookie: node cookie for collision resolution + * + * This function looks up and reads a node which contains name hash in the key. + * Since the hash may have collisions, there may be many nodes with the same + * key, so we have to sequentially look to all of them until the needed one + * with the same cookie value is found. + * This function returns zero in case of success, %-ENOENT if the node + * was not found, and a negative error code in case of failure. + */ +int ubifs_tnc_lookup_dh(struct ubifs_info *c, const union ubifs_key *key, + void *node, uint32_t cookie) +{ + int err; + const struct ubifs_dent_node *dent = node; + + if (!c->double_hash) + return -EOPNOTSUPP; + + /* + * We assume that in most of the cases there are no name collisions and + * 'ubifs_tnc_lookup()' returns us the right direntry. + */ + err = ubifs_tnc_lookup(c, key, node); + if (err) + return err; + + if (le32_to_cpu(dent->cookie) == cookie) + return 0; + + /* + * Unluckily, there are hash collisions and we have to iterate over + * them look at each direntry with colliding name hash sequentially. + */ + return do_lookup_dh(c, key, node, cookie); +} + /** * correct_parent_keys - correct parent znodes' keys. * @c: UBIFS file-system description object @@ -1892,8 +2035,8 @@ static void correct_parent_keys(const struct ubifs_info *c, { union ubifs_key *key, *key1; - ubifs_assert(znode->parent); - ubifs_assert(znode->iip == 0); + ubifs_assert(c, znode->parent); + ubifs_assert(c, znode->iip == 0); key = &znode->zbranch[0].key; key1 = &znode->parent->zbranch[0].key; @@ -1910,6 +2053,7 @@ static void correct_parent_keys(const struct ubifs_info *c, /** * insert_zbranch - insert a zbranch into a znode. + * @c: UBIFS file-system description object * @znode: znode into which to insert * @zbr: zbranch to insert * @n: slot number to insert to @@ -1919,12 +2063,12 @@ static void correct_parent_keys(const struct ubifs_info *c, * zbranch has to be inserted to the @znode->zbranches[]' array at the @n-th * slot, zbranches starting from @n have to be moved right. */ -static void insert_zbranch(struct ubifs_znode *znode, +static void insert_zbranch(struct ubifs_info *c, struct ubifs_znode *znode, const struct ubifs_zbranch *zbr, int n) { int i; - ubifs_assert(ubifs_zn_dirty(znode)); + ubifs_assert(c, ubifs_zn_dirty(znode)); if (znode->level) { for (i = znode->child_cnt; i > n; i--) { @@ -1978,16 +2122,16 @@ static int tnc_insert(struct ubifs_info *c, struct ubifs_znode *znode, int i, keep, move, appending = 0; union ubifs_key *key = &zbr->key, *key1; - ubifs_assert(n >= 0 && n <= c->fanout); + ubifs_assert(c, n >= 0 && n <= c->fanout); /* Implement naive insert for now */ again: zp = znode->parent; if (znode->child_cnt < c->fanout) { - ubifs_assert(n != c->fanout); + ubifs_assert(c, n != c->fanout); dbg_tnck(key, "inserted at %d level %d, key ", n, znode->level); - insert_zbranch(znode, zbr, n); + insert_zbranch(c, znode, zbr, n); /* Ensure parent's key is correct */ if (n == 0 && zp && znode->iip == 0) @@ -2096,7 +2240,7 @@ do_split: /* Insert new key and branch */ dbg_tnck(key, "inserting at %d level %d, key ", n, zn->level); - insert_zbranch(zi, zbr, n); + insert_zbranch(c, zi, zbr, n); /* Insert new znode (produced by spitting) into the parent */ if (zp) { @@ -2158,13 +2302,14 @@ do_split: * @lnum: LEB number of node * @offs: node offset * @len: node length + * @hash: The hash over the node * * This function adds a node with key @key to TNC. The node may be new or it may * obsolete some existing one. Returns %0 on success or negative error code on * failure. */ int ubifs_tnc_add(struct ubifs_info *c, const union ubifs_key *key, int lnum, - int offs, int len) + int offs, int len, const u8 *hash) { int found, n, err = 0; struct ubifs_znode *znode; @@ -2179,6 +2324,7 @@ int ubifs_tnc_add(struct ubifs_info *c, const union ubifs_key *key, int lnum, zbr.lnum = lnum; zbr.offs = offs; zbr.len = len; + ubifs_copy_hash(c, hash, zbr.hash); key_copy(c, key, &zbr.key); err = tnc_insert(c, znode, &zbr, n + 1); } else if (found == 1) { @@ -2189,6 +2335,7 @@ int ubifs_tnc_add(struct ubifs_info *c, const union ubifs_key *key, int lnum, zbr->lnum = lnum; zbr->offs = offs; zbr->len = len; + ubifs_copy_hash(c, hash, zbr->hash); } else err = found; if (!err) @@ -2290,20 +2437,21 @@ out_unlock: * @lnum: LEB number of node * @offs: node offset * @len: node length + * @hash: The hash over the node * @nm: node name * * This is the same as 'ubifs_tnc_add()' but it should be used with keys which * may have collisions, like directory entry keys. */ int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key, - int lnum, int offs, int len, const struct qstr *nm) + int lnum, int offs, int len, const u8 *hash, + const struct fscrypt_name *nm) { int found, n, err = 0; struct ubifs_znode *znode; mutex_lock(&c->tnc_mutex); - dbg_tnck(key, "LEB %d:%d, name '%.*s', key ", - lnum, offs, nm->len, nm->name); + dbg_tnck(key, "LEB %d:%d, key ", lnum, offs); found = lookup_level0_dirty(c, key, &znode, &n); if (found < 0) { err = found; @@ -2339,6 +2487,7 @@ int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key, zbr->lnum = lnum; zbr->offs = offs; zbr->len = len; + ubifs_copy_hash(c, hash, zbr->hash); goto out_unlock; } } @@ -2350,6 +2499,7 @@ int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key, zbr.lnum = lnum; zbr.offs = offs; zbr.len = len; + ubifs_copy_hash(c, hash, zbr.hash); key_copy(c, key, &zbr.key); err = tnc_insert(c, znode, &zbr, n + 1); if (err) @@ -2361,7 +2511,7 @@ int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key, * by passing 'ubifs_tnc_remove_nm()' the same key but * an unmatchable name. */ - struct qstr noname = { .name = "" }; + struct fscrypt_name noname = { .disk_name = { .name = "", .len = 1 } }; err = dbg_check_tnc(c, 0); mutex_unlock(&c->tnc_mutex); @@ -2394,8 +2544,8 @@ static int tnc_delete(struct ubifs_info *c, struct ubifs_znode *znode, int n) int i, err; /* Delete without merge for now */ - ubifs_assert(znode->level == 0); - ubifs_assert(n >= 0 && n < c->fanout); + ubifs_assert(c, znode->level == 0); + ubifs_assert(c, n >= 0 && n < c->fanout); dbg_tnck(&znode->zbranch[n].key, "deleting key "); zbr = &znode->zbranch[n]; @@ -2421,8 +2571,8 @@ static int tnc_delete(struct ubifs_info *c, struct ubifs_znode *znode, int n) */ do { - ubifs_assert(!ubifs_zn_obsolete(znode)); - ubifs_assert(ubifs_zn_dirty(znode)); + ubifs_assert(c, !ubifs_zn_obsolete(znode)); + ubifs_assert(c, ubifs_zn_dirty(znode)); zp = znode->parent; n = znode->iip; @@ -2444,7 +2594,7 @@ static int tnc_delete(struct ubifs_info *c, struct ubifs_znode *znode, int n) /* Remove from znode, entry n - 1 */ znode->child_cnt -= 1; - ubifs_assert(znode->level != 0); + ubifs_assert(c, znode->level != 0); for (i = n; i < znode->child_cnt; i++) { znode->zbranch[i] = znode->zbranch[i + 1]; if (znode->zbranch[i].znode) @@ -2477,8 +2627,8 @@ static int tnc_delete(struct ubifs_info *c, struct ubifs_znode *znode, int n) c->zroot.offs = zbr->offs; c->zroot.len = zbr->len; c->zroot.znode = znode; - ubifs_assert(!ubifs_zn_obsolete(zp)); - ubifs_assert(ubifs_zn_dirty(zp)); + ubifs_assert(c, !ubifs_zn_obsolete(zp)); + ubifs_assert(c, ubifs_zn_dirty(zp)); atomic_long_dec(&c->dirty_zn_cnt); if (zp->cnext) { @@ -2531,13 +2681,13 @@ out_unlock: * Returns %0 on success or negative error code on failure. */ int ubifs_tnc_remove_nm(struct ubifs_info *c, const union ubifs_key *key, - const struct qstr *nm) + const struct fscrypt_name *nm) { int n, err; struct ubifs_znode *znode; mutex_lock(&c->tnc_mutex); - dbg_tnck(key, "%.*s, key ", nm->len, nm->name); + dbg_tnck(key, "key "); err = lookup_level0_dirty(c, key, &znode, &n); if (err < 0) goto out_unlock; @@ -2572,6 +2722,74 @@ out_unlock: } /** + * ubifs_tnc_remove_dh - remove an index entry for a "double hashed" node. + * @c: UBIFS file-system description object + * @key: key of node + * @cookie: node cookie for collision resolution + * + * Returns %0 on success or negative error code on failure. + */ +int ubifs_tnc_remove_dh(struct ubifs_info *c, const union ubifs_key *key, + uint32_t cookie) +{ + int n, err; + struct ubifs_znode *znode; + struct ubifs_dent_node *dent; + struct ubifs_zbranch *zbr; + + if (!c->double_hash) + return -EOPNOTSUPP; + + mutex_lock(&c->tnc_mutex); + err = lookup_level0_dirty(c, key, &znode, &n); + if (err <= 0) + goto out_unlock; + + zbr = &znode->zbranch[n]; + dent = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS); + if (!dent) { + err = -ENOMEM; + goto out_unlock; + } + + err = tnc_read_hashed_node(c, zbr, dent); + if (err) + goto out_free; + + /* If the cookie does not match, we're facing a hash collision. */ + if (le32_to_cpu(dent->cookie) != cookie) { + union ubifs_key start_key; + + lowest_dent_key(c, &start_key, key_inum(c, key)); + + err = ubifs_lookup_level0(c, &start_key, &znode, &n); + if (unlikely(err < 0)) + goto out_free; + + err = search_dh_cookie(c, key, dent, cookie, &znode, &n, err); + if (err) + goto out_free; + } + + if (znode->cnext || !ubifs_zn_dirty(znode)) { + znode = dirty_cow_bottom_up(c, znode); + if (IS_ERR(znode)) { + err = PTR_ERR(znode); + goto out_free; + } + } + err = tnc_delete(c, znode, n); + +out_free: + kfree(dent); +out_unlock: + if (!err) + err = dbg_check_tnc(c, 0); + mutex_unlock(&c->tnc_mutex); + return err; +} + +/** * key_in_range - determine if a key falls within a range of keys. * @c: UBIFS file-system description object * @key: key to check @@ -2686,7 +2904,7 @@ int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum) { union ubifs_key key1, key2; struct ubifs_dent_node *xent, *pxent = NULL; - struct qstr nm = { .name = NULL }; + struct fscrypt_name nm = {0}; dbg_tnc("ino %lu", (unsigned long)inum); @@ -2704,6 +2922,7 @@ int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum) err = PTR_ERR(xent); if (err == -ENOENT) break; + kfree(pxent); return err; } @@ -2711,10 +2930,11 @@ int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum) dbg_tnc("xent '%s', ino %lu", xent->name, (unsigned long)xattr_inum); - nm.name = xent->name; - nm.len = le16_to_cpu(xent->nlen); + fname_name(&nm) = xent->name; + fname_len(&nm) = le16_to_cpu(xent->nlen); err = ubifs_tnc_remove_nm(c, &key1, &nm); if (err) { + kfree(pxent); kfree(xent); return err; } @@ -2723,6 +2943,7 @@ int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum) highest_ino_key(c, &key2, xattr_inum); err = ubifs_tnc_remove_range(c, &key1, &key2); if (err) { + kfree(pxent); kfree(xent); return err; } @@ -2764,7 +2985,7 @@ int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum) */ struct ubifs_dent_node *ubifs_tnc_next_ent(struct ubifs_info *c, union ubifs_key *key, - const struct qstr *nm) + const struct fscrypt_name *nm) { int n, err, type = key_type(c, key); struct ubifs_znode *znode; @@ -2772,18 +2993,22 @@ struct ubifs_dent_node *ubifs_tnc_next_ent(struct ubifs_info *c, struct ubifs_zbranch *zbr; union ubifs_key *dkey; - dbg_tnck(key, "%s ", nm->name ? (char *)nm->name : "(lowest)"); - ubifs_assert(is_hash_key(c, key)); + dbg_tnck(key, "key "); + ubifs_assert(c, is_hash_key(c, key)); mutex_lock(&c->tnc_mutex); err = ubifs_lookup_level0(c, key, &znode, &n); if (unlikely(err < 0)) goto out_unlock; - if (nm->name) { + if (fname_len(nm) > 0) { if (err) { /* Handle collisions */ - err = resolve_collision(c, key, &znode, &n, nm); + if (c->replaying) + err = fallible_resolve_collision(c, key, &znode, &n, + nm, 0); + else + err = resolve_collision(c, key, &znode, &n, nm); dbg_tnc("rc returned %d, znode %p, n %d", err, znode, n); if (unlikely(err < 0)) @@ -2830,7 +3055,7 @@ struct ubifs_dent_node *ubifs_tnc_next_ent(struct ubifs_info *c, goto out_free; } - err = tnc_read_node_nm(c, zbr, dent); + err = tnc_read_hashed_node(c, zbr, dent); if (unlikely(err)) goto out_free; @@ -2856,7 +3081,7 @@ static void tnc_destroy_cnext(struct ubifs_info *c) if (!c->cnext) return; - ubifs_assert(c->cmt_state == COMMIT_BROKEN); + ubifs_assert(c, c->cmt_state == COMMIT_BROKEN); cnext = c->cnext; do { struct ubifs_znode *znode = cnext; @@ -2864,6 +3089,21 @@ static void tnc_destroy_cnext(struct ubifs_info *c) cnext = cnext->cnext; if (ubifs_zn_obsolete(znode)) kfree(znode); + else if (!ubifs_zn_cow(znode)) { + /* + * Don't forget to update clean znode count after + * committing failed, because ubifs will check this + * count while closing tnc. Non-obsolete znode could + * be re-dirtied during committing process, so dirty + * flag is untrustable. The flag 'COW_ZNODE' is set + * for each dirty znode before committing, and it is + * cleared as long as the znode become clean, so we + * can statistic clean znode count according to this + * flag. + */ + atomic_long_inc(&c->clean_zn_cnt); + atomic_long_inc(&ubifs_clean_zn_cnt); + } } while (cnext && cnext != c->cnext); } @@ -2874,13 +3114,7 @@ static void tnc_destroy_cnext(struct ubifs_info *c) void ubifs_tnc_close(struct ubifs_info *c) { tnc_destroy_cnext(c); - if (c->zroot.znode) { - long n; - - ubifs_destroy_tnc_subtree(c->zroot.znode); - n = atomic_long_read(&c->clean_zn_cnt); - atomic_long_sub(n, &ubifs_clean_zn_cnt); - } + ubifs_destroy_tnc_tree(c); kfree(c->gap_lebs); kfree(c->ilebs); destroy_old_idx(c); @@ -2991,7 +3225,7 @@ static struct ubifs_znode *lookup_znode(struct ubifs_info *c, struct ubifs_znode *znode, *zn; int n, nn; - ubifs_assert(key_type(c, key) < UBIFS_INVALID_KEY); + ubifs_assert(c, key_type(c, key) < UBIFS_INVALID_KEY); /* * The arguments have probably been read off flash, so don't assume @@ -3030,7 +3264,7 @@ static struct ubifs_znode *lookup_znode(struct ubifs_info *c, if (IS_ERR(znode)) return znode; ubifs_search_zbranch(c, znode, key, &n); - ubifs_assert(n >= 0); + ubifs_assert(c, n >= 0); } if (znode->level == level + 1) break; @@ -3278,7 +3512,7 @@ out_unlock: /** * dbg_check_inode_size - check if inode size is correct. * @c: UBIFS file-system description object - * @inum: inode number + * @inode: inode to check * @size: inode size * * This function makes sure that the inode size (@size) is correct and it does @@ -3309,7 +3543,6 @@ int dbg_check_inode_size(struct ubifs_info *c, const struct inode *inode, goto out_unlock; if (err) { - err = -EINVAL; key = &from_key; goto out_dump; } @@ -3322,14 +3555,14 @@ int dbg_check_inode_size(struct ubifs_info *c, const struct inode *inode, if (err < 0) goto out_unlock; - ubifs_assert(err == 0); + ubifs_assert(c, err == 0); key = &znode->zbranch[n].key; if (!key_in_range(c, key, &from_key, &to_key)) goto out_unlock; out_dump: block = key_block(c, key); - ubifs_err("inode %lu has size %lld, but there are data at offset %lld", + ubifs_err(c, "inode %lu has size %lld, but there are data at offset %lld", (unsigned long)inode->i_ino, size, ((loff_t)block) << UBIFS_BLOCK_SHIFT); mutex_unlock(&c->tnc_mutex); |
