ubifs: orphan: Handle xattrs like files
authorRichard Weinberger <richard@nod.at>
Thu, 4 Apr 2019 22:34:37 +0000 (00:34 +0200)
committerRichard Weinberger <richard@nod.at>
Tue, 7 May 2019 19:58:30 +0000 (21:58 +0200)
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: 1e51764a3c2ac ("UBIFS: add new flash file system")
Signed-off-by: Richard Weinberger <richard@nod.at>
fs/ubifs/orphan.c
fs/ubifs/ubifs.h

index 8f70494..2f1618f 100644 (file)
 
 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,24 +94,22 @@ 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 0;
+       return orphan;
 }
 
-/**
- * ubifs_delete_orphan - delete an orphan.
- * @c: UBIFS file-system description object
- * @inum: orphan inode number
- *
- * Delete an orphan. This function is called when an inode is deleted.
- */
-void ubifs_delete_orphan(struct ubifs_info *c, ino_t inum)
+static struct ubifs_orphan *lookup_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);
@@ -126,37 +118,124 @@ void ubifs_delete_orphan(struct ubifs_info *c, ino_t inum)
                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;
+                       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_err(c, "missing orphan ino %lu", (unsigned long)inum);
-       dump_stack();
+}
+
+/**
+ * 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;
+}
+
+/**
+ * ubifs_delete_orphan - delete an orphan.
+ * @c: UBIFS file-system description object
+ * @inum: orphan inode number
+ *
+ * Delete an orphan. This function is called when an inode is deleted.
+ */
+void ubifs_delete_orphan(struct ubifs_info *c, ino_t inum)
+{
+       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)
index 1ae1290..d281028 100644 (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;