]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
ubifs: Fix space leak when powercut happens in linking tmpfile
authorZhihao Cheng <chengzhihao1@huawei.com>
Wed, 10 Apr 2024 07:37:49 +0000 (15:37 +0800)
committerRichard Weinberger <richard@nod.at>
Fri, 12 Jul 2024 19:40:47 +0000 (21:40 +0200)
There is a potential space leak problem when powercut happens in linking
tmpfile, in which case, inode node (with nlink=0) and its' data nodes can
be found from tnc (on flash), but there are no dentries related to the
inode, so the file is invisible but takes free space. Detailed process is
shown as:
 ubifs_tmpfile
  ubifs_jnl_update // Add bud A into log area
   ubifs_add_orphan // Add inode into orphan list

     P1             P2
 ubifs_link
  ubifs_delete_orphan // Delete inode from orphan list, then inode won't
      // be written into orphan area, there is no chance
      // to delete inode by replaying orphan.
                commit // bud A won't be replayed in next mounting
   >> powercut <<
  ubifs_jnl_update // Link inode to dentry

The root cause is that orphan entry deletion and journal writing(for link)
are interrupted by commit, which makes the two operations are not atomic.
Fix it by doing ubifs_delete_orphan under the protection of c->commit_sem
within ubifs_jnl_update. This is also a preparation to support all creating
new files by orphan inode.

v1 is https://lore.kernel.org/linux-mtd/20200701093227.674945-1-chengzhihao1@huawei.com/

Fixes: 32fe905c17f0 ("ubifs: Fix O_TMPFILE corner case in ubifs_link()")
Link: https://bugzilla.kernel.org/show_bug.cgi?id=208405
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
fs/ubifs/dir.c
fs/ubifs/journal.c
fs/ubifs/ubifs.h
fs/ubifs/xattr.c

index 848988f2b7dcfb880da6782344f1780ccd182823..fe16443243ab89c8f173e93b74fb374ddaf5e106 100644 (file)
@@ -325,7 +325,7 @@ static int ubifs_create(struct mnt_idmap *idmap, struct inode *dir,
        dir_ui->ui_size = dir->i_size;
        inode_set_mtime_to_ts(dir,
                              inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
-       err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0);
+       err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0, 0);
        if (err)
                goto out_cancel;
        mutex_unlock(&dir_ui->ui_mutex);
@@ -479,7 +479,7 @@ static int ubifs_tmpfile(struct mnt_idmap *idmap, struct inode *dir,
        mutex_unlock(&ui->ui_mutex);
 
        lock_2_inodes(dir, inode);
-       err = ubifs_jnl_update(c, dir, &nm, inode, 1, 0);
+       err = ubifs_jnl_update(c, dir, &nm, inode, 1, 0, 0);
        if (err)
                goto out_cancel;
        unlock_2_inodes(dir, inode);
@@ -760,10 +760,6 @@ static int ubifs_link(struct dentry *old_dentry, struct inode *dir,
 
        lock_2_inodes(dir, inode);
 
-       /* Handle O_TMPFILE corner case, it is allowed to link a O_TMPFILE. */
-       if (inode->i_nlink == 0)
-               ubifs_delete_orphan(c, inode->i_ino);
-
        inc_nlink(inode);
        ihold(inode);
        inode_set_ctime_current(inode);
@@ -771,7 +767,7 @@ static int ubifs_link(struct dentry *old_dentry, struct inode *dir,
        dir_ui->ui_size = dir->i_size;
        inode_set_mtime_to_ts(dir,
                              inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
-       err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0);
+       err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0, inode->i_nlink == 1);
        if (err)
                goto out_cancel;
        unlock_2_inodes(dir, inode);
@@ -785,8 +781,6 @@ out_cancel:
        dir->i_size -= sz_change;
        dir_ui->ui_size = dir->i_size;
        drop_nlink(inode);
-       if (inode->i_nlink == 0)
-               ubifs_add_orphan(c, inode->i_ino);
        unlock_2_inodes(dir, inode);
        ubifs_release_budget(c, &req);
        iput(inode);
@@ -846,7 +840,7 @@ static int ubifs_unlink(struct inode *dir, struct dentry *dentry)
        dir_ui->ui_size = dir->i_size;
        inode_set_mtime_to_ts(dir,
                              inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
-       err = ubifs_jnl_update(c, dir, &nm, inode, 1, 0);
+       err = ubifs_jnl_update(c, dir, &nm, inode, 1, 0, 0);
        if (err)
                goto out_cancel;
        unlock_2_inodes(dir, inode);
@@ -950,7 +944,7 @@ static int ubifs_rmdir(struct inode *dir, struct dentry *dentry)
        dir_ui->ui_size = dir->i_size;
        inode_set_mtime_to_ts(dir,
                              inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
-       err = ubifs_jnl_update(c, dir, &nm, inode, 1, 0);
+       err = ubifs_jnl_update(c, dir, &nm, inode, 1, 0, 0);
        if (err)
                goto out_cancel;
        unlock_2_inodes(dir, inode);
@@ -1025,7 +1019,7 @@ static int ubifs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
        dir_ui->ui_size = dir->i_size;
        inode_set_mtime_to_ts(dir,
                              inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
-       err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0);
+       err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0, 0);
        if (err) {
                ubifs_err(c, "cannot create directory, error %d", err);
                goto out_cancel;
@@ -1119,7 +1113,7 @@ static int ubifs_mknod(struct mnt_idmap *idmap, struct inode *dir,
        dir_ui->ui_size = dir->i_size;
        inode_set_mtime_to_ts(dir,
                              inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
-       err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0);
+       err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0, 0);
        if (err)
                goto out_cancel;
        mutex_unlock(&dir_ui->ui_mutex);
@@ -1220,7 +1214,7 @@ static int ubifs_symlink(struct mnt_idmap *idmap, struct inode *dir,
        dir_ui->ui_size = dir->i_size;
        inode_set_mtime_to_ts(dir,
                              inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
-       err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0);
+       err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0, 0);
        if (err)
                goto out_cancel;
        mutex_unlock(&dir_ui->ui_mutex);
index 74aee92433d75c2fe0880e351519e00a85b1729a..2b4b05c2d9b2c63190981fe65676f3a6417fae8e 100644 (file)
@@ -643,6 +643,7 @@ static void set_dent_cookie(struct ubifs_info *c, struct ubifs_dent_node *dent)
  * @inode: inode to update
  * @deletion: indicates a directory entry deletion i.e unlink or rmdir
  * @xent: non-zero if the directory entry is an extended attribute entry
+ * @delete_orphan: indicates an orphan entry deletion for @inode
  *
  * This function updates an inode by writing a directory entry (or extended
  * attribute entry), the inode itself, and the parent directory inode (or the
@@ -664,7 +665,7 @@ static void set_dent_cookie(struct ubifs_info *c, struct ubifs_dent_node *dent)
  */
 int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
                     const struct fscrypt_name *nm, const struct inode *inode,
-                    int deletion, int xent)
+                    int deletion, int xent, int delete_orphan)
 {
        int err, dlen, ilen, len, lnum, ino_offs, dent_offs, orphan_added = 0;
        int aligned_dlen, aligned_ilen, sync = IS_DIRSYNC(dir);
@@ -806,6 +807,9 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
        if (err)
                goto out_ro;
 
+       if (delete_orphan)
+               ubifs_delete_orphan(c, inode->i_ino);
+
        finish_reservation(c);
        spin_lock(&ui->ui_lock);
        ui->synced_i_size = ui->ui_size;
index 821c6ae99e624f1e606ba2ffc0866789455cdbd9..d0ed78345617c82b5c77bf8656528e2d5077845b 100644 (file)
@@ -1800,7 +1800,7 @@ int ubifs_consolidate_log(struct ubifs_info *c);
 /* journal.c */
 int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
                     const struct fscrypt_name *nm, const struct inode *inode,
-                    int deletion, int xent);
+                    int deletion, int xent, int delete_orphan);
 int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
                         const union ubifs_key *key, const void *buf, int len);
 int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode);
index 0847db521984ac98a4de135e984c47ed703837d0..f734588b224a31838cc9830093603c4dff9c377a 100644 (file)
@@ -149,7 +149,7 @@ static int create_xattr(struct ubifs_info *c, struct inode *host,
        if (strcmp(fname_name(nm), UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT) == 0)
                host_ui->flags |= UBIFS_CRYPT_FL;
 
-       err = ubifs_jnl_update(c, host, nm, inode, 0, 1);
+       err = ubifs_jnl_update(c, host, nm, inode, 0, 1, 0);
        if (err)
                goto out_cancel;
        ubifs_set_inode_flags(host);