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 | |
| 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>
| -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) |
