btrfs-progs: convert: Introduce function to check if convert image is able to be...
[platform/upstream/btrfs-progs.git] / convert / source-fs.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public
4  * License v2 as published by the Free Software Foundation.
5  *
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
9  * General Public License for more details.
10  *
11  * You should have received a copy of the GNU General Public
12  * License along with this program; if not, write to the
13  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
14  * Boston, MA 021110-1307, USA.
15  */
16
17 #include "kerncompat.h"
18 #include <unistd.h>
19 #include "internal.h"
20 #include "disk-io.h"
21 #include "volumes.h"
22 #include "convert/common.h"
23 #include "convert/source-fs.h"
24
25 struct simple_range btrfs_reserved_ranges[3] = {
26         { 0,                         SZ_1M },
27         { BTRFS_SB_MIRROR_OFFSET(1), SZ_64K },
28         { BTRFS_SB_MIRROR_OFFSET(2), SZ_64K }
29 };
30
31 static int intersect_with_sb(u64 bytenr, u64 num_bytes)
32 {
33         int i;
34         u64 offset;
35
36         for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
37                 offset = btrfs_sb_offset(i);
38                 offset &= ~((u64)BTRFS_STRIPE_LEN - 1);
39
40                 if (bytenr < offset + BTRFS_STRIPE_LEN &&
41                     bytenr + num_bytes > offset)
42                         return 1;
43         }
44         return 0;
45 }
46
47 void init_convert_context(struct btrfs_convert_context *cctx)
48 {
49         memset(cctx, 0, sizeof(*cctx));
50
51         cache_tree_init(&cctx->used_space);
52         cache_tree_init(&cctx->data_chunks);
53         cache_tree_init(&cctx->free_space);
54 }
55
56 void clean_convert_context(struct btrfs_convert_context *cctx)
57 {
58         free_extent_cache_tree(&cctx->used_space);
59         free_extent_cache_tree(&cctx->data_chunks);
60         free_extent_cache_tree(&cctx->free_space);
61 }
62
63 int block_iterate_proc(u64 disk_block, u64 file_block,
64                               struct blk_iterate_data *idata)
65 {
66         int ret = 0;
67         int sb_region;
68         int do_barrier;
69         struct btrfs_root *root = idata->root;
70         struct btrfs_block_group_cache *cache;
71         u64 bytenr = disk_block * root->sectorsize;
72
73         sb_region = intersect_with_sb(bytenr, root->sectorsize);
74         do_barrier = sb_region || disk_block >= idata->boundary;
75         if ((idata->num_blocks > 0 && do_barrier) ||
76             (file_block > idata->first_block + idata->num_blocks) ||
77             (disk_block != idata->disk_block + idata->num_blocks)) {
78                 if (idata->num_blocks > 0) {
79                         ret = record_file_blocks(idata, idata->first_block,
80                                                  idata->disk_block,
81                                                  idata->num_blocks);
82                         if (ret)
83                                 goto fail;
84                         idata->first_block += idata->num_blocks;
85                         idata->num_blocks = 0;
86                 }
87                 if (file_block > idata->first_block) {
88                         ret = record_file_blocks(idata, idata->first_block,
89                                         0, file_block - idata->first_block);
90                         if (ret)
91                                 goto fail;
92                 }
93
94                 if (sb_region) {
95                         bytenr += BTRFS_STRIPE_LEN - 1;
96                         bytenr &= ~((u64)BTRFS_STRIPE_LEN - 1);
97                 } else {
98                         cache = btrfs_lookup_block_group(root->fs_info, bytenr);
99                         BUG_ON(!cache);
100                         bytenr = cache->key.objectid + cache->key.offset;
101                 }
102
103                 idata->first_block = file_block;
104                 idata->disk_block = disk_block;
105                 idata->boundary = bytenr / root->sectorsize;
106         }
107         idata->num_blocks++;
108 fail:
109         return ret;
110 }
111
112 void init_blk_iterate_data(struct blk_iterate_data *data,
113                                   struct btrfs_trans_handle *trans,
114                                   struct btrfs_root *root,
115                                   struct btrfs_inode_item *inode,
116                                   u64 objectid, int checksum)
117 {
118         struct btrfs_key key;
119
120         data->trans             = trans;
121         data->root              = root;
122         data->inode             = inode;
123         data->objectid          = objectid;
124         data->first_block       = 0;
125         data->disk_block        = 0;
126         data->num_blocks        = 0;
127         data->boundary          = (u64)-1;
128         data->checksum          = checksum;
129         data->errcode           = 0;
130
131         key.objectid = CONV_IMAGE_SUBVOL_OBJECTID;
132         key.type = BTRFS_ROOT_ITEM_KEY;
133         key.offset = (u64)-1;
134         data->convert_root = btrfs_read_fs_root(root->fs_info, &key);
135         /* Impossible as we just opened it before */
136         BUG_ON(!data->convert_root || IS_ERR(data->convert_root));
137         data->convert_ino = BTRFS_FIRST_FREE_OBJECTID + 1;
138 }
139
140 int convert_insert_dirent(struct btrfs_trans_handle *trans,
141                                  struct btrfs_root *root,
142                                  const char *name, size_t name_len,
143                                  u64 dir, u64 objectid,
144                                  u8 file_type, u64 index_cnt,
145                                  struct btrfs_inode_item *inode)
146 {
147         int ret;
148         u64 inode_size;
149         struct btrfs_key location = {
150                 .objectid = objectid,
151                 .offset = 0,
152                 .type = BTRFS_INODE_ITEM_KEY,
153         };
154
155         ret = btrfs_insert_dir_item(trans, root, name, name_len,
156                                     dir, &location, file_type, index_cnt);
157         if (ret)
158                 return ret;
159         ret = btrfs_insert_inode_ref(trans, root, name, name_len,
160                                      objectid, dir, index_cnt);
161         if (ret)
162                 return ret;
163         inode_size = btrfs_stack_inode_size(inode) + name_len * 2;
164         btrfs_set_stack_inode_size(inode, inode_size);
165
166         return 0;
167 }
168
169 int read_disk_extent(struct btrfs_root *root, u64 bytenr,
170                             u32 num_bytes, char *buffer)
171 {
172         int ret;
173         struct btrfs_fs_devices *fs_devs = root->fs_info->fs_devices;
174
175         ret = pread(fs_devs->latest_bdev, buffer, num_bytes, bytenr);
176         if (ret != num_bytes)
177                 goto fail;
178         ret = 0;
179 fail:
180         if (ret > 0)
181                 ret = -1;
182         return ret;
183 }
184
185 /*
186  * Record a file extent in original filesystem into btrfs one.
187  * The special point is, old disk_block can point to a reserved range.
188  * So here, we don't use disk_block directly but search convert_root
189  * to get the real disk_bytenr.
190  */
191 int record_file_blocks(struct blk_iterate_data *data,
192                               u64 file_block, u64 disk_block, u64 num_blocks)
193 {
194         int ret = 0;
195         struct btrfs_root *root = data->root;
196         struct btrfs_root *convert_root = data->convert_root;
197         struct btrfs_path path;
198         u64 file_pos = file_block * root->sectorsize;
199         u64 old_disk_bytenr = disk_block * root->sectorsize;
200         u64 num_bytes = num_blocks * root->sectorsize;
201         u64 cur_off = old_disk_bytenr;
202
203         /* Hole, pass it to record_file_extent directly */
204         if (old_disk_bytenr == 0)
205                 return btrfs_record_file_extent(data->trans, root,
206                                 data->objectid, data->inode, file_pos, 0,
207                                 num_bytes);
208
209         btrfs_init_path(&path);
210
211         /*
212          * Search real disk bytenr from convert root
213          */
214         while (cur_off < old_disk_bytenr + num_bytes) {
215                 struct btrfs_key key;
216                 struct btrfs_file_extent_item *fi;
217                 struct extent_buffer *node;
218                 int slot;
219                 u64 extent_disk_bytenr;
220                 u64 extent_num_bytes;
221                 u64 real_disk_bytenr;
222                 u64 cur_len;
223
224                 key.objectid = data->convert_ino;
225                 key.type = BTRFS_EXTENT_DATA_KEY;
226                 key.offset = cur_off;
227
228                 ret = btrfs_search_slot(NULL, convert_root, &key, &path, 0, 0);
229                 if (ret < 0)
230                         break;
231                 if (ret > 0) {
232                         ret = btrfs_previous_item(convert_root, &path,
233                                                   data->convert_ino,
234                                                   BTRFS_EXTENT_DATA_KEY);
235                         if (ret < 0)
236                                 break;
237                         if (ret > 0) {
238                                 ret = -ENOENT;
239                                 break;
240                         }
241                 }
242                 node = path.nodes[0];
243                 slot = path.slots[0];
244                 btrfs_item_key_to_cpu(node, &key, slot);
245                 BUG_ON(key.type != BTRFS_EXTENT_DATA_KEY ||
246                        key.objectid != data->convert_ino ||
247                        key.offset > cur_off);
248                 fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item);
249                 extent_disk_bytenr = btrfs_file_extent_disk_bytenr(node, fi);
250                 extent_num_bytes = btrfs_file_extent_num_bytes(node, fi);
251                 BUG_ON(cur_off - key.offset >= extent_num_bytes);
252                 btrfs_release_path(&path);
253
254                 if (extent_disk_bytenr)
255                         real_disk_bytenr = cur_off - key.offset +
256                                            extent_disk_bytenr;
257                 else
258                         real_disk_bytenr = 0;
259                 cur_len = min(key.offset + extent_num_bytes,
260                               old_disk_bytenr + num_bytes) - cur_off;
261                 ret = btrfs_record_file_extent(data->trans, data->root,
262                                         data->objectid, data->inode, file_pos,
263                                         real_disk_bytenr, cur_len);
264                 if (ret < 0)
265                         break;
266                 cur_off += cur_len;
267                 file_pos += cur_len;
268
269                 /*
270                  * No need to care about csum
271                  * As every byte of old fs image is calculated for csum, no
272                  * need to waste CPU cycles now.
273                  */
274         }
275         btrfs_release_path(&path);
276         return ret;
277 }
278