summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Weinberger <richard@nod.at>2019-04-05 00:34:36 +0200
committerStefan Agner <stefan.agner@toradex.com>2019-04-09 18:11:24 +0200
commita3a0066bd9cef5c5c5bdfa5e1549cb5258529cc1 (patch)
treea4a49f85dcb1755dca6ceae1d09d38d6f6f6001c
parent6f01eb5bf8e8110ab5f3a8e7b0f3abf19a205e4b (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.c66
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);