Skip to content

Commit b25e6a5

Browse files
Zhihao Chengrichardweinberger
authored andcommitted
ubifs: Fix space leak when powercut happens in linking tmpfile
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: 32fe905 ("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>
1 parent 9f5ecac commit b25e6a5

File tree

4 files changed

+15
-17
lines changed

4 files changed

+15
-17
lines changed

fs/ubifs/dir.c

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ static int ubifs_create(struct mnt_idmap *idmap, struct inode *dir,
325325
dir_ui->ui_size = dir->i_size;
326326
inode_set_mtime_to_ts(dir,
327327
inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
328-
err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0);
328+
err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0, 0);
329329
if (err)
330330
goto out_cancel;
331331
mutex_unlock(&dir_ui->ui_mutex);
@@ -479,7 +479,7 @@ static int ubifs_tmpfile(struct mnt_idmap *idmap, struct inode *dir,
479479
mutex_unlock(&ui->ui_mutex);
480480

481481
lock_2_inodes(dir, inode);
482-
err = ubifs_jnl_update(c, dir, &nm, inode, 1, 0);
482+
err = ubifs_jnl_update(c, dir, &nm, inode, 1, 0, 0);
483483
if (err)
484484
goto out_cancel;
485485
unlock_2_inodes(dir, inode);
@@ -760,18 +760,14 @@ static int ubifs_link(struct dentry *old_dentry, struct inode *dir,
760760

761761
lock_2_inodes(dir, inode);
762762

763-
/* Handle O_TMPFILE corner case, it is allowed to link a O_TMPFILE. */
764-
if (inode->i_nlink == 0)
765-
ubifs_delete_orphan(c, inode->i_ino);
766-
767763
inc_nlink(inode);
768764
ihold(inode);
769765
inode_set_ctime_current(inode);
770766
dir->i_size += sz_change;
771767
dir_ui->ui_size = dir->i_size;
772768
inode_set_mtime_to_ts(dir,
773769
inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
774-
err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0);
770+
err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0, inode->i_nlink == 1);
775771
if (err)
776772
goto out_cancel;
777773
unlock_2_inodes(dir, inode);
@@ -785,8 +781,6 @@ static int ubifs_link(struct dentry *old_dentry, struct inode *dir,
785781
dir->i_size -= sz_change;
786782
dir_ui->ui_size = dir->i_size;
787783
drop_nlink(inode);
788-
if (inode->i_nlink == 0)
789-
ubifs_add_orphan(c, inode->i_ino);
790784
unlock_2_inodes(dir, inode);
791785
ubifs_release_budget(c, &req);
792786
iput(inode);
@@ -846,7 +840,7 @@ static int ubifs_unlink(struct inode *dir, struct dentry *dentry)
846840
dir_ui->ui_size = dir->i_size;
847841
inode_set_mtime_to_ts(dir,
848842
inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
849-
err = ubifs_jnl_update(c, dir, &nm, inode, 1, 0);
843+
err = ubifs_jnl_update(c, dir, &nm, inode, 1, 0, 0);
850844
if (err)
851845
goto out_cancel;
852846
unlock_2_inodes(dir, inode);
@@ -950,7 +944,7 @@ static int ubifs_rmdir(struct inode *dir, struct dentry *dentry)
950944
dir_ui->ui_size = dir->i_size;
951945
inode_set_mtime_to_ts(dir,
952946
inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
953-
err = ubifs_jnl_update(c, dir, &nm, inode, 1, 0);
947+
err = ubifs_jnl_update(c, dir, &nm, inode, 1, 0, 0);
954948
if (err)
955949
goto out_cancel;
956950
unlock_2_inodes(dir, inode);
@@ -1025,7 +1019,7 @@ static int ubifs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
10251019
dir_ui->ui_size = dir->i_size;
10261020
inode_set_mtime_to_ts(dir,
10271021
inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
1028-
err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0);
1022+
err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0, 0);
10291023
if (err) {
10301024
ubifs_err(c, "cannot create directory, error %d", err);
10311025
goto out_cancel;
@@ -1119,7 +1113,7 @@ static int ubifs_mknod(struct mnt_idmap *idmap, struct inode *dir,
11191113
dir_ui->ui_size = dir->i_size;
11201114
inode_set_mtime_to_ts(dir,
11211115
inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
1122-
err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0);
1116+
err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0, 0);
11231117
if (err)
11241118
goto out_cancel;
11251119
mutex_unlock(&dir_ui->ui_mutex);
@@ -1220,7 +1214,7 @@ static int ubifs_symlink(struct mnt_idmap *idmap, struct inode *dir,
12201214
dir_ui->ui_size = dir->i_size;
12211215
inode_set_mtime_to_ts(dir,
12221216
inode_set_ctime_to_ts(dir, inode_get_ctime(inode)));
1223-
err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0);
1217+
err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0, 0);
12241218
if (err)
12251219
goto out_cancel;
12261220
mutex_unlock(&dir_ui->ui_mutex);

fs/ubifs/journal.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,7 @@ static void set_dent_cookie(struct ubifs_info *c, struct ubifs_dent_node *dent)
643643
* @inode: inode to update
644644
* @deletion: indicates a directory entry deletion i.e unlink or rmdir
645645
* @xent: non-zero if the directory entry is an extended attribute entry
646+
* @delete_orphan: indicates an orphan entry deletion for @inode
646647
*
647648
* This function updates an inode by writing a directory entry (or extended
648649
* 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)
664665
*/
665666
int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
666667
const struct fscrypt_name *nm, const struct inode *inode,
667-
int deletion, int xent)
668+
int deletion, int xent, int delete_orphan)
668669
{
669670
int err, dlen, ilen, len, lnum, ino_offs, dent_offs, orphan_added = 0;
670671
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,
806807
if (err)
807808
goto out_ro;
808809

810+
if (delete_orphan)
811+
ubifs_delete_orphan(c, inode->i_ino);
812+
809813
finish_reservation(c);
810814
spin_lock(&ui->ui_lock);
811815
ui->synced_i_size = ui->ui_size;

fs/ubifs/ubifs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1800,7 +1800,7 @@ int ubifs_consolidate_log(struct ubifs_info *c);
18001800
/* journal.c */
18011801
int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
18021802
const struct fscrypt_name *nm, const struct inode *inode,
1803-
int deletion, int xent);
1803+
int deletion, int xent, int delete_orphan);
18041804
int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
18051805
const union ubifs_key *key, const void *buf, int len);
18061806
int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode);

fs/ubifs/xattr.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ static int create_xattr(struct ubifs_info *c, struct inode *host,
149149
if (strcmp(fname_name(nm), UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT) == 0)
150150
host_ui->flags |= UBIFS_CRYPT_FL;
151151

152-
err = ubifs_jnl_update(c, host, nm, inode, 0, 1);
152+
err = ubifs_jnl_update(c, host, nm, inode, 0, 1, 0);
153153
if (err)
154154
goto out_cancel;
155155
ubifs_set_inode_flags(host);

0 commit comments

Comments
 (0)