aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs
diff options
context:
space:
mode:
authorSage Weil <sage@newdream.net>2008-05-02 14:43:14 -0400
committerChris Mason <chris.mason@oracle.com>2008-09-25 11:04:02 -0400
commitf2eb0a241f0e5c135d93243b0236cb1f14c305e0 (patch)
treedd3141efb9142a294222b9195593371887519207 /fs/btrfs
parentd6bfde8765668c8687de72f7a40f52acdf4f2f19 (diff)
Btrfs: Clone file data ioctl
Add a new ioctl to clone file data Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r--fs/btrfs/ctree.h4
-rw-r--r--fs/btrfs/file-item.c12
-rw-r--r--fs/btrfs/file.c2
-rw-r--r--fs/btrfs/inode.c171
-rw-r--r--fs/btrfs/ioctl.h1
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 */
1517int btrfs_insert_file_extent(struct btrfs_trans_handle *trans, 1517int 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);
1522int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans, 1522int 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))
30int btrfs_insert_file_extent(struct btrfs_trans_handle *trans, 30int 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
3104void 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
3127long 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 }
3190next_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
3247out:
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
3260out_unlock:
3261 mutex_unlock(&src->i_mutex);
3262 mutex_unlock(&inode->i_mutex);
3263out_fput:
3264 fput(src_file);
3265 return ret;
3266}
3267
3103long btrfs_ioctl(struct file *file, unsigned int 3268long 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, \