diff options
author | Richard Weinberger <richard@nod.at> | 2019-04-05 00:34:36 +0200 |
---|---|---|
committer | Stefan Agner <stefan.agner@toradex.com> | 2019-04-09 18:11:24 +0200 |
commit | a3a0066bd9cef5c5c5bdfa5e1549cb5258529cc1 (patch) | |
tree | a4a49f85dcb1755dca6ceae1d09d38d6f6f6001c | |
parent | 6f01eb5bf8e8110ab5f3a8e7b0f3abf19a205e4b (diff) |
ubifs: journal: Handle xattrs like files
If an inode hosts xattrs, create deletion entries for each
inode. That way we can make sure that upon journal replay UBIFS
can find find all xattr inodes.
Otherwise it can happen that GC consumed already a LEB which contained
parts of the TNC that pointed to the xattrs and we no longer
find all xattr inodes, which will confuse the LPT and cause
space allocation issues.
Reported-by: Stefan Agner <stefan@agner.ch>
Fixes: 1e51764a3c2ac ("UBIFS: add new flash file system")
Signed-off-by: Richard Weinberger <richard@nod.at>
[backport to Linux 4.4]
Signed-off-by: Stefan Agner <stefan.agner@toradex.com>
-rw-r--r-- | fs/ubifs/journal.c | 66 |
1 files changed, 54 insertions, 12 deletions
diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c index 539fa934ed93..bae0c6f432e5 100644 --- a/fs/ubifs/journal.c +++ b/fs/ubifs/journal.c @@ -789,9 +789,11 @@ out_free: int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode) { int err, lnum, offs; - struct ubifs_ino_node *ino; + struct ubifs_ino_node *ino, *ino_start; struct ubifs_inode *ui = ubifs_inode(inode); - int sync = 0, len = UBIFS_INO_NODE_SZ, last_reference = !inode->i_nlink; + int sync = 0, write_len = 0, ilen = UBIFS_INO_NODE_SZ; + int last_reference = !inode->i_nlink; + int kill_xattrs = ui->xattr_cnt && last_reference; dbg_jnl("ino %lu, nlink %u", inode->i_ino, inode->i_nlink); @@ -800,20 +802,60 @@ int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode) * need to synchronize the write-buffer either. */ if (!last_reference) { - len += ui->data_len; + ilen += ui->data_len; sync = IS_SYNC(inode); + } else if (kill_xattrs) { + write_len += UBIFS_INO_NODE_SZ * ui->xattr_cnt; } - ino = kmalloc(len, GFP_NOFS); + + write_len += ilen; + + ino_start = ino = kmalloc(write_len, GFP_NOFS); if (!ino) return -ENOMEM; /* Make reservation before allocating sequence numbers */ - err = make_reservation(c, BASEHD, len); + err = make_reservation(c, BASEHD, ilen); if (err) goto out_free; + if (kill_xattrs) { + union ubifs_key key; + struct qstr nm = { 0 }; + struct inode *xino; + struct ubifs_dent_node *xent, *pxent = NULL; + + lowest_xent_key(c, &key, inode->i_ino); + while (1) { + xent = ubifs_tnc_next_ent(c, &key, &nm); + if (IS_ERR(xent)) { + err = PTR_ERR(xent); + if (err == -ENOENT) + break; + + goto out_release; + } + + nm.name = xent->name; + nm.len = le16_to_cpu(xent->nlen); + + xino = ubifs_iget(c->vfs_sb, xent->inum); + ubifs_assert(ubifs_inode(xino)->xattr); + + clear_nlink(xino); + pack_inode(c, ino, xino, 0); + ino = (void *)ino + UBIFS_INO_NODE_SZ; + iput(xino); + + kfree(pxent); + pxent = xent; + key_read(c, &xent->key, &key); + } + kfree(pxent); + } + pack_inode(c, ino, inode, 1); - err = write_head(c, BASEHD, ino, len, &lnum, &offs, sync); + err = write_head(c, BASEHD, ino_start, write_len, &lnum, &offs, sync); if (err) goto out_release; if (!sync) @@ -826,12 +868,12 @@ int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode) if (err) goto out_ro; ubifs_delete_orphan(c, inode->i_ino); - err = ubifs_add_dirt(c, lnum, len); + err = ubifs_add_dirt(c, lnum, write_len); } else { union ubifs_key key; ino_key_init(c, &key, inode->i_ino); - err = ubifs_tnc_add(c, &key, lnum, offs, len); + err = ubifs_tnc_add(c, &key, lnum, offs, ilen); } if (err) goto out_ro; @@ -840,7 +882,7 @@ int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode) spin_lock(&ui->ui_lock); ui->synced_i_size = ui->ui_size; spin_unlock(&ui->ui_lock); - kfree(ino); + kfree(ino_start); return 0; out_release: @@ -849,7 +891,7 @@ out_ro: ubifs_ro_mode(c, err); finish_reservation(c); out_free: - kfree(ino); + kfree(ino_start); return err; } @@ -889,8 +931,8 @@ int ubifs_jnl_delete_inode(struct ubifs_info *c, const struct inode *inode) ubifs_assert(inode->i_nlink == 0); - if (ui->del_cmtno != c->cmt_no) - /* A commit happened for sure */ + if (ui->xattr_cnt || ui->del_cmtno != c->cmt_no) + /* A commit happened for sure or inode hosts xattrs */ return ubifs_jnl_write_inode(c, inode); down_read(&c->commit_sem); |