]> www.infradead.org Git - mtd-utils.git/commitdiff
fsck.ubifs: Add file organization realization
authorZhihao Cheng <chengzhihao1@huawei.com>
Mon, 11 Nov 2024 09:01:07 +0000 (17:01 +0800)
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>
Mon, 11 Nov 2024 09:32:45 +0000 (10:32 +0100)
In order to check the consistency of each file and the reachability of
the whole dentry tree, fsck orginizes all nodes into files. And the
final recovered file(xattr is treated as a file) is organized as:
   (rbtree, inum indexed)
        /    \
     file1   file2
     /    \
  file3  file4

file {
        inode node // each file has 1 inode node
        dentry (sub rb_tree, sqnum indexed) // '/' has no dentries,
                                            // otherwise at least 1
                                            // dentry is required.
        trun node // the newest one truncation node
        data (sub rb_tree, block number indexed) // Each file may have 0
                                                 // or many data nodes
xattrs (sub rb_tree, inum indexed) // Each file may have 0 or
                                           // many xattr files
}

Each file from file rb_tree is constructed by scanning nodes(eg. inode,
dentry, etc.) from the TNC or the UBI volume. File's xattrs will be
initialized in subsequent steps.

Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
ubifs-utils/fsck.ubifs/extract_files.c
ubifs-utils/fsck.ubifs/fsck.ubifs.h

index dd52ef84547dad4bda34571bdc8e876702726318..58aa9db82e8944d718ac4c0a0956d5312b6ee9c7 100644 (file)
@@ -403,3 +403,278 @@ bool parse_trun_node(struct ubifs_info *c, int lnum, int offs, void *node,
 out:
        return valid;
 }
+
+/**
+ * insert_file_dentry - insert dentry according to scanned dent node.
+ * @file: file object
+ * @n_dent: scanned dent node
+ *
+ * Insert file dentry information. Returns zero in case of success, a
+ * negative error code in case of failure.
+ */
+static int insert_file_dentry(struct scanned_file *file,
+                             struct scanned_dent_node *n_dent)
+{
+       struct scanned_dent_node *dent;
+       struct rb_node **p, *parent = NULL;
+
+       p = &file->dent_nodes.rb_node;
+       while (*p) {
+               parent = *p;
+               dent = rb_entry(parent, struct scanned_dent_node, rb);
+               if (n_dent->header.sqnum < dent->header.sqnum)
+                       p = &(*p)->rb_left;
+               else
+                       p = &(*p)->rb_right;
+       }
+
+       dent = kmalloc(sizeof(struct scanned_dent_node), GFP_KERNEL);
+       if (!dent)
+               return -ENOMEM;
+
+       *dent = *n_dent;
+       rb_link_node(&dent->rb, parent, p);
+       rb_insert_color(&dent->rb, &file->dent_nodes);
+
+       return 0;
+}
+
+/**
+ * update_file_data - insert/update data according to scanned data node.
+ * @c: UBIFS file-system description object
+ * @file: file object
+ * @n_dn: scanned data node
+ *
+ * Insert or update file data information. Returns zero in case of success,
+ * a negative error code in case of failure.
+ */
+static int update_file_data(struct ubifs_info *c, struct scanned_file *file,
+                           struct scanned_data_node *n_dn)
+{
+       int cmp;
+       struct scanned_data_node *dn, *o_dn = NULL;
+       struct rb_node **p, *parent = NULL;
+
+       p = &file->data_nodes.rb_node;
+       while (*p) {
+               parent = *p;
+               dn = rb_entry(parent, struct scanned_data_node, rb);
+               cmp = keys_cmp(c, &n_dn->key, &dn->key);
+               if (cmp < 0) {
+                       p = &(*p)->rb_left;
+               } else if (cmp > 0) {
+                       p = &(*p)->rb_right;
+               } else {
+                       o_dn = dn;
+                       break;
+               }
+       }
+
+       if (o_dn) {
+               /* found data node with same block no. */
+               if (o_dn->header.sqnum < n_dn->header.sqnum) {
+                       o_dn->header = n_dn->header;
+                       o_dn->size = n_dn->size;
+               }
+
+               return 0;
+       }
+
+       dn = kmalloc(sizeof(struct scanned_data_node), GFP_KERNEL);
+       if (!dn)
+               return -ENOMEM;
+
+       *dn = *n_dn;
+       INIT_LIST_HEAD(&dn->list);
+       rb_link_node(&dn->rb, parent, p);
+       rb_insert_color(&dn->rb, &file->data_nodes);
+
+       return 0;
+}
+
+/**
+ * update_file - update file information.
+ * @c: UBIFS file-system description object
+ * @file: file object
+ * @sn: scanned node
+ * @key_type: type of @sn
+ *
+ * Update inode/dent/truncation/data node information of @file. Returns
+ * zero in case of success, a negative error code in case of failure.
+ */
+static int update_file(struct ubifs_info *c, struct scanned_file *file,
+                      struct scanned_node *sn, int key_type)
+{
+       int err = 0;
+
+       switch (key_type) {
+       case UBIFS_INO_KEY:
+       {
+               struct scanned_ino_node *o_ino, *n_ino;
+
+               o_ino = &file->ino;
+               n_ino = (struct scanned_ino_node *)sn;
+               if (o_ino->header.exist && o_ino->header.sqnum > sn->sqnum)
+                       goto out;
+
+               *o_ino = *n_ino;
+               break;
+       }
+       case UBIFS_DENT_KEY:
+       case UBIFS_XENT_KEY:
+       {
+               struct scanned_dent_node *dent = (struct scanned_dent_node *)sn;
+
+               err = insert_file_dentry(file, dent);
+               break;
+       }
+       case UBIFS_DATA_KEY:
+       {
+               struct scanned_data_node *dn = (struct scanned_data_node *)sn;
+
+               err = update_file_data(c, file, dn);
+               break;
+       }
+       case UBIFS_TRUN_KEY:
+       {
+               struct scanned_trun_node *o_trun, *n_trun;
+
+               o_trun = &file->trun;
+               n_trun = (struct scanned_trun_node *)sn;
+               if (o_trun->header.exist && o_trun->header.sqnum > sn->sqnum)
+                       goto out;
+
+               *o_trun = *n_trun;
+               break;
+       }
+       default:
+               err = -EINVAL;
+               log_err(c, 0, "unknown key type %d", key_type);
+       }
+
+out:
+       return err;
+}
+
+/**
+ * insert_or_update_file - insert or update file according to scanned node.
+ * @c: UBIFS file-system description object
+ * @file_tree: tree of all scanned files
+ * @sn: scanned node
+ * @key_type: key type of @sn
+ * @inum: inode number
+ *
+ * According to @sn, this function inserts file into the tree, or updates
+ * file information if it already exists in the tree. Returns zero in case
+ * of success, a negative error code in case of failure.
+ */
+int insert_or_update_file(struct ubifs_info *c, struct rb_root *file_tree,
+                         struct scanned_node *sn, int key_type, ino_t inum)
+{
+       int err;
+       struct scanned_file *file, *old_file = NULL;
+       struct rb_node **p, *parent = NULL;
+
+       p = &file_tree->rb_node;
+       while (*p) {
+               parent = *p;
+               file = rb_entry(parent, struct scanned_file, rb);
+               if (inum < file->inum) {
+                       p = &(*p)->rb_left;
+               } else if (inum > file->inum) {
+                       p = &(*p)->rb_right;
+               } else {
+                       old_file = file;
+                       break;
+               }
+       }
+       if (old_file)
+               return update_file(c, old_file, sn, key_type);
+
+       file = kzalloc(sizeof(struct scanned_file), GFP_KERNEL);
+       if (!file)
+               return -ENOMEM;
+
+       file->inum = inum;
+       file->dent_nodes = RB_ROOT;
+       file->data_nodes = RB_ROOT;
+       file->xattr_files = RB_ROOT;
+       INIT_LIST_HEAD(&file->list);
+       err = update_file(c, file, sn, key_type);
+       if (err) {
+               kfree(file);
+               return err;
+       }
+       rb_link_node(&file->rb, parent, p);
+       rb_insert_color(&file->rb, file_tree);
+
+       return 0;
+}
+
+/**
+ * destroy_file_content - destroy scanned data/dentry nodes in give file.
+ * @c: UBIFS file-system description object
+ * @file: file object
+ *
+ * Destroy all data/dentry nodes and xattrs attached to @file.
+ */
+void destroy_file_content(struct ubifs_info *c, struct scanned_file *file)
+{
+       struct scanned_data_node *data_node;
+       struct scanned_dent_node *dent_node;
+       struct scanned_file *xattr_file;
+       struct rb_node *this;
+
+       this = rb_first(&file->data_nodes);
+       while (this) {
+               data_node = rb_entry(this, struct scanned_data_node, rb);
+               this = rb_next(this);
+
+               rb_erase(&data_node->rb, &file->data_nodes);
+               kfree(data_node);
+       }
+
+       this = rb_first(&file->dent_nodes);
+       while (this) {
+               dent_node = rb_entry(this, struct scanned_dent_node, rb);
+               this = rb_next(this);
+
+               rb_erase(&dent_node->rb, &file->dent_nodes);
+               kfree(dent_node);
+       }
+
+       this = rb_first(&file->xattr_files);
+       while (this) {
+               xattr_file = rb_entry(this, struct scanned_file, rb);
+               this = rb_next(this);
+
+               ubifs_assert(c, !rb_first(&xattr_file->xattr_files));
+               destroy_file_content(c, xattr_file);
+               rb_erase(&xattr_file->rb, &file->xattr_files);
+               kfree(xattr_file);
+       }
+}
+
+/**
+ * destroy_file_tree - destroy files from a given tree.
+ * @c: UBIFS file-system description object
+ * @file_tree: tree of all scanned files
+ *
+ * Destroy scanned files from a given tree.
+ */
+void destroy_file_tree(struct ubifs_info *c, struct rb_root *file_tree)
+{
+       struct scanned_file *file;
+       struct rb_node *this;
+
+       this = rb_first(file_tree);
+       while (this) {
+               file = rb_entry(this, struct scanned_file, rb);
+               this = rb_next(this);
+
+               destroy_file_content(c, file);
+
+               rb_erase(&file->rb, file_tree);
+               kfree(file);
+       }
+}
index d77ebc61c4882d41eea784b000223a9c0a42362a..aad18d4cf33348d9b2f76508a419e2cc1d53be39 100644 (file)
@@ -135,6 +135,45 @@ struct scanned_trun_node {
        unsigned long long new_size;
 };
 
+/**
+ * scanned_file - file info scanned from UBIFS volume.
+ *
+ * @calc_nlink: calculated count of directory entries refer this inode
+ * @calc_xcnt: calculated count of extended attributes
+ * @calc_xsz: calculated summary size of all extended attributes
+ * @calc_xnms: calculated sum of lengths of all extended attribute names
+ * @calc_size: calculated file size
+ * @has_encrypted_info: whether the file has encryption related xattrs
+ *
+ * @inum: inode number
+ * @ino: inode node
+ * @trun: truncation node
+ *
+ * @rb: link in the tree of all scanned files
+ * @list: link in the list files for kinds of processing
+ * @dent_nodes: tree of all scanned dentry nodes
+ * @data_nodes: tree of all scanned data nodes
+ * @xattr_files: tree of all scanned xattr files
+ */
+struct scanned_file {
+       unsigned int calc_nlink;
+       unsigned int calc_xcnt;
+       unsigned int calc_xsz;
+       unsigned int calc_xnms;
+       unsigned long long calc_size;
+       bool has_encrypted_info;
+
+       ino_t inum;
+       struct scanned_ino_node ino;
+       struct scanned_trun_node trun;
+
+       struct rb_node rb;
+       struct list_head list;
+       struct rb_root dent_nodes;
+       struct rb_root data_nodes;
+       struct rb_root xattr_files;
+};
+
 /**
  * struct ubifs_fsck_info - UBIFS fsck information.
  * @mode: working mode
@@ -207,5 +246,9 @@ bool parse_data_node(struct ubifs_info *c, int lnum, int offs, void *node,
                     union ubifs_key *key, struct scanned_data_node *data_node);
 bool parse_trun_node(struct ubifs_info *c, int lnum, int offs, void *node,
                     union ubifs_key *key, struct scanned_trun_node *trun_node);
+int insert_or_update_file(struct ubifs_info *c, struct rb_root *file_tree,
+                         struct scanned_node *sn, int key_type, ino_t inum);
+void destroy_file_content(struct ubifs_info *c, struct scanned_file *file);
+void destroy_file_tree(struct ubifs_info *c, struct rb_root *file_tree);
 
 #endif