/** * dbg_check_old_index - check the old copy of the index. * @c: UBIFS file-system description object * @zroot: root of the new index * * In order to be able to recover from an unclean unmount, a complete copy of * the index must exist on flash. This is the "old" index. The commit process * must write the "new" index to flash without overwriting or destroying any * part of the old index. This function is run at commit end in order to check * that the old index does indeed exist completely intact. * * This function returns %0 on success and a negative error code on failure. */ int dbg_check_old_index(struct ubifs_info *c, struct ubifs_zbranch *zroot) { int lnum, offs, len, err = 0, uninitialized_var(last_level), child_cnt; int first = 1, iip; struct ubifs_debug_info *d = c->dbg; union ubifs_key lower_key, upper_key, l_key, u_key; unsigned long long uninitialized_var(last_sqnum); struct ubifs_idx_node *idx; struct list_head list; struct idx_node *i; size_t sz; if (!(ubifs_chk_flags & UBIFS_CHK_OLD_IDX)) goto out; INIT_LIST_HEAD(&list); sz = sizeof(struct idx_node) + ubifs_idx_node_sz(c, c->fanout) - UBIFS_IDX_NODE_SZ; /* Start at the old zroot */ lnum = d->old_zroot.lnum; offs = d->old_zroot.offs; len = d->old_zroot.len; iip = 0; /* * Traverse the index tree preorder depth-first i.e. do a node and then * its subtrees from left to right. */ while (1) { struct ubifs_branch *br; /* Get the next index node */ i = kmalloc(sz, GFP_NOFS); if (!i) { err = -ENOMEM; goto out_free; } i->iip = iip; /* Keep the index nodes on our path in a linked list */ list_add_tail(&i->list, &list); /* Read the index node */ idx = &i->idx; err = ubifs_read_node(c, idx, UBIFS_IDX_NODE, len, lnum, offs); if (err) goto out_free; /* Validate index node */ child_cnt = le16_to_cpu(idx->child_cnt); if (child_cnt < 1 || child_cnt > c->fanout) { err = 1; goto out_dump; } if (first) { first = 0; /* Check root level and sqnum */ if (le16_to_cpu(idx->level) != d->old_zroot_level) { err = 2; goto out_dump; } if (le64_to_cpu(idx->ch.sqnum) != d->old_zroot_sqnum) { err = 3; goto out_dump; } /* Set last values as though root had a parent */ last_level = le16_to_cpu(idx->level) + 1; last_sqnum = le64_to_cpu(idx->ch.sqnum) + 1; key_read(c, ubifs_idx_key(c, idx), &lower_key); highest_ino_key(c, &upper_key, INUM_WATERMARK); } key_copy(c, &upper_key, &i->upper_key); if (le16_to_cpu(idx->level) != last_level - 1) { err = 3; goto out_dump; } /* * The index is always written bottom up hence a child's sqnum * is always less than the parents. */ if (le64_to_cpu(idx->ch.sqnum) >= last_sqnum) { err = 4; goto out_dump; } /* Check key range */ key_read(c, ubifs_idx_key(c, idx), &l_key); br = ubifs_idx_branch(c, idx, child_cnt - 1); key_read(c, &br->key, &u_key); if (keys_cmp(c, &lower_key, &l_key) > 0) { err = 5; goto out_dump; } if (keys_cmp(c, &upper_key, &u_key) < 0) { err = 6; goto out_dump; } if (keys_cmp(c, &upper_key, &u_key) == 0) if (!is_hash_key(c, &u_key)) { err = 7; goto out_dump; } /* Go to next index node */ if (le16_to_cpu(idx->level) == 0) { /* At the bottom, so go up until can go right */ while (1) { /* Drop the bottom of the list */ list_del(&i->list); kfree(i); /* No more list means we are done */ if (list_empty(&list)) goto out; /* Look at the new bottom */ i = list_entry(list.prev, struct idx_node, list); idx = &i->idx; /* Can we go right */ if (iip + 1 < le16_to_cpu(idx->child_cnt)) { iip = iip + 1; break; } else /* Nope, so go up again */ iip = i->iip; } } else /* Go down left */ iip = 0; /* * We have the parent in 'idx' and now we set up for reading the * child pointed to by slot 'iip'. */ last_level = le16_to_cpu(idx->level); last_sqnum = le64_to_cpu(idx->ch.sqnum); br = ubifs_idx_branch(c, idx, iip); lnum = le32_to_cpu(br->lnum); offs = le32_to_cpu(br->offs); len = le32_to_cpu(br->len); key_read(c, &br->key, &lower_key); if (iip + 1 < le16_to_cpu(idx->child_cnt)) { br = ubifs_idx_branch(c, idx, iip + 1); key_read(c, &br->key, &upper_key); } else key_copy(c, &i->upper_key, &upper_key); }
int ubifs_jrn_delete_xattr(struct ubifs_info *c, const struct inode *host, const struct inode *inode, const struct qstr *nm, int sync) { int err, xlen, hlen, len, lnum, xent_offs, aligned_xlen; struct ubifs_dent_node *xent; struct ubifs_ino_node *ino; union ubifs_key xent_key, key1, key2; dbg_jrn("host %lu, xattr ino %lu, name '%s', data len %d", host->i_ino, inode->i_ino, nm->name, ubifs_inode(inode)->data_len); ubifs_assert(inode->i_nlink == 0); /* * Since we are deleting the inode, we do not bother to attach any data * to it and assume its length is %UBIFS_INO_NODE_SZ. */ xlen = UBIFS_DENT_NODE_SZ + nm->len + 1; aligned_xlen = ALIGN(xlen, 8); hlen = ubifs_inode(host)->data_len + UBIFS_INO_NODE_SZ; len = aligned_xlen + UBIFS_INO_NODE_SZ + ALIGN(hlen, 8); xent = kmalloc(len, GFP_NOFS); if (!xent) return -ENOMEM; xent->ch.node_type = UBIFS_XENT_NODE; xent_key_init(c, &xent_key, host->i_ino, nm); key_write(c, &xent_key, xent->key); xent->inum = 0; xent->type = get_dent_type(inode->i_mode); xent->nlen = cpu_to_le16(nm->len); memcpy(xent->name, nm->name, nm->len); xent->name[nm->len] = '\0'; zero_dent_node_unused(xent); ubifs_prep_grp_node(c, xent, xlen, 0); ino = (void *)xent + aligned_xlen; pack_inode(c, ino, inode, 0, 1); ino = (void *)ino + UBIFS_INO_NODE_SZ; pack_inode(c, ino, host, 1, 0); err = make_reservation(c, BASEHD, len); if (err) { kfree(xent); return err; } err = write_head(c, BASEHD, xent, len, &lnum, &xent_offs, sync); if (!sync && !err) ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf, host->i_ino); release_head(c, BASEHD); kfree(xent); if (err) goto out_ro; /* Remove the extended attribute entry from TNC */ err = ubifs_tnc_remove_nm(c, &xent_key, nm); if (err) goto out_ro; err = ubifs_add_dirt(c, lnum, xlen); if (err) goto out_ro; /* * Remove all nodes belonging to the extended attribute inode from TNC. * Well, there actually must be only one node - the inode itself. */ lowest_ino_key(c, &key1, inode->i_ino); highest_ino_key(c, &key2, inode->i_ino); err = ubifs_tnc_remove_range(c, &key1, &key2); if (err) goto out_ro; err = ubifs_add_dirt(c, lnum, UBIFS_INO_NODE_SZ); if (err) goto out_ro; /* And update TNC with the new host inode position */ ino_key_init(c, &key1, host->i_ino); err = ubifs_tnc_add(c, &key1, lnum, xent_offs + len - hlen, hlen); if (err) goto out_ro; finish_reservation(c); return 0; out_ro: ubifs_ro_mode(c, err); finish_reservation(c); return err; }