ubifs: orphan: Handle xattrs like files

Like for the journal case, make sure that we track all xattr
inodes.
Otherwise UBIFS might not be able to locate stale xattr inodes
upon recovery.

Reported-by: Stefan Agner <stefan@agner.ch>
Fixes: 1e51764a3c ("UBIFS: add new flash file system")
Signed-off-by: Richard Weinberger <richard@nod.at>
This commit is contained in:
Richard Weinberger 2019-04-05 00:34:37 +02:00
parent 7959cf3a75
commit 988bec4131
2 changed files with 149 additions and 72 deletions

View File

@ -54,30 +54,24 @@
static int dbg_check_orphans(struct ubifs_info *c);
/**
* ubifs_add_orphan - add an orphan.
* @c: UBIFS file-system description object
* @inum: orphan inode number
*
* Add an orphan. This function is called when an inodes link count drops to
* zero.
*/
int ubifs_add_orphan(struct ubifs_info *c, ino_t inum)
static struct ubifs_orphan *orphan_add(struct ubifs_info *c, ino_t inum,
struct ubifs_orphan *parent_orphan)
{
struct ubifs_orphan *orphan, *o;
struct rb_node **p, *parent = NULL;
orphan = kzalloc(sizeof(struct ubifs_orphan), GFP_NOFS);
if (!orphan)
return -ENOMEM;
return ERR_PTR(-ENOMEM);
orphan->inum = inum;
orphan->new = 1;
INIT_LIST_HEAD(&orphan->child_list);
spin_lock(&c->orphan_lock);
if (c->tot_orphans >= c->max_orphans) {
spin_unlock(&c->orphan_lock);
kfree(orphan);
return -ENFILE;
return ERR_PTR(-ENFILE);
}
p = &c->orph_tree.rb_node;
while (*p) {
@ -91,7 +85,7 @@ int ubifs_add_orphan(struct ubifs_info *c, ino_t inum)
ubifs_err(c, "orphaned twice");
spin_unlock(&c->orphan_lock);
kfree(orphan);
return 0;
return ERR_PTR(-EINVAL);
}
}
c->tot_orphans += 1;
@ -100,8 +94,135 @@ int ubifs_add_orphan(struct ubifs_info *c, ino_t inum)
rb_insert_color(&orphan->rb, &c->orph_tree);
list_add_tail(&orphan->list, &c->orph_list);
list_add_tail(&orphan->new_list, &c->orph_new);
if (parent_orphan) {
list_add_tail(&orphan->child_list,
&parent_orphan->child_list);
}
spin_unlock(&c->orphan_lock);
dbg_gen("ino %lu", (unsigned long)inum);
return orphan;
}
static struct ubifs_orphan *lookup_orphan(struct ubifs_info *c, ino_t inum)
{
struct ubifs_orphan *o;
struct rb_node *p;
p = c->orph_tree.rb_node;
while (p) {
o = rb_entry(p, struct ubifs_orphan, rb);
if (inum < o->inum)
p = p->rb_left;
else if (inum > o->inum)
p = p->rb_right;
else {
return o;
}
}
return NULL;
}
static void __orphan_drop(struct ubifs_info *c, struct ubifs_orphan *o)
{
rb_erase(&o->rb, &c->orph_tree);
list_del(&o->list);
c->tot_orphans -= 1;
if (o->new) {
list_del(&o->new_list);
c->new_orphans -= 1;
}
kfree(o);
}
static void orphan_delete(struct ubifs_info *c, ino_t inum)
{
struct ubifs_orphan *orph, *child_orph, *tmp_o;
spin_lock(&c->orphan_lock);
orph = lookup_orphan(c, inum);
if (!orph) {
spin_unlock(&c->orphan_lock);
ubifs_err(c, "missing orphan ino %lu", (unsigned long)inum);
dump_stack();
return;
}
if (orph->del) {
spin_unlock(&c->orphan_lock);
dbg_gen("deleted twice ino %lu",
(unsigned long)inum);
return;
}
if (orph->cmt) {
orph->del = 1;
orph->dnext = c->orph_dnext;
c->orph_dnext = orph;
spin_unlock(&c->orphan_lock);
dbg_gen("delete later ino %lu",
(unsigned long)inum);
return;
}
list_for_each_entry_safe(child_orph, tmp_o, &orph->child_list, child_list) {
list_del(&child_orph->child_list);
__orphan_drop(c, child_orph);
}
__orphan_drop(c, orph);
spin_unlock(&c->orphan_lock);
}
/**
* ubifs_add_orphan - add an orphan.
* @c: UBIFS file-system description object
* @inum: orphan inode number
*
* Add an orphan. This function is called when an inodes link count drops to
* zero.
*/
int ubifs_add_orphan(struct ubifs_info *c, ino_t inum)
{
int err = 0;
ino_t xattr_inum;
union ubifs_key key;
struct ubifs_dent_node *xent;
struct fscrypt_name nm = {0};
struct ubifs_orphan *xattr_orphan;
struct ubifs_orphan *orphan;
orphan = orphan_add(c, inum, NULL);
if (IS_ERR(orphan))
return PTR_ERR(orphan);
lowest_xent_key(c, &key, inum);
while (1) {
xent = ubifs_tnc_next_ent(c, &key, &nm);
if (IS_ERR(xent)) {
err = PTR_ERR(xent);
if (err == -ENOENT)
break;
return err;
}
fname_name(&nm) = xent->name;
fname_len(&nm) = le16_to_cpu(xent->nlen);
xattr_inum = le64_to_cpu(xent->inum);
xattr_orphan = orphan_add(c, xattr_inum, orphan);
if (IS_ERR(xattr_orphan))
return PTR_ERR(xattr_orphan);
key_read(c, &xent->key, &key);
}
return 0;
}
@ -114,49 +235,7 @@ int ubifs_add_orphan(struct ubifs_info *c, ino_t inum)
*/
void ubifs_delete_orphan(struct ubifs_info *c, ino_t inum)
{
struct ubifs_orphan *o;
struct rb_node *p;
spin_lock(&c->orphan_lock);
p = c->orph_tree.rb_node;
while (p) {
o = rb_entry(p, struct ubifs_orphan, rb);
if (inum < o->inum)
p = p->rb_left;
else if (inum > o->inum)
p = p->rb_right;
else {
if (o->del) {
spin_unlock(&c->orphan_lock);
dbg_gen("deleted twice ino %lu",
(unsigned long)inum);
return;
}
if (o->cmt) {
o->del = 1;
o->dnext = c->orph_dnext;
c->orph_dnext = o;
spin_unlock(&c->orphan_lock);
dbg_gen("delete later ino %lu",
(unsigned long)inum);
return;
}
rb_erase(p, &c->orph_tree);
list_del(&o->list);
c->tot_orphans -= 1;
if (o->new) {
list_del(&o->new_list);
c->new_orphans -= 1;
}
spin_unlock(&c->orphan_lock);
kfree(o);
dbg_gen("inum %lu", (unsigned long)inum);
return;
}
}
spin_unlock(&c->orphan_lock);
ubifs_err(c, "missing orphan ino %lu", (unsigned long)inum);
dump_stack();
orphan_delete(c, inum);
}
/**
@ -611,10 +690,16 @@ static int do_kill_orphans(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
n = (le32_to_cpu(orph->ch.len) - UBIFS_ORPH_NODE_SZ) >> 3;
for (i = 0; i < n; i++) {
union ubifs_key key1, key2;
inum = le64_to_cpu(orph->inos[i]);
dbg_rcvry("deleting orphaned inode %lu",
(unsigned long)inum);
err = ubifs_tnc_remove_ino(c, inum);
lowest_ino_key(c, &key1, inum);
highest_ino_key(c, &key2, inum);
err = ubifs_tnc_remove_range(c, &key1, &key2);
if (err)
return err;
err = insert_dead_orphan(c, inum);
@ -744,26 +829,15 @@ struct check_info {
struct rb_root root;
};
static int dbg_find_orphan(struct ubifs_info *c, ino_t inum)
static bool dbg_find_orphan(struct ubifs_info *c, ino_t inum)
{
struct ubifs_orphan *o;
struct rb_node *p;
bool found = false;
spin_lock(&c->orphan_lock);
p = c->orph_tree.rb_node;
while (p) {
o = rb_entry(p, struct ubifs_orphan, rb);
if (inum < o->inum)
p = p->rb_left;
else if (inum > o->inum)
p = p->rb_right;
else {
spin_unlock(&c->orphan_lock);
return 1;
}
}
found = !!lookup_orphan(c, inum);
spin_unlock(&c->orphan_lock);
return 0;
return found;
}
static int dbg_ins_check_orphan(struct rb_root *root, ino_t inum)

View File

@ -924,6 +924,8 @@ struct ubifs_budget_req {
* @rb: rb-tree node of rb-tree of orphans sorted by inode number
* @list: list head of list of orphans in order added
* @new_list: list head of list of orphans added since the last commit
* @child_list: list of xattr childs if this orphan hosts xattrs, list head
* if this orphan is a xattr, not used otherwise.
* @cnext: next orphan to commit
* @dnext: next orphan to delete
* @inum: inode number
@ -935,6 +937,7 @@ struct ubifs_orphan {
struct rb_node rb;
struct list_head list;
struct list_head new_list;
struct list_head child_list;
struct ubifs_orphan *cnext;
struct ubifs_orphan *dnext;
ino_t inum;