diff options
Diffstat (limited to 'ubifs-utils')
-rw-r--r-- | ubifs-utils/fsck.ubifs/rebuild_fs.c | 283 |
1 files changed, 262 insertions, 21 deletions
diff --git a/ubifs-utils/fsck.ubifs/rebuild_fs.c b/ubifs-utils/fsck.ubifs/rebuild_fs.c index 62bd412..e1d1957 100644 --- a/ubifs-utils/fsck.ubifs/rebuild_fs.c +++ b/ubifs-utils/fsck.ubifs/rebuild_fs.c @@ -34,6 +34,29 @@ struct scanned_info { struct rb_root del_dents; }; +/** + * struct idx_entry - index entry. + * @list: link in the list index entries for building index tree + * @key: key + * @name: directory entry name used for sorting colliding keys by name + * @lnum: LEB number + * @offs: offset + * @len: length + * + * The index is recorded as a linked list which is sorted and used to create + * the bottom level of the on-flash index tree. The remaining levels of the + * index tree are each built from the level below. + */ +struct idx_entry { + struct list_head list; + union ubifs_key key; + char *name; + int name_len; + int lnum; + int offs; + int len; +}; + static int init_rebuild_info(struct ubifs_info *c) { int err; @@ -146,6 +169,17 @@ static int insert_or_update_ino_node(struct ubifs_info *c, return 0; } +static int namecmp(const char *a, int la, const char *b, int lb) +{ + int cmp, len = min(la, lb); + + cmp = memcmp(a, b, len); + if (cmp) + return cmp; + + return la - lb; +} + /** * insert_or_update_dent_node - insert or update dentry node. * @c: UBIFS file-system description object @@ -160,7 +194,7 @@ static int insert_or_update_dent_node(struct ubifs_info *c, struct scanned_dent_node *new_dent, struct rb_root *tree) { - int cmp, nlen; + int cmp; struct scanned_dent_node *dent_node, *old_dent_node = NULL; struct rb_node **p, *parent = NULL; @@ -174,9 +208,8 @@ static int insert_or_update_dent_node(struct ubifs_info *c, } else if (cmp > 0) { p = &(*p)->rb_right; } else { - nlen = min(new_dent->nlen, dent_node->nlen); - cmp = strncmp(new_dent->name, dent_node->name, nlen) ? : - new_dent->nlen - dent_node->nlen; + cmp = namecmp(new_dent->name, new_dent->nlen, + dent_node->name, dent_node->nlen); if (cmp < 0) { p = &(*p)->rb_left; } else if (cmp > 0) { @@ -424,7 +457,7 @@ static struct scanned_dent_node * lookup_valid_dent_node(struct ubifs_info *c, struct scanned_info *si, struct scanned_dent_node *target) { - int cmp, nlen; + int cmp; struct scanned_dent_node *dent_node; struct rb_node *p; @@ -437,9 +470,8 @@ lookup_valid_dent_node(struct ubifs_info *c, struct scanned_info *si, } else if (cmp > 0) { p = p->rb_right; } else { - nlen = min(target->nlen, dent_node->nlen); - cmp = strncmp(target->name, dent_node->name, nlen) ? : - target->nlen - dent_node->nlen; + cmp = namecmp(target->name, target->nlen, + dent_node->name, dent_node->nlen); if (cmp < 0) { p = p->rb_left; } else if (cmp > 0) { @@ -885,9 +917,12 @@ static const char *get_file_name(struct ubifs_info *c, struct scanned_file *file return name; } -static void parse_node_location(struct ubifs_info *c, struct scanned_node *sn) +static int parse_node_info(struct ubifs_info *c, struct scanned_node *sn, + union ubifs_key *key, char *name, int name_len, + struct list_head *idx_list, int *idx_cnt) { int lnum, pos; + struct idx_entry *e; lnum = sn->lnum - c->main_first; pos = sn->offs + ALIGN(sn->len, 8); @@ -895,11 +930,184 @@ static void parse_node_location(struct ubifs_info *c, struct scanned_node *sn) set_bit(lnum, FSCK(c)->rebuild->used_lebs); FSCK(c)->rebuild->lpts[lnum].end = max_t(int, FSCK(c)->rebuild->lpts[lnum].end, pos); + + if (idx_cnt == NULL) + /* Skip truncation node. */ + return 0; + + e = kmalloc(sizeof(struct idx_entry), GFP_KERNEL); + if (!e) + return -ENOMEM; + + key_copy(c, key, &e->key); + e->name = name; + e->name_len = name_len; + e->lnum = sn->lnum; + e->offs = sn->offs; + e->len = sn->len; + list_add_tail(&e->list, idx_list); + *idx_cnt = *idx_cnt + 1; + + return 0; +} + +static int add_idx_node(struct ubifs_info *c, struct ubifs_idx_node *idx, + union ubifs_key *key, int child_cnt, + struct idx_entry *e) +{ + int err, lnum, offs, len; + + len = ubifs_idx_node_sz(c, child_cnt); + ubifs_prepare_node(c, idx, len, 0); + + err = reserve_space(c, len, &lnum, &offs); + if (err) + return err; + + copy_node_data(c, idx, offs, len); + + c->calc_idx_sz += ALIGN(len, 8); + + /* The last index node written will be the root */ + c->zroot.lnum = lnum; + c->zroot.offs = offs; + c->zroot.len = len; + + key_copy(c, key, &e->key); + e->lnum = lnum; + e->offs = offs; + e->len = len; + + return err; +} + +static int cmp_idx(void *priv, const struct list_head *a, + const struct list_head *b) +{ + int cmp; + struct ubifs_info *c = priv; + struct idx_entry *ia, *ib; + + if (a == b) + return 0; + + ia = list_entry(a, struct idx_entry, list); + ib = list_entry(b, struct idx_entry, list); + + cmp = keys_cmp(c, &ia->key, &ib->key); + if (cmp) + return cmp; + + return namecmp(ia->name, ia->name_len, ib->name, ib->name_len); +} + +/** + * build_tnc - construct TNC and write it into flash. + * @c: UBIFS file-system description object + * @lower_idxs: leaf entries of TNC + * @lower_cnt: the count of @lower_idxs + * + * This function builds TNC according to @lower_idxs and writes all index + * nodes into flash. + */ +static int build_tnc(struct ubifs_info *c, struct list_head *lower_idxs, + int lower_cnt) +{ + int i, j, err, upper_cnt, child_cnt, idx_sz, level = 0; + struct idx_entry *pe, *tmp_e, *e = NULL; + struct ubifs_idx_node *idx; + struct ubifs_branch *br; + union ubifs_key key; + LIST_HEAD(upper_idxs); + + idx_sz = ubifs_idx_node_sz(c, c->fanout); + idx = kmalloc(idx_sz, GFP_KERNEL); + if (!idx) + return -ENOMEM; + + list_sort(c, lower_idxs, cmp_idx); + + ubifs_assert(c, lower_cnt != 0); + + do { + upper_cnt = lower_cnt / c->fanout; + if (lower_cnt % c->fanout) + upper_cnt += 1; + e = list_first_entry(lower_idxs, struct idx_entry, list); + + for (i = 0; i < upper_cnt; i++) { + if (i == upper_cnt - 1) { + child_cnt = lower_cnt % c->fanout; + if (child_cnt == 0) + child_cnt = c->fanout; + } else + child_cnt = c->fanout; + + key_copy(c, &e->key, &key); + memset(idx, 0, idx_sz); + idx->ch.node_type = UBIFS_IDX_NODE; + idx->child_cnt = cpu_to_le16(child_cnt); + idx->level = cpu_to_le16(level); + for (j = 0; j < child_cnt; j++) { + ubifs_assert(c, + !list_entry_is_head(e, lower_idxs, list)); + br = ubifs_idx_branch(c, idx, j); + key_write_idx(c, &e->key, &br->key); + br->lnum = cpu_to_le32(e->lnum); + br->offs = cpu_to_le32(e->offs); + br->len = cpu_to_le32(e->len); + e = list_next_entry(e, list); + } + + pe = kmalloc(sizeof(struct idx_entry), GFP_KERNEL); + if (!pe) { + err = -ENOMEM; + goto out; + } + + err = add_idx_node(c, idx, &key, child_cnt, pe); + if (err) { + kfree(pe); + goto out; + } + + list_add_tail(&pe->list, &upper_idxs); + } + + level++; + list_for_each_entry_safe(e, tmp_e, lower_idxs, list) { + list_del(&e->list); + kfree(e); + } + list_splice_init(&upper_idxs, lower_idxs); + lower_cnt = upper_cnt; + } while (lower_cnt > 1); + + /* Set the index head */ + c->ihead_lnum = FSCK(c)->rebuild->head_lnum; + c->ihead_offs = ALIGN(FSCK(c)->rebuild->head_offs, c->min_io_size); + + /* Flush the last index LEB */ + err = flush_write_buf(c); + +out: + list_for_each_entry_safe(e, tmp_e, lower_idxs, list) { + list_del(&e->list); + kfree(e); + } + list_for_each_entry_safe(e, tmp_e, &upper_idxs, list) { + list_del(&e->list); + kfree(e); + } + kfree(idx); + return err; } -static void record_file_used_lebs(struct ubifs_info *c, - struct scanned_file *file) +static int record_file_used_lebs(struct ubifs_info *c, + struct scanned_file *file, + struct list_head *idx_list, int *idx_cnt) { + int err; struct rb_node *node; struct scanned_file *xattr_file; struct scanned_dent_node *dent_node; @@ -911,28 +1119,46 @@ static void record_file_used_lebs(struct ubifs_info *c, ubifs_get_type_name(ubifs_get_dent_type(file->ino.mode)), c->dev_name); - parse_node_location(c, &file->ino.header); + err = parse_node_info(c, &file->ino.header, &file->ino.key, + NULL, 0, idx_list, idx_cnt); + if (err) + return err; - if (file->trun.header.exist) - parse_node_location(c, &file->trun.header); + if (file->trun.header.exist) { + err = parse_node_info(c, &file->trun.header, NULL, NULL, + 0, idx_list, NULL); + if (err) + return err; + } for (node = rb_first(&file->data_nodes); node; node = rb_next(node)) { data_node = rb_entry(node, struct scanned_data_node, rb); - parse_node_location(c, &data_node->header); + err = parse_node_info(c, &data_node->header, &data_node->key, + NULL, 0, idx_list, idx_cnt); + if (err) + return err; } for (node = rb_first(&file->dent_nodes); node; node = rb_next(node)) { dent_node = rb_entry(node, struct scanned_dent_node, rb); - parse_node_location(c, &dent_node->header); + err = parse_node_info(c, &dent_node->header, &dent_node->key, + dent_node->name, dent_node->nlen, + idx_list, idx_cnt); + if (err) + return err; } for (node = rb_first(&file->xattr_files); node; node = rb_next(node)) { xattr_file = rb_entry(node, struct scanned_file, rb); - record_file_used_lebs(c, xattr_file); + err = record_file_used_lebs(c, xattr_file, idx_list, idx_cnt); + if (err) + return err; } + + return err; } /** @@ -946,13 +1172,16 @@ static void record_file_used_lebs(struct ubifs_info *c, * LEBs could be taken for storing new index tree. * 3. Re-write data to prevent failed gc scanning in the subsequent mounting * process caused by corrupted data. + * 4. Build TNC. */ static int traverse_files_and_nodes(struct ubifs_info *c) { - int i, err = 0; + int i, err = 0, idx_cnt = 0; struct rb_node *node; struct scanned_file *file; struct rb_root *tree = &FSCK(c)->rebuild->scanned_files; + struct idx_entry *ie, *tmp_ie; + LIST_HEAD(idx_list); if (rb_first(tree) == NULL) { /* No scanned files. Create root dir. */ @@ -966,7 +1195,9 @@ static int traverse_files_and_nodes(struct ubifs_info *c) for (node = rb_first(tree); node; node = rb_next(node)) { file = rb_entry(node, struct scanned_file, rb); - record_file_used_lebs(c, file); + err = record_file_used_lebs(c, file, &idx_list, &idx_cnt); + if (err) + goto out_idx_list; } /* Re-write data. */ @@ -985,16 +1216,25 @@ static int traverse_files_and_nodes(struct ubifs_info *c) err = ubifs_leb_read(c, lnum, c->sbuf, 0, len, 0); if (err && err != -EBADMSG) - return err; + goto out_idx_list; if (len > end) ubifs_pad(c, c->sbuf + end, len - end); err = ubifs_leb_change(c, lnum, c->sbuf, len); if (err) - return err; + goto out_idx_list; } + /* Build TNC. */ + log_out(c, "Build TNC"); + err = build_tnc(c, &idx_list, idx_cnt); + +out_idx_list: + list_for_each_entry_safe(ie, tmp_ie, &idx_list, list) { + list_del(&ie->list); + kfree(ie); + } return err; } @@ -1059,6 +1299,7 @@ int ubifs_rebuild_filesystem(struct ubifs_info *c) /* * Step 7: Record used LEBs. * Step 8: Re-write data to clean corrupted data. + * Step 9: Build TNC. */ err = traverse_files_and_nodes(c); if (err) |