Commit c43be108 authored by Artem Bityutskiy's avatar Artem Bityutskiy

UBIFS: do not use inc_link when i_nlink is zero

This patch changes the 'i_nlink' counter handling in 'ubifs_unlink()',
'ubifs_rmdir()' and 'ubifs_rename()'. In these function  'i_nlink' may become 0,
and if 'ubifs_jnl_update()' failed, we would use 'inc_nlink()' to restore
the previous 'i_nlink' value, which is incorrect from the VFS point of view and
would cause a 'WARN_ON()' (see 'inc_nlink() implementation).

This patches saves the previous 'i_nlink' value in a local variable and uses it
at the error path instead of calling 'inc_nlink()'. We do this only for the
inodes where 'i_nlink' may potentially become zero.

This change has been requested by Al Viro <viro@ZenIV.linux.org.uk>.
Signed-off-by: default avatarArtem Bityutskiy <artem.bityutskiy@linux.intel.com>
parent b06283c7
...@@ -566,6 +566,7 @@ static int ubifs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -566,6 +566,7 @@ static int ubifs_unlink(struct inode *dir, struct dentry *dentry)
int sz_change = CALC_DENT_SIZE(dentry->d_name.len); int sz_change = CALC_DENT_SIZE(dentry->d_name.len);
int err, budgeted = 1; int err, budgeted = 1;
struct ubifs_budget_req req = { .mod_dent = 1, .dirtied_ino = 2 }; struct ubifs_budget_req req = { .mod_dent = 1, .dirtied_ino = 2 };
unsigned int saved_nlink = inode->i_nlink;
/* /*
* Budget request settings: deletion direntry, deletion inode (+1 for * Budget request settings: deletion direntry, deletion inode (+1 for
...@@ -613,7 +614,7 @@ static int ubifs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -613,7 +614,7 @@ static int ubifs_unlink(struct inode *dir, struct dentry *dentry)
out_cancel: out_cancel:
dir->i_size += sz_change; dir->i_size += sz_change;
dir_ui->ui_size = dir->i_size; dir_ui->ui_size = dir->i_size;
inc_nlink(inode); set_nlink(inode, saved_nlink);
unlock_2_inodes(dir, inode); unlock_2_inodes(dir, inode);
if (budgeted) if (budgeted)
ubifs_release_budget(c, &req); ubifs_release_budget(c, &req);
...@@ -704,8 +705,7 @@ static int ubifs_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -704,8 +705,7 @@ static int ubifs_rmdir(struct inode *dir, struct dentry *dentry)
dir->i_size += sz_change; dir->i_size += sz_change;
dir_ui->ui_size = dir->i_size; dir_ui->ui_size = dir->i_size;
inc_nlink(dir); inc_nlink(dir);
inc_nlink(inode); set_nlink(inode, 2);
inc_nlink(inode);
unlock_2_inodes(dir, inode); unlock_2_inodes(dir, inode);
if (budgeted) if (budgeted)
ubifs_release_budget(c, &req); ubifs_release_budget(c, &req);
...@@ -977,6 +977,7 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -977,6 +977,7 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct ubifs_budget_req ino_req = { .dirtied_ino = 1, struct ubifs_budget_req ino_req = { .dirtied_ino = 1,
.dirtied_ino_d = ALIGN(old_inode_ui->data_len, 8) }; .dirtied_ino_d = ALIGN(old_inode_ui->data_len, 8) };
struct timespec time; struct timespec time;
unsigned int saved_nlink;
/* /*
* Budget request settings: deletion direntry, new direntry, removing * Budget request settings: deletion direntry, new direntry, removing
...@@ -1059,13 +1060,14 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1059,13 +1060,14 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (unlink) { if (unlink) {
/* /*
* Directories cannot have hard-links, so if this is a * Directories cannot have hard-links, so if this is a
* directory, decrement its @i_nlink twice because an empty * directory, just clear @i_nlink.
* directory has @i_nlink 2.
*/ */
saved_nlink = new_inode->i_nlink;
if (is_dir) if (is_dir)
clear_nlink(new_inode);
else
drop_nlink(new_inode); drop_nlink(new_inode);
new_inode->i_ctime = time; new_inode->i_ctime = time;
drop_nlink(new_inode);
} else { } else {
new_dir->i_size += new_sz; new_dir->i_size += new_sz;
ubifs_inode(new_dir)->ui_size = new_dir->i_size; ubifs_inode(new_dir)->ui_size = new_dir->i_size;
...@@ -1102,9 +1104,7 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1102,9 +1104,7 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry,
out_cancel: out_cancel:
if (unlink) { if (unlink) {
if (is_dir) set_nlink(new_inode, saved_nlink);
inc_nlink(new_inode);
inc_nlink(new_inode);
} else { } else {
new_dir->i_size -= new_sz; new_dir->i_size -= new_sz;
ubifs_inode(new_dir)->ui_size = new_dir->i_size; ubifs_inode(new_dir)->ui_size = new_dir->i_size;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment