diff options
author | Artem Bityutskiy <artem.bityutskiy@linux.intel.com> | 2012-02-07 03:58:51 -0500 |
---|---|---|
committer | Artem Bityutskiy <artem.bityutskiy@linux.intel.com> | 2012-02-29 09:10:20 -0500 |
commit | c43be1085f8480ab36d5c8c76a08e67bdf6d2e18 (patch) | |
tree | 5d77b528c065e6d5c542c631634cd440ae3938ac /fs/ubifs | |
parent | b06283c7df35b5a49ab141ed38e0280821379096 (diff) |
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: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
Diffstat (limited to 'fs/ubifs')
-rw-r--r-- | fs/ubifs/dir.c | 18 |
1 files changed, 9 insertions, 9 deletions
diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c index d6fe1c79f18b..ec9f1870ab7f 100644 --- a/fs/ubifs/dir.c +++ b/fs/ubifs/dir.c | |||
@@ -566,6 +566,7 @@ static int ubifs_unlink(struct inode *dir, struct dentry *dentry) | |||
566 | int sz_change = CALC_DENT_SIZE(dentry->d_name.len); | 566 | int sz_change = CALC_DENT_SIZE(dentry->d_name.len); |
567 | int err, budgeted = 1; | 567 | int err, budgeted = 1; |
568 | struct ubifs_budget_req req = { .mod_dent = 1, .dirtied_ino = 2 }; | 568 | struct ubifs_budget_req req = { .mod_dent = 1, .dirtied_ino = 2 }; |
569 | unsigned int saved_nlink = inode->i_nlink; | ||
569 | 570 | ||
570 | /* | 571 | /* |
571 | * Budget request settings: deletion direntry, deletion inode (+1 for | 572 | * Budget request settings: deletion direntry, deletion inode (+1 for |
@@ -613,7 +614,7 @@ static int ubifs_unlink(struct inode *dir, struct dentry *dentry) | |||
613 | out_cancel: | 614 | out_cancel: |
614 | dir->i_size += sz_change; | 615 | dir->i_size += sz_change; |
615 | dir_ui->ui_size = dir->i_size; | 616 | dir_ui->ui_size = dir->i_size; |
616 | inc_nlink(inode); | 617 | set_nlink(inode, saved_nlink); |
617 | unlock_2_inodes(dir, inode); | 618 | unlock_2_inodes(dir, inode); |
618 | if (budgeted) | 619 | if (budgeted) |
619 | ubifs_release_budget(c, &req); | 620 | ubifs_release_budget(c, &req); |
@@ -704,8 +705,7 @@ out_cancel: | |||
704 | dir->i_size += sz_change; | 705 | dir->i_size += sz_change; |
705 | dir_ui->ui_size = dir->i_size; | 706 | dir_ui->ui_size = dir->i_size; |
706 | inc_nlink(dir); | 707 | inc_nlink(dir); |
707 | inc_nlink(inode); | 708 | set_nlink(inode, 2); |
708 | inc_nlink(inode); | ||
709 | unlock_2_inodes(dir, inode); | 709 | unlock_2_inodes(dir, inode); |
710 | if (budgeted) | 710 | if (budgeted) |
711 | ubifs_release_budget(c, &req); | 711 | ubifs_release_budget(c, &req); |
@@ -977,6 +977,7 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
977 | struct ubifs_budget_req ino_req = { .dirtied_ino = 1, | 977 | struct ubifs_budget_req ino_req = { .dirtied_ino = 1, |
978 | .dirtied_ino_d = ALIGN(old_inode_ui->data_len, 8) }; | 978 | .dirtied_ino_d = ALIGN(old_inode_ui->data_len, 8) }; |
979 | struct timespec time; | 979 | struct timespec time; |
980 | unsigned int saved_nlink; | ||
980 | 981 | ||
981 | /* | 982 | /* |
982 | * Budget request settings: deletion direntry, new direntry, removing | 983 | * 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 | if (unlink) { | 1060 | if (unlink) { |
1060 | /* | 1061 | /* |
1061 | * Directories cannot have hard-links, so if this is a | 1062 | * Directories cannot have hard-links, so if this is a |
1062 | * directory, decrement its @i_nlink twice because an empty | 1063 | * directory, just clear @i_nlink. |
1063 | * directory has @i_nlink 2. | ||
1064 | */ | 1064 | */ |
1065 | saved_nlink = new_inode->i_nlink; | ||
1065 | if (is_dir) | 1066 | if (is_dir) |
1067 | clear_nlink(new_inode); | ||
1068 | else | ||
1066 | drop_nlink(new_inode); | 1069 | drop_nlink(new_inode); |
1067 | new_inode->i_ctime = time; | 1070 | new_inode->i_ctime = time; |
1068 | drop_nlink(new_inode); | ||
1069 | } else { | 1071 | } else { |
1070 | new_dir->i_size += new_sz; | 1072 | new_dir->i_size += new_sz; |
1071 | ubifs_inode(new_dir)->ui_size = new_dir->i_size; | 1073 | 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 | 1104 | ||
1103 | out_cancel: | 1105 | out_cancel: |
1104 | if (unlink) { | 1106 | if (unlink) { |
1105 | if (is_dir) | 1107 | set_nlink(new_inode, saved_nlink); |
1106 | inc_nlink(new_inode); | ||
1107 | inc_nlink(new_inode); | ||
1108 | } else { | 1108 | } else { |
1109 | new_dir->i_size -= new_sz; | 1109 | new_dir->i_size -= new_sz; |
1110 | ubifs_inode(new_dir)->ui_size = new_dir->i_size; | 1110 | ubifs_inode(new_dir)->ui_size = new_dir->i_size; |