diff options
author | Yan, Zheng <zheng.yan@oracle.com> | 2009-09-24 09:17:31 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2009-09-24 09:17:31 -0400 |
commit | a57195214358b75807a74bad96a8601a36262af7 (patch) | |
tree | e288410ecfab5f15097bb66f06a5dd6bf99bca08 /fs/btrfs | |
parent | 11ef160fda9c150cd75db77194bcc66839709662 (diff) |
Btrfs: check size of inode backref before adding hardlink
For every hardlink in btrfs, there is a corresponding inode back
reference. All inode back references for hardlinks in a given
directory are stored in single b-tree item. The size of b-tree item
is limited by the size of b-tree leaf, so we can only create limited
number of hardlinks to a given file in a directory.
The original code lacks of the check, it oops if the number of
hardlinks goes over the limit. This patch fixes the issue by adding
check to btrfs_link and btrfs_rename.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r-- | fs/btrfs/ctree.c | 6 | ||||
-rw-r--r-- | fs/btrfs/inode-item.c | 2 | ||||
-rw-r--r-- | fs/btrfs/inode.c | 53 |
3 files changed, 37 insertions, 24 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 3fdcc0512d3a..ec96f3a6d536 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c | |||
@@ -2853,6 +2853,12 @@ static noinline int split_leaf(struct btrfs_trans_handle *trans, | |||
2853 | int split; | 2853 | int split; |
2854 | int num_doubles = 0; | 2854 | int num_doubles = 0; |
2855 | 2855 | ||
2856 | l = path->nodes[0]; | ||
2857 | slot = path->slots[0]; | ||
2858 | if (extend && data_size + btrfs_item_size_nr(l, slot) + | ||
2859 | sizeof(struct btrfs_item) > BTRFS_LEAF_DATA_SIZE(root)) | ||
2860 | return -EOVERFLOW; | ||
2861 | |||
2856 | /* first try to make some room by pushing left and right */ | 2862 | /* first try to make some room by pushing left and right */ |
2857 | if (data_size && ins_key->type != BTRFS_DIR_ITEM_KEY) { | 2863 | if (data_size && ins_key->type != BTRFS_DIR_ITEM_KEY) { |
2858 | wret = push_leaf_right(trans, root, path, data_size, 0); | 2864 | wret = push_leaf_right(trans, root, path, data_size, 0); |
diff --git a/fs/btrfs/inode-item.c b/fs/btrfs/inode-item.c index fc6f4a7f6405..72ce3c173d6a 100644 --- a/fs/btrfs/inode-item.c +++ b/fs/btrfs/inode-item.c | |||
@@ -149,6 +149,8 @@ int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans, | |||
149 | ptr = (unsigned long)(ref + 1); | 149 | ptr = (unsigned long)(ref + 1); |
150 | ret = 0; | 150 | ret = 0; |
151 | } else if (ret < 0) { | 151 | } else if (ret < 0) { |
152 | if (ret == -EOVERFLOW) | ||
153 | ret = -EMLINK; | ||
152 | goto out; | 154 | goto out; |
153 | } else { | 155 | } else { |
154 | ref = btrfs_item_ptr(path->nodes[0], path->slots[0], | 156 | ref = btrfs_item_ptr(path->nodes[0], path->slots[0], |
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index db9cbd91eb4c..19fcde289dd9 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c | |||
@@ -4133,18 +4133,16 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir, | |||
4133 | 4133 | ||
4134 | err = btrfs_add_nondir(trans, dentry, inode, 1, index); | 4134 | err = btrfs_add_nondir(trans, dentry, inode, 1, index); |
4135 | 4135 | ||
4136 | if (err) | 4136 | if (err) { |
4137 | drop_inode = 1; | ||
4138 | |||
4139 | btrfs_update_inode_block_group(trans, dir); | ||
4140 | err = btrfs_update_inode(trans, root, inode); | ||
4141 | |||
4142 | if (err) | ||
4143 | drop_inode = 1; | 4137 | drop_inode = 1; |
4138 | } else { | ||
4139 | btrfs_update_inode_block_group(trans, dir); | ||
4140 | err = btrfs_update_inode(trans, root, inode); | ||
4141 | BUG_ON(err); | ||
4142 | btrfs_log_new_name(trans, inode, NULL, dentry->d_parent); | ||
4143 | } | ||
4144 | 4144 | ||
4145 | nr = trans->blocks_used; | 4145 | nr = trans->blocks_used; |
4146 | |||
4147 | btrfs_log_new_name(trans, inode, NULL, dentry->d_parent); | ||
4148 | btrfs_end_transaction_throttle(trans, root); | 4146 | btrfs_end_transaction_throttle(trans, root); |
4149 | fail: | 4147 | fail: |
4150 | if (drop_inode) { | 4148 | if (drop_inode) { |
@@ -5087,23 +5085,26 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
5087 | down_read(&root->fs_info->subvol_sem); | 5085 | down_read(&root->fs_info->subvol_sem); |
5088 | 5086 | ||
5089 | trans = btrfs_start_transaction(root, 1); | 5087 | trans = btrfs_start_transaction(root, 1); |
5088 | btrfs_set_trans_block_group(trans, new_dir); | ||
5090 | 5089 | ||
5091 | if (dest != root) | 5090 | if (dest != root) |
5092 | btrfs_record_root_in_trans(trans, dest); | 5091 | btrfs_record_root_in_trans(trans, dest); |
5093 | 5092 | ||
5094 | /* | 5093 | ret = btrfs_set_inode_index(new_dir, &index); |
5095 | * make sure the inode gets flushed if it is replacing | 5094 | if (ret) |
5096 | * something. | 5095 | goto out_fail; |
5097 | */ | ||
5098 | if (new_inode && new_inode->i_size && | ||
5099 | old_inode && S_ISREG(old_inode->i_mode)) { | ||
5100 | btrfs_add_ordered_operation(trans, root, old_inode); | ||
5101 | } | ||
5102 | 5096 | ||
5103 | if (old_inode->i_ino == BTRFS_FIRST_FREE_OBJECTID) { | 5097 | if (unlikely(old_inode->i_ino == BTRFS_FIRST_FREE_OBJECTID)) { |
5104 | /* force full log commit if subvolume involved. */ | 5098 | /* force full log commit if subvolume involved. */ |
5105 | root->fs_info->last_trans_log_full_commit = trans->transid; | 5099 | root->fs_info->last_trans_log_full_commit = trans->transid; |
5106 | } else { | 5100 | } else { |
5101 | ret = btrfs_insert_inode_ref(trans, dest, | ||
5102 | new_dentry->d_name.name, | ||
5103 | new_dentry->d_name.len, | ||
5104 | old_inode->i_ino, | ||
5105 | new_dir->i_ino, index); | ||
5106 | if (ret) | ||
5107 | goto out_fail; | ||
5107 | /* | 5108 | /* |
5108 | * this is an ugly little race, but the rename is required | 5109 | * this is an ugly little race, but the rename is required |
5109 | * to make sure that if we crash, the inode is either at the | 5110 | * to make sure that if we crash, the inode is either at the |
@@ -5113,8 +5114,14 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
5113 | */ | 5114 | */ |
5114 | btrfs_pin_log_trans(root); | 5115 | btrfs_pin_log_trans(root); |
5115 | } | 5116 | } |
5116 | 5117 | /* | |
5117 | btrfs_set_trans_block_group(trans, new_dir); | 5118 | * make sure the inode gets flushed if it is replacing |
5119 | * something. | ||
5120 | */ | ||
5121 | if (new_inode && new_inode->i_size && | ||
5122 | old_inode && S_ISREG(old_inode->i_mode)) { | ||
5123 | btrfs_add_ordered_operation(trans, root, old_inode); | ||
5124 | } | ||
5118 | 5125 | ||
5119 | old_dir->i_ctime = old_dir->i_mtime = ctime; | 5126 | old_dir->i_ctime = old_dir->i_mtime = ctime; |
5120 | new_dir->i_ctime = new_dir->i_mtime = ctime; | 5127 | new_dir->i_ctime = new_dir->i_mtime = ctime; |
@@ -5159,12 +5166,10 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
5159 | BUG_ON(ret); | 5166 | BUG_ON(ret); |
5160 | } | 5167 | } |
5161 | } | 5168 | } |
5162 | ret = btrfs_set_inode_index(new_dir, &index); | ||
5163 | BUG_ON(ret); | ||
5164 | 5169 | ||
5165 | ret = btrfs_add_link(trans, new_dir, old_inode, | 5170 | ret = btrfs_add_link(trans, new_dir, old_inode, |
5166 | new_dentry->d_name.name, | 5171 | new_dentry->d_name.name, |
5167 | new_dentry->d_name.len, 1, index); | 5172 | new_dentry->d_name.len, 0, index); |
5168 | BUG_ON(ret); | 5173 | BUG_ON(ret); |
5169 | 5174 | ||
5170 | if (old_inode->i_ino != BTRFS_FIRST_FREE_OBJECTID) { | 5175 | if (old_inode->i_ino != BTRFS_FIRST_FREE_OBJECTID) { |
@@ -5172,7 +5177,7 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
5172 | new_dentry->d_parent); | 5177 | new_dentry->d_parent); |
5173 | btrfs_end_log_trans(root); | 5178 | btrfs_end_log_trans(root); |
5174 | } | 5179 | } |
5175 | 5180 | out_fail: | |
5176 | btrfs_end_transaction_throttle(trans, root); | 5181 | btrfs_end_transaction_throttle(trans, root); |
5177 | 5182 | ||
5178 | if (old_inode->i_ino == BTRFS_FIRST_FREE_OBJECTID) | 5183 | if (old_inode->i_ino == BTRFS_FIRST_FREE_OBJECTID) |