diff options
author | Sage Weil <sage@newdream.net> | 2008-05-02 14:43:14 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2008-09-25 11:04:02 -0400 |
commit | f2eb0a241f0e5c135d93243b0236cb1f14c305e0 (patch) | |
tree | dd3141efb9142a294222b9195593371887519207 | |
parent | d6bfde8765668c8687de72f7a40f52acdf4f2f19 (diff) |
Btrfs: Clone file data ioctl
Add a new ioctl to clone file data
Signed-off-by: Chris Mason <chris.mason@oracle.com>
-rw-r--r-- | fs/btrfs/ctree.h | 4 | ||||
-rw-r--r-- | fs/btrfs/file-item.c | 12 | ||||
-rw-r--r-- | fs/btrfs/file.c | 2 | ||||
-rw-r--r-- | fs/btrfs/inode.c | 171 | ||||
-rw-r--r-- | fs/btrfs/ioctl.h | 1 |
5 files changed, 179 insertions, 11 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 73b92dd150ff..3b6f8524a4ad 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h | |||
@@ -1516,9 +1516,9 @@ int btrfs_lookup_inode(struct btrfs_trans_handle *trans, struct btrfs_root | |||
1516 | /* file-item.c */ | 1516 | /* file-item.c */ |
1517 | int btrfs_insert_file_extent(struct btrfs_trans_handle *trans, | 1517 | int btrfs_insert_file_extent(struct btrfs_trans_handle *trans, |
1518 | struct btrfs_root *root, | 1518 | struct btrfs_root *root, |
1519 | u64 objectid, u64 pos, u64 offset, | 1519 | u64 objectid, u64 pos, u64 disk_offset, |
1520 | u64 disk_num_bytes, | 1520 | u64 disk_num_bytes, |
1521 | u64 num_bytes); | 1521 | u64 num_bytes, u64 offset); |
1522 | int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans, | 1522 | int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans, |
1523 | struct btrfs_root *root, | 1523 | struct btrfs_root *root, |
1524 | struct btrfs_path *path, u64 objectid, | 1524 | struct btrfs_path *path, u64 objectid, |
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index 9259aece6ed1..f537eb43c2c6 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c | |||
@@ -28,10 +28,10 @@ | |||
28 | sizeof(struct btrfs_item) * 2) / \ | 28 | sizeof(struct btrfs_item) * 2) / \ |
29 | BTRFS_CRC32_SIZE) - 1)) | 29 | BTRFS_CRC32_SIZE) - 1)) |
30 | int btrfs_insert_file_extent(struct btrfs_trans_handle *trans, | 30 | int btrfs_insert_file_extent(struct btrfs_trans_handle *trans, |
31 | struct btrfs_root *root, | 31 | struct btrfs_root *root, |
32 | u64 objectid, u64 pos, | 32 | u64 objectid, u64 pos, |
33 | u64 offset, u64 disk_num_bytes, | 33 | u64 disk_offset, u64 disk_num_bytes, |
34 | u64 num_bytes) | 34 | u64 num_bytes, u64 offset) |
35 | { | 35 | { |
36 | int ret = 0; | 36 | int ret = 0; |
37 | struct btrfs_file_extent_item *item; | 37 | struct btrfs_file_extent_item *item; |
@@ -53,9 +53,9 @@ int btrfs_insert_file_extent(struct btrfs_trans_handle *trans, | |||
53 | leaf = path->nodes[0]; | 53 | leaf = path->nodes[0]; |
54 | item = btrfs_item_ptr(leaf, path->slots[0], | 54 | item = btrfs_item_ptr(leaf, path->slots[0], |
55 | struct btrfs_file_extent_item); | 55 | struct btrfs_file_extent_item); |
56 | btrfs_set_file_extent_disk_bytenr(leaf, item, offset); | 56 | btrfs_set_file_extent_disk_bytenr(leaf, item, disk_offset); |
57 | btrfs_set_file_extent_disk_num_bytes(leaf, item, disk_num_bytes); | 57 | btrfs_set_file_extent_disk_num_bytes(leaf, item, disk_num_bytes); |
58 | btrfs_set_file_extent_offset(leaf, item, 0); | 58 | btrfs_set_file_extent_offset(leaf, item, offset); |
59 | btrfs_set_file_extent_num_bytes(leaf, item, num_bytes); | 59 | btrfs_set_file_extent_num_bytes(leaf, item, num_bytes); |
60 | btrfs_set_file_extent_generation(leaf, item, trans->transid); | 60 | btrfs_set_file_extent_generation(leaf, item, trans->transid); |
61 | btrfs_set_file_extent_type(leaf, item, BTRFS_FILE_EXTENT_REG); | 61 | btrfs_set_file_extent_type(leaf, item, BTRFS_FILE_EXTENT_REG); |
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 8effdf4f5d6f..a50507f3056d 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c | |||
@@ -285,7 +285,7 @@ static int noinline dirty_and_release_pages(struct btrfs_trans_handle *trans, | |||
285 | err = btrfs_insert_file_extent(trans, root, | 285 | err = btrfs_insert_file_extent(trans, root, |
286 | inode->i_ino, | 286 | inode->i_ino, |
287 | last_pos_in_file, | 287 | last_pos_in_file, |
288 | 0, 0, hole_size); | 288 | 0, 0, hole_size, 0); |
289 | btrfs_drop_extent_cache(inode, last_pos_in_file, | 289 | btrfs_drop_extent_cache(inode, last_pos_in_file, |
290 | last_pos_in_file + hole_size -1); | 290 | last_pos_in_file + hole_size -1); |
291 | btrfs_check_file(root, inode); | 291 | btrfs_check_file(root, inode); |
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 994834474590..c6fae29c0b9e 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/kernel.h> | 19 | #include <linux/kernel.h> |
20 | #include <linux/bio.h> | 20 | #include <linux/bio.h> |
21 | #include <linux/buffer_head.h> | 21 | #include <linux/buffer_head.h> |
22 | #include <linux/file.h> | ||
22 | #include <linux/fs.h> | 23 | #include <linux/fs.h> |
23 | #include <linux/pagemap.h> | 24 | #include <linux/pagemap.h> |
24 | #include <linux/highmem.h> | 25 | #include <linux/highmem.h> |
@@ -141,7 +142,7 @@ static int cow_file_range(struct inode *inode, u64 start, u64 end) | |||
141 | cur_alloc_size = ins.offset; | 142 | cur_alloc_size = ins.offset; |
142 | ret = btrfs_insert_file_extent(trans, root, inode->i_ino, | 143 | ret = btrfs_insert_file_extent(trans, root, inode->i_ino, |
143 | start, ins.objectid, ins.offset, | 144 | start, ins.objectid, ins.offset, |
144 | ins.offset); | 145 | ins.offset, 0); |
145 | inode->i_blocks += ins.offset >> 9; | 146 | inode->i_blocks += ins.offset >> 9; |
146 | btrfs_check_file(root, inode); | 147 | btrfs_check_file(root, inode); |
147 | if (num_bytes < cur_alloc_size) { | 148 | if (num_bytes < cur_alloc_size) { |
@@ -1227,7 +1228,7 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr) | |||
1227 | err = btrfs_insert_file_extent(trans, root, | 1228 | err = btrfs_insert_file_extent(trans, root, |
1228 | inode->i_ino, | 1229 | inode->i_ino, |
1229 | hole_start, 0, 0, | 1230 | hole_start, 0, 0, |
1230 | hole_size); | 1231 | hole_size, 0); |
1231 | btrfs_drop_extent_cache(inode, hole_start, | 1232 | btrfs_drop_extent_cache(inode, hole_start, |
1232 | (u64)-1); | 1233 | (u64)-1); |
1233 | btrfs_check_file(root, inode); | 1234 | btrfs_check_file(root, inode); |
@@ -3100,6 +3101,170 @@ out: | |||
3100 | return ret; | 3101 | return ret; |
3101 | } | 3102 | } |
3102 | 3103 | ||
3104 | void dup_item_to_inode(struct btrfs_trans_handle *trans, | ||
3105 | struct btrfs_root *root, | ||
3106 | struct btrfs_path *path, | ||
3107 | struct extent_buffer *leaf, | ||
3108 | int slot, | ||
3109 | struct btrfs_key *key, | ||
3110 | u64 destino) | ||
3111 | { | ||
3112 | struct btrfs_path *cpath = btrfs_alloc_path(); | ||
3113 | int len = btrfs_item_size_nr(leaf, slot); | ||
3114 | int dstoff; | ||
3115 | struct btrfs_key ckey = *key; | ||
3116 | int ret; | ||
3117 | |||
3118 | ckey.objectid = destino; | ||
3119 | ret = btrfs_insert_empty_item(trans, root, cpath, &ckey, len); | ||
3120 | dstoff = btrfs_item_ptr_offset(cpath->nodes[0], cpath->slots[0]); | ||
3121 | copy_extent_buffer(cpath->nodes[0], leaf, dstoff, | ||
3122 | btrfs_item_ptr_offset(leaf, slot), | ||
3123 | len); | ||
3124 | btrfs_release_path(root, cpath); | ||
3125 | } | ||
3126 | |||
3127 | long btrfs_ioctl_clone(struct file *file, unsigned long src_fd) | ||
3128 | { | ||
3129 | struct inode *inode = fdentry(file)->d_inode; | ||
3130 | struct btrfs_root *root = BTRFS_I(inode)->root; | ||
3131 | struct file *src_file; | ||
3132 | struct inode *src; | ||
3133 | struct btrfs_trans_handle *trans; | ||
3134 | int ret; | ||
3135 | u64 pos; | ||
3136 | struct btrfs_path *path; | ||
3137 | struct btrfs_key key; | ||
3138 | struct extent_buffer *leaf; | ||
3139 | u32 nritems; | ||
3140 | int nextret; | ||
3141 | int slot; | ||
3142 | |||
3143 | src_file = fget(src_fd); | ||
3144 | if (!src_file) | ||
3145 | return -EBADF; | ||
3146 | src = src_file->f_dentry->d_inode; | ||
3147 | |||
3148 | ret = -EXDEV; | ||
3149 | if (src->i_sb != inode->i_sb) | ||
3150 | goto out_fput; | ||
3151 | |||
3152 | if (inode < src) { | ||
3153 | mutex_lock(&inode->i_mutex); | ||
3154 | mutex_lock(&src->i_mutex); | ||
3155 | } else { | ||
3156 | mutex_lock(&src->i_mutex); | ||
3157 | mutex_lock(&inode->i_mutex); | ||
3158 | } | ||
3159 | |||
3160 | ret = -ENOTEMPTY; | ||
3161 | if (inode->i_size) | ||
3162 | goto out_unlock; | ||
3163 | |||
3164 | /* do any pending delalloc/csum calc on src, one way or | ||
3165 | another, and lock file content */ | ||
3166 | while (1) { | ||
3167 | filemap_write_and_wait(src->i_mapping); | ||
3168 | lock_extent(&BTRFS_I(src)->io_tree, 0, (u64)-1, GFP_NOFS); | ||
3169 | if (BTRFS_I(src)->delalloc_bytes == 0) | ||
3170 | break; | ||
3171 | unlock_extent(&BTRFS_I(src)->io_tree, 0, (u64)-1, GFP_NOFS); | ||
3172 | } | ||
3173 | |||
3174 | mutex_lock(&root->fs_info->fs_mutex); | ||
3175 | trans = btrfs_start_transaction(root, 0); | ||
3176 | path = btrfs_alloc_path(); | ||
3177 | pos = 0; | ||
3178 | while (1) { | ||
3179 | ret = btrfs_lookup_file_extent(trans, root, path, src->i_ino, | ||
3180 | pos, 0); | ||
3181 | if (ret < 0) | ||
3182 | goto out; | ||
3183 | if (ret > 0) { | ||
3184 | if (path->slots[0] == 0) { | ||
3185 | ret = 0; | ||
3186 | goto out; | ||
3187 | } | ||
3188 | path->slots[0]--; | ||
3189 | } | ||
3190 | next_slot: | ||
3191 | leaf = path->nodes[0]; | ||
3192 | slot = path->slots[0]; | ||
3193 | btrfs_item_key_to_cpu(leaf, &key, slot); | ||
3194 | nritems = btrfs_header_nritems(leaf); | ||
3195 | |||
3196 | if (btrfs_key_type(&key) > BTRFS_CSUM_ITEM_KEY || | ||
3197 | key.objectid != src->i_ino) | ||
3198 | goto out; | ||
3199 | if (btrfs_key_type(&key) == BTRFS_EXTENT_DATA_KEY) { | ||
3200 | struct btrfs_file_extent_item *extent; | ||
3201 | int found_type; | ||
3202 | pos = key.offset; | ||
3203 | extent = btrfs_item_ptr(leaf, slot, | ||
3204 | struct btrfs_file_extent_item); | ||
3205 | found_type = btrfs_file_extent_type(leaf, extent); | ||
3206 | if (found_type == BTRFS_FILE_EXTENT_REG) { | ||
3207 | u64 len = btrfs_file_extent_num_bytes(leaf, | ||
3208 | extent); | ||
3209 | u64 ds = btrfs_file_extent_disk_bytenr(leaf, | ||
3210 | extent); | ||
3211 | u64 dl = btrfs_file_extent_disk_num_bytes(leaf, | ||
3212 | extent); | ||
3213 | u64 off = btrfs_file_extent_offset(leaf, | ||
3214 | extent); | ||
3215 | btrfs_insert_file_extent(trans, root, | ||
3216 | inode->i_ino, pos, | ||
3217 | ds, dl, len, off); | ||
3218 | /* ds == 0 means there's a hole */ | ||
3219 | if (ds != 0) { | ||
3220 | btrfs_inc_extent_ref(trans, root, | ||
3221 | ds, dl, | ||
3222 | root->root_key.objectid, | ||
3223 | trans->transid, | ||
3224 | inode->i_ino, pos); | ||
3225 | } | ||
3226 | pos = key.offset + len; | ||
3227 | } else if (found_type == BTRFS_FILE_EXTENT_INLINE) { | ||
3228 | dup_item_to_inode(trans, root, path, leaf, slot, | ||
3229 | &key, inode->i_ino); | ||
3230 | pos = key.offset + btrfs_item_size_nr(leaf, | ||
3231 | slot); | ||
3232 | } | ||
3233 | } else if (btrfs_key_type(&key) == BTRFS_CSUM_ITEM_KEY) | ||
3234 | dup_item_to_inode(trans, root, path, leaf, slot, &key, | ||
3235 | inode->i_ino); | ||
3236 | |||
3237 | if (slot >= nritems - 1) { | ||
3238 | nextret = btrfs_next_leaf(root, path); | ||
3239 | if (nextret) | ||
3240 | goto out; | ||
3241 | } else { | ||
3242 | path->slots[0]++; | ||
3243 | } | ||
3244 | goto next_slot; | ||
3245 | } | ||
3246 | |||
3247 | out: | ||
3248 | btrfs_free_path(path); | ||
3249 | ret = 0; | ||
3250 | |||
3251 | inode->i_blocks = src->i_blocks; | ||
3252 | i_size_write(inode, src->i_size); | ||
3253 | btrfs_update_inode(trans, root, inode); | ||
3254 | |||
3255 | unlock_extent(&BTRFS_I(src)->io_tree, 0, (u64)-1, GFP_NOFS); | ||
3256 | |||
3257 | btrfs_end_transaction(trans, root); | ||
3258 | mutex_unlock(&root->fs_info->fs_mutex); | ||
3259 | |||
3260 | out_unlock: | ||
3261 | mutex_unlock(&src->i_mutex); | ||
3262 | mutex_unlock(&inode->i_mutex); | ||
3263 | out_fput: | ||
3264 | fput(src_file); | ||
3265 | return ret; | ||
3266 | } | ||
3267 | |||
3103 | long btrfs_ioctl(struct file *file, unsigned int | 3268 | long btrfs_ioctl(struct file *file, unsigned int |
3104 | cmd, unsigned long arg) | 3269 | cmd, unsigned long arg) |
3105 | { | 3270 | { |
@@ -3116,6 +3281,8 @@ long btrfs_ioctl(struct file *file, unsigned int | |||
3116 | return btrfs_ioctl_add_dev(root, (void __user *)arg); | 3281 | return btrfs_ioctl_add_dev(root, (void __user *)arg); |
3117 | case BTRFS_IOC_BALANCE: | 3282 | case BTRFS_IOC_BALANCE: |
3118 | return btrfs_balance(root->fs_info->dev_root); | 3283 | return btrfs_balance(root->fs_info->dev_root); |
3284 | case BTRFS_IOC_CLONE: | ||
3285 | return btrfs_ioctl_clone(file, arg); | ||
3119 | } | 3286 | } |
3120 | 3287 | ||
3121 | return -ENOTTY; | 3288 | return -ENOTTY; |
diff --git a/fs/btrfs/ioctl.h b/fs/btrfs/ioctl.h index 8ad35fc4ba56..b0e73f51d636 100644 --- a/fs/btrfs/ioctl.h +++ b/fs/btrfs/ioctl.h | |||
@@ -36,6 +36,7 @@ struct btrfs_ioctl_vol_args { | |||
36 | struct btrfs_ioctl_vol_args) | 36 | struct btrfs_ioctl_vol_args) |
37 | #define BTRFS_IOC_SCAN_DEV _IOW(BTRFS_IOCTL_MAGIC, 4, \ | 37 | #define BTRFS_IOC_SCAN_DEV _IOW(BTRFS_IOCTL_MAGIC, 4, \ |
38 | struct btrfs_ioctl_vol_args) | 38 | struct btrfs_ioctl_vol_args) |
39 | #define BTRFS_IOC_CLONE _IOW(BTRFS_IOCTL_MAGIC, 9, int) | ||
39 | #define BTRFS_IOC_ADD_DEV _IOW(BTRFS_IOCTL_MAGIC, 10, \ | 40 | #define BTRFS_IOC_ADD_DEV _IOW(BTRFS_IOCTL_MAGIC, 10, \ |
40 | struct btrfs_ioctl_vol_args) | 41 | struct btrfs_ioctl_vol_args) |
41 | #define BTRFS_IOC_RM_DEV _IOW(BTRFS_IOCTL_MAGIC, 11, \ | 42 | #define BTRFS_IOC_RM_DEV _IOW(BTRFS_IOCTL_MAGIC, 11, \ |