/src/e2fsprogs/lib/ext2fs/extent.c
Line | Count | Source |
1 | | /* |
2 | | * extent.c --- routines to implement extents support |
3 | | * |
4 | | * Copyright (C) 2007 Theodore Ts'o. |
5 | | * |
6 | | * %Begin-Header% |
7 | | * This file may be redistributed under the terms of the GNU Library |
8 | | * General Public License, version 2. |
9 | | * %End-Header% |
10 | | */ |
11 | | |
12 | | #include "config.h" |
13 | | #include <stdio.h> |
14 | | #include <string.h> |
15 | | #if HAVE_UNISTD_H |
16 | | #include <unistd.h> |
17 | | #endif |
18 | | #if HAVE_ERRNO_H |
19 | | #include <errno.h> |
20 | | #endif |
21 | | #if HAVE_SYS_STAT_H |
22 | | #include <sys/stat.h> |
23 | | #endif |
24 | | #if HAVE_SYS_TYPES_H |
25 | | #include <sys/types.h> |
26 | | #endif |
27 | | |
28 | | #include "ext2_fs.h" |
29 | | #include "ext2fsP.h" |
30 | | #include "e2image.h" |
31 | | |
32 | | #undef DEBUG |
33 | | |
34 | | /* |
35 | | * Definitions to be dropped in lib/ext2fs/ext2fs.h |
36 | | */ |
37 | | |
38 | | /* |
39 | | * Private definitions |
40 | | */ |
41 | | |
42 | | struct extent_path { |
43 | | char *buf; |
44 | | int entries; |
45 | | int max_entries; |
46 | | int left; |
47 | | int visit_num; |
48 | | int flags; |
49 | | blk64_t end_blk; |
50 | | blk64_t blk; |
51 | | void *curr; |
52 | | }; |
53 | | |
54 | | |
55 | | struct ext2_extent_handle { |
56 | | errcode_t magic; |
57 | | ext2_filsys fs; |
58 | | ext2_ino_t ino; |
59 | | struct ext2_inode *inode; |
60 | | struct ext2_inode inodebuf; |
61 | | int type; |
62 | | int level; |
63 | | int max_depth; |
64 | | int max_paths; |
65 | | struct extent_path *path; |
66 | | }; |
67 | | |
68 | | struct ext2_extent_path { |
69 | | errcode_t magic; |
70 | | int leaf_height; |
71 | | blk64_t lblk; |
72 | | }; |
73 | | |
74 | | /* |
75 | | * Useful Debugging stuff |
76 | | */ |
77 | | |
78 | | #ifdef DEBUG |
79 | | static void dbg_show_header(struct ext3_extent_header *eh) |
80 | | { |
81 | | printf("header: magic=%x entries=%u max=%u depth=%u generation=%u\n", |
82 | | ext2fs_le16_to_cpu(eh->eh_magic), |
83 | | ext2fs_le16_to_cpu(eh->eh_entries), |
84 | | ext2fs_le16_to_cpu(eh->eh_max), |
85 | | ext2fs_le16_to_cpu(eh->eh_depth), |
86 | | ext2fs_le32_to_cpu(eh->eh_generation)); |
87 | | } |
88 | | |
89 | | static void dbg_show_index(struct ext3_extent_idx *ix) |
90 | | { |
91 | | printf("index: block=%u leaf=%u leaf_hi=%u unused=%u\n", |
92 | | ext2fs_le32_to_cpu(ix->ei_block), |
93 | | ext2fs_le32_to_cpu(ix->ei_leaf), |
94 | | ext2fs_le16_to_cpu(ix->ei_leaf_hi), |
95 | | ext2fs_le16_to_cpu(ix->ei_unused)); |
96 | | } |
97 | | |
98 | | static void dbg_show_extent(struct ext3_extent *ex) |
99 | | { |
100 | | printf("extent: block=%u-%u len=%u start=%u start_hi=%u\n", |
101 | | ext2fs_le32_to_cpu(ex->ee_block), |
102 | | ext2fs_le32_to_cpu(ex->ee_block) + |
103 | | ext2fs_le16_to_cpu(ex->ee_len) - 1, |
104 | | ext2fs_le16_to_cpu(ex->ee_len), |
105 | | ext2fs_le32_to_cpu(ex->ee_start), |
106 | | ext2fs_le16_to_cpu(ex->ee_start_hi)); |
107 | | } |
108 | | |
109 | | static void dbg_print_extent(char *desc, struct ext2fs_extent *extent) |
110 | | { |
111 | | if (desc) |
112 | | printf("%s: ", desc); |
113 | | printf("extent: lblk %llu--%llu, len %u, pblk %llu, flags: ", |
114 | | extent->e_lblk, extent->e_lblk + extent->e_len - 1, |
115 | | extent->e_len, extent->e_pblk); |
116 | | if (extent->e_flags & EXT2_EXTENT_FLAGS_LEAF) |
117 | | fputs("LEAF ", stdout); |
118 | | if (extent->e_flags & EXT2_EXTENT_FLAGS_UNINIT) |
119 | | fputs("UNINIT ", stdout); |
120 | | if (extent->e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT) |
121 | | fputs("2ND_VISIT ", stdout); |
122 | | if (!extent->e_flags) |
123 | | fputs("(none)", stdout); |
124 | | fputc('\n', stdout); |
125 | | |
126 | | } |
127 | | |
128 | | static void dump_path(const char *tag, struct ext2_extent_handle *handle, |
129 | | struct extent_path *path) |
130 | | { |
131 | | struct extent_path *ppp = path; |
132 | | printf("%s: level=%d\n", tag, handle->level); |
133 | | |
134 | | do { |
135 | | printf("%s: path=%ld buf=%p entries=%d max_entries=%d left=%d " |
136 | | "visit_num=%d flags=0x%x end_blk=%llu curr=%p(%ld)\n", |
137 | | tag, (ppp - handle->path), ppp->buf, ppp->entries, |
138 | | ppp->max_entries, ppp->left, ppp->visit_num, ppp->flags, |
139 | | ppp->end_blk, ppp->curr, ppp->curr - (void *)ppp->buf); |
140 | | printf(" "); |
141 | | dbg_show_header((struct ext3_extent_header *)ppp->buf); |
142 | | if (ppp->curr) { |
143 | | printf(" "); |
144 | | dbg_show_index(ppp->curr); |
145 | | printf(" "); |
146 | | dbg_show_extent(ppp->curr); |
147 | | } |
148 | | ppp--; |
149 | | } while (ppp >= handle->path); |
150 | | fflush(stdout); |
151 | | |
152 | | return; |
153 | | } |
154 | | |
155 | | #else |
156 | 0 | #define dbg_show_header(eh) do { } while (0) |
157 | | #define dbg_show_index(ix) do { } while (0) |
158 | | #define dbg_show_extent(ex) do { } while (0) |
159 | 0 | #define dbg_print_extent(desc, ex) do { } while (0) |
160 | 0 | #define dump_path(tag, handle, path) do { } while (0) |
161 | | #endif |
162 | | |
163 | | /* |
164 | | * Verify the extent header as being sane |
165 | | */ |
166 | | errcode_t ext2fs_extent_header_verify(void *ptr, int size) |
167 | 0 | { |
168 | 0 | int eh_max, entry_size; |
169 | 0 | struct ext3_extent_header *eh = ptr; |
170 | |
|
171 | 0 | dbg_show_header(eh); |
172 | 0 | if (ext2fs_le16_to_cpu(eh->eh_magic) != EXT3_EXT_MAGIC) |
173 | 0 | return EXT2_ET_EXTENT_HEADER_BAD; |
174 | 0 | if (ext2fs_le16_to_cpu(eh->eh_entries) > ext2fs_le16_to_cpu(eh->eh_max)) |
175 | 0 | return EXT2_ET_EXTENT_HEADER_BAD; |
176 | 0 | if (eh->eh_depth == 0) |
177 | 0 | entry_size = sizeof(struct ext3_extent); |
178 | 0 | else |
179 | 0 | entry_size = sizeof(struct ext3_extent_idx); |
180 | |
|
181 | 0 | eh_max = (size - sizeof(*eh)) / entry_size; |
182 | | /* Allow two extent-sized items at the end of the block, for |
183 | | * ext4_extent_tail with checksum in the future. */ |
184 | 0 | if ((ext2fs_le16_to_cpu(eh->eh_max) > eh_max) || |
185 | 0 | (ext2fs_le16_to_cpu(eh->eh_max) < (eh_max - 2))) |
186 | 0 | return EXT2_ET_EXTENT_HEADER_BAD; |
187 | | |
188 | 0 | return 0; |
189 | 0 | } |
190 | | |
191 | | |
192 | | /* |
193 | | * Begin functions to handle an inode's extent information |
194 | | */ |
195 | | void ext2fs_extent_free(ext2_extent_handle_t handle) |
196 | 0 | { |
197 | 0 | int i; |
198 | |
|
199 | 0 | if (!handle) |
200 | 0 | return; |
201 | | |
202 | 0 | if (handle->path) { |
203 | 0 | for (i = 1; i < handle->max_paths; i++) { |
204 | 0 | if (handle->path[i].buf) |
205 | 0 | ext2fs_free_mem(&handle->path[i].buf); |
206 | 0 | } |
207 | 0 | ext2fs_free_mem(&handle->path); |
208 | 0 | } |
209 | 0 | ext2fs_free_mem(&handle); |
210 | 0 | } |
211 | | |
212 | | errcode_t ext2fs_extent_open(ext2_filsys fs, ext2_ino_t ino, |
213 | | ext2_extent_handle_t *ret_handle) |
214 | 0 | { |
215 | 0 | return ext2fs_extent_open2(fs, ino, NULL, ret_handle); |
216 | 0 | } |
217 | | |
218 | | errcode_t ext2fs_extent_open2(ext2_filsys fs, ext2_ino_t ino, |
219 | | struct ext2_inode *inode, |
220 | | ext2_extent_handle_t *ret_handle) |
221 | 0 | { |
222 | 0 | struct ext2_extent_handle *handle; |
223 | 0 | errcode_t retval; |
224 | 0 | int i; |
225 | 0 | struct ext3_extent_header *eh; |
226 | |
|
227 | 0 | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
228 | | |
229 | 0 | if (!inode) |
230 | 0 | if ((ino == 0) || (ino > fs->super->s_inodes_count)) |
231 | 0 | return EXT2_ET_BAD_INODE_NUM; |
232 | | |
233 | 0 | retval = ext2fs_get_mem(sizeof(struct ext2_extent_handle), &handle); |
234 | 0 | if (retval) |
235 | 0 | return retval; |
236 | 0 | memset(handle, 0, sizeof(struct ext2_extent_handle)); |
237 | |
|
238 | 0 | handle->ino = ino; |
239 | 0 | handle->fs = fs; |
240 | |
|
241 | 0 | if (inode) { |
242 | 0 | handle->inode = inode; |
243 | 0 | } else { |
244 | 0 | handle->inode = &handle->inodebuf; |
245 | 0 | retval = ext2fs_read_inode(fs, ino, handle->inode); |
246 | 0 | if (retval) |
247 | 0 | goto errout; |
248 | 0 | } |
249 | | |
250 | 0 | eh = (struct ext3_extent_header *) &handle->inode->i_block[0]; |
251 | |
|
252 | 0 | for (i=0; i < EXT2_N_BLOCKS; i++) |
253 | 0 | if (handle->inode->i_block[i]) |
254 | 0 | break; |
255 | 0 | if (i >= EXT2_N_BLOCKS) { |
256 | 0 | eh->eh_magic = ext2fs_cpu_to_le16(EXT3_EXT_MAGIC); |
257 | 0 | eh->eh_depth = 0; |
258 | 0 | eh->eh_entries = 0; |
259 | 0 | i = (sizeof(handle->inode->i_block) - sizeof(*eh)) / |
260 | 0 | sizeof(struct ext3_extent); |
261 | 0 | eh->eh_max = ext2fs_cpu_to_le16(i); |
262 | 0 | handle->inode->i_flags |= EXT4_EXTENTS_FL; |
263 | 0 | } |
264 | |
|
265 | 0 | if (!(handle->inode->i_flags & EXT4_EXTENTS_FL)) { |
266 | 0 | retval = EXT2_ET_INODE_NOT_EXTENT; |
267 | 0 | goto errout; |
268 | 0 | } |
269 | | |
270 | 0 | retval = ext2fs_extent_header_verify(eh, sizeof(handle->inode->i_block)); |
271 | 0 | if (retval) |
272 | 0 | goto errout; |
273 | | |
274 | 0 | handle->max_depth = ext2fs_le16_to_cpu(eh->eh_depth); |
275 | 0 | handle->type = ext2fs_le16_to_cpu(eh->eh_magic); |
276 | |
|
277 | 0 | handle->max_paths = handle->max_depth + 1; |
278 | 0 | retval = ext2fs_get_memzero(handle->max_paths * |
279 | 0 | sizeof(struct extent_path), |
280 | 0 | &handle->path); |
281 | 0 | handle->path[0].buf = (char *) handle->inode->i_block; |
282 | |
|
283 | 0 | handle->path[0].left = handle->path[0].entries = |
284 | 0 | ext2fs_le16_to_cpu(eh->eh_entries); |
285 | 0 | handle->path[0].max_entries = ext2fs_le16_to_cpu(eh->eh_max); |
286 | 0 | handle->path[0].curr = 0; |
287 | 0 | handle->path[0].end_blk = |
288 | 0 | (EXT2_I_SIZE(handle->inode) + fs->blocksize - 1) >> |
289 | 0 | EXT2_BLOCK_SIZE_BITS(fs->super); |
290 | 0 | handle->path[0].blk = 0; |
291 | 0 | handle->path[0].visit_num = 1; |
292 | 0 | handle->level = 0; |
293 | 0 | handle->magic = EXT2_ET_MAGIC_EXTENT_HANDLE; |
294 | |
|
295 | 0 | *ret_handle = handle; |
296 | 0 | return 0; |
297 | | |
298 | 0 | errout: |
299 | 0 | ext2fs_extent_free(handle); |
300 | 0 | return retval; |
301 | 0 | } |
302 | | |
303 | | /* |
304 | | * This function is responsible for (optionally) moving through the |
305 | | * extent tree and then returning the current extent |
306 | | */ |
307 | | errcode_t ext2fs_extent_get(ext2_extent_handle_t handle, |
308 | | int flags, struct ext2fs_extent *extent) |
309 | 0 | { |
310 | 0 | struct extent_path *path, *newpath, *tp; |
311 | 0 | struct ext3_extent_header *eh; |
312 | 0 | struct ext3_extent_idx *ix = 0; |
313 | 0 | struct ext3_extent *ex; |
314 | 0 | errcode_t retval; |
315 | 0 | blk64_t blk; |
316 | 0 | blk64_t end_blk; |
317 | 0 | int orig_op, op, l; |
318 | 0 | int failed_csum = 0; |
319 | |
|
320 | 0 | EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); |
321 | | |
322 | 0 | if (!handle->path) |
323 | 0 | return EXT2_ET_NO_CURRENT_NODE; |
324 | | |
325 | 0 | orig_op = op = flags & EXT2_EXTENT_MOVE_MASK; |
326 | |
|
327 | 0 | retry: |
328 | 0 | path = handle->path + handle->level; |
329 | 0 | if ((orig_op == EXT2_EXTENT_NEXT) || |
330 | 0 | (orig_op == EXT2_EXTENT_NEXT_LEAF)) { |
331 | 0 | if (handle->level < handle->max_depth) { |
332 | | /* interior node */ |
333 | 0 | if (path->visit_num == 0) { |
334 | 0 | path->visit_num++; |
335 | 0 | op = EXT2_EXTENT_DOWN; |
336 | 0 | } else if (path->left > 0) |
337 | 0 | op = EXT2_EXTENT_NEXT_SIB; |
338 | 0 | else if (handle->level > 0) |
339 | 0 | op = EXT2_EXTENT_UP; |
340 | 0 | else |
341 | 0 | return EXT2_ET_EXTENT_NO_NEXT; |
342 | 0 | } else { |
343 | | /* leaf node */ |
344 | 0 | if (path->left > 0) |
345 | 0 | op = EXT2_EXTENT_NEXT_SIB; |
346 | 0 | else if (handle->level > 0) |
347 | 0 | op = EXT2_EXTENT_UP; |
348 | 0 | else |
349 | 0 | return EXT2_ET_EXTENT_NO_NEXT; |
350 | 0 | } |
351 | 0 | if (op != EXT2_EXTENT_NEXT_SIB) { |
352 | | #ifdef DEBUG_GET_EXTENT |
353 | | printf("<<<< OP = %s\n", |
354 | | (op == EXT2_EXTENT_DOWN) ? "down" : |
355 | | ((op == EXT2_EXTENT_UP) ? "up" : "unknown")); |
356 | | #endif |
357 | 0 | } |
358 | 0 | } |
359 | | |
360 | 0 | if ((orig_op == EXT2_EXTENT_PREV) || |
361 | 0 | (orig_op == EXT2_EXTENT_PREV_LEAF)) { |
362 | 0 | if (handle->level < handle->max_depth) { |
363 | | /* interior node */ |
364 | 0 | if (path->visit_num > 0 ) { |
365 | | /* path->visit_num = 0; */ |
366 | 0 | op = EXT2_EXTENT_DOWN_AND_LAST; |
367 | 0 | } else if (path->left < path->entries-1) |
368 | 0 | op = EXT2_EXTENT_PREV_SIB; |
369 | 0 | else if (handle->level > 0) |
370 | 0 | op = EXT2_EXTENT_UP; |
371 | 0 | else |
372 | 0 | return EXT2_ET_EXTENT_NO_PREV; |
373 | 0 | } else { |
374 | | /* leaf node */ |
375 | 0 | if (path->left < path->entries-1) |
376 | 0 | op = EXT2_EXTENT_PREV_SIB; |
377 | 0 | else if (handle->level > 0) |
378 | 0 | op = EXT2_EXTENT_UP; |
379 | 0 | else |
380 | 0 | return EXT2_ET_EXTENT_NO_PREV; |
381 | 0 | } |
382 | 0 | if (op != EXT2_EXTENT_PREV_SIB) { |
383 | | #ifdef DEBUG_GET_EXTENT |
384 | | printf("<<<< OP = %s\n", |
385 | | (op == EXT2_EXTENT_DOWN_AND_LAST) ? "down/last" : |
386 | | ((op == EXT2_EXTENT_UP) ? "up" : "unknown")); |
387 | | #endif |
388 | 0 | } |
389 | 0 | } |
390 | | |
391 | 0 | if (orig_op == EXT2_EXTENT_LAST_LEAF) { |
392 | 0 | if ((handle->level < handle->max_depth) && |
393 | 0 | (path->left == 0)) |
394 | 0 | op = EXT2_EXTENT_DOWN; |
395 | 0 | else |
396 | 0 | op = EXT2_EXTENT_LAST_SIB; |
397 | | #ifdef DEBUG_GET_EXTENT |
398 | | printf("<<<< OP = %s\n", |
399 | | (op == EXT2_EXTENT_DOWN) ? "down" : "last_sib"); |
400 | | #endif |
401 | 0 | } |
402 | |
|
403 | 0 | switch (op) { |
404 | 0 | case EXT2_EXTENT_CURRENT: |
405 | 0 | ix = path->curr; |
406 | 0 | break; |
407 | 0 | case EXT2_EXTENT_ROOT: |
408 | 0 | handle->level = 0; |
409 | 0 | path = handle->path + handle->level; |
410 | | /* fallthrough */ |
411 | 0 | case EXT2_EXTENT_FIRST_SIB: |
412 | 0 | path->left = path->entries; |
413 | 0 | path->curr = 0; |
414 | | /* fallthrough */ |
415 | 0 | case EXT2_EXTENT_NEXT_SIB: |
416 | 0 | if (path->left <= 0) |
417 | 0 | return EXT2_ET_EXTENT_NO_NEXT; |
418 | 0 | if (path->curr) { |
419 | 0 | ix = path->curr; |
420 | 0 | ix++; |
421 | 0 | } else { |
422 | 0 | eh = (struct ext3_extent_header *) path->buf; |
423 | 0 | ix = EXT_FIRST_INDEX(eh); |
424 | 0 | } |
425 | 0 | path->left--; |
426 | 0 | path->curr = ix; |
427 | 0 | path->visit_num = 0; |
428 | 0 | break; |
429 | 0 | case EXT2_EXTENT_PREV_SIB: |
430 | 0 | if (!path->curr || |
431 | 0 | path->left+1 >= path->entries) |
432 | 0 | return EXT2_ET_EXTENT_NO_PREV; |
433 | 0 | ix = path->curr; |
434 | 0 | ix--; |
435 | 0 | path->curr = ix; |
436 | 0 | path->left++; |
437 | 0 | if (handle->level < handle->max_depth) |
438 | 0 | path->visit_num = 1; |
439 | 0 | break; |
440 | 0 | case EXT2_EXTENT_LAST_SIB: |
441 | 0 | eh = (struct ext3_extent_header *) path->buf; |
442 | 0 | path->curr = EXT_LAST_EXTENT(eh); |
443 | 0 | ix = path->curr; |
444 | 0 | path->left = 0; |
445 | 0 | path->visit_num = 0; |
446 | 0 | break; |
447 | 0 | case EXT2_EXTENT_UP: |
448 | 0 | if (handle->level <= 0) |
449 | 0 | return EXT2_ET_EXTENT_NO_UP; |
450 | 0 | handle->level--; |
451 | 0 | path--; |
452 | 0 | ix = path->curr; |
453 | 0 | if ((orig_op == EXT2_EXTENT_PREV) || |
454 | 0 | (orig_op == EXT2_EXTENT_PREV_LEAF)) |
455 | 0 | path->visit_num = 0; |
456 | 0 | break; |
457 | 0 | case EXT2_EXTENT_DOWN: |
458 | 0 | case EXT2_EXTENT_DOWN_AND_LAST: |
459 | 0 | if (!path->curr ||(handle->level >= handle->max_depth)) |
460 | 0 | return EXT2_ET_EXTENT_NO_DOWN; |
461 | | |
462 | 0 | ix = path->curr; |
463 | 0 | newpath = path + 1; |
464 | 0 | if (!newpath->buf) { |
465 | 0 | retval = ext2fs_get_mem(handle->fs->blocksize, |
466 | 0 | &newpath->buf); |
467 | 0 | if (retval) |
468 | 0 | return retval; |
469 | 0 | } |
470 | 0 | blk = ext2fs_le32_to_cpu(ix->ei_leaf) + |
471 | 0 | ((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32); |
472 | 0 | for (l = handle->level, tp = path; l > 0; l--, tp--) { |
473 | 0 | if (blk == tp->blk) |
474 | 0 | return EXT2_ET_EXTENT_CYCLE; |
475 | 0 | } |
476 | 0 | newpath->blk = blk; |
477 | 0 | if ((handle->fs->flags & EXT2_FLAG_IMAGE_FILE) && |
478 | 0 | (handle->fs->io != handle->fs->image_io)) |
479 | 0 | memset(newpath->buf, 0, handle->fs->blocksize); |
480 | 0 | else { |
481 | 0 | retval = io_channel_read_blk64(handle->fs->io, |
482 | 0 | blk, 1, newpath->buf); |
483 | 0 | if (retval) |
484 | 0 | return retval; |
485 | 0 | } |
486 | 0 | handle->level++; |
487 | |
|
488 | 0 | eh = (struct ext3_extent_header *) newpath->buf; |
489 | |
|
490 | 0 | retval = ext2fs_extent_header_verify(eh, handle->fs->blocksize); |
491 | 0 | if (retval) { |
492 | 0 | handle->level--; |
493 | 0 | return retval; |
494 | 0 | } |
495 | | |
496 | 0 | if (!(handle->fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) && |
497 | 0 | !ext2fs_extent_block_csum_verify(handle->fs, handle->ino, |
498 | 0 | eh)) |
499 | 0 | failed_csum = 1; |
500 | |
|
501 | 0 | newpath->left = newpath->entries = |
502 | 0 | ext2fs_le16_to_cpu(eh->eh_entries); |
503 | 0 | newpath->max_entries = ext2fs_le16_to_cpu(eh->eh_max); |
504 | | |
505 | | /* Make sure there is at least one extent present */ |
506 | 0 | if (newpath->left <= 0) |
507 | 0 | return EXT2_ET_EXTENT_NO_DOWN; |
508 | | |
509 | 0 | if (path->left > 0) { |
510 | 0 | ix++; |
511 | 0 | newpath->end_blk = ext2fs_le32_to_cpu(ix->ei_block); |
512 | 0 | } else |
513 | 0 | newpath->end_blk = path->end_blk; |
514 | |
|
515 | 0 | path = newpath; |
516 | 0 | if (op == EXT2_EXTENT_DOWN) { |
517 | 0 | ix = EXT_FIRST_INDEX((struct ext3_extent_header *) eh); |
518 | 0 | path->curr = ix; |
519 | 0 | path->left = path->entries - 1; |
520 | 0 | path->visit_num = 0; |
521 | 0 | } else { |
522 | 0 | ix = EXT_LAST_INDEX((struct ext3_extent_header *) eh); |
523 | 0 | path->curr = ix; |
524 | 0 | path->left = 0; |
525 | 0 | if (handle->level < handle->max_depth) |
526 | 0 | path->visit_num = 1; |
527 | 0 | } |
528 | | #ifdef DEBUG_GET_EXTENT |
529 | | printf("Down to level %d/%d, end_blk=%llu\n", |
530 | | handle->level, handle->max_depth, |
531 | | path->end_blk); |
532 | | #endif |
533 | 0 | break; |
534 | 0 | default: |
535 | 0 | return EXT2_ET_OP_NOT_SUPPORTED; |
536 | 0 | } |
537 | | |
538 | 0 | if (!ix) |
539 | 0 | return EXT2_ET_NO_CURRENT_NODE; |
540 | | |
541 | 0 | extent->e_flags = 0; |
542 | | #ifdef DEBUG_GET_EXTENT |
543 | | printf("(Left %d)\n", path->left); |
544 | | #endif |
545 | |
|
546 | 0 | if (handle->level == handle->max_depth) { |
547 | 0 | ex = (struct ext3_extent *) ix; |
548 | |
|
549 | 0 | extent->e_pblk = ext2fs_le32_to_cpu(ex->ee_start) + |
550 | 0 | ((__u64) ext2fs_le16_to_cpu(ex->ee_start_hi) << 32); |
551 | 0 | extent->e_lblk = ext2fs_le32_to_cpu(ex->ee_block); |
552 | 0 | extent->e_len = ext2fs_le16_to_cpu(ex->ee_len); |
553 | 0 | extent->e_flags |= EXT2_EXTENT_FLAGS_LEAF; |
554 | 0 | if (extent->e_len > EXT_INIT_MAX_LEN) { |
555 | 0 | extent->e_len -= EXT_INIT_MAX_LEN; |
556 | 0 | extent->e_flags |= EXT2_EXTENT_FLAGS_UNINIT; |
557 | 0 | } |
558 | 0 | } else { |
559 | 0 | extent->e_pblk = ext2fs_le32_to_cpu(ix->ei_leaf) + |
560 | 0 | ((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32); |
561 | 0 | extent->e_lblk = ext2fs_le32_to_cpu(ix->ei_block); |
562 | 0 | if (path->left > 0) { |
563 | 0 | ix++; |
564 | 0 | end_blk = ext2fs_le32_to_cpu(ix->ei_block); |
565 | 0 | } else |
566 | 0 | end_blk = path->end_blk; |
567 | |
|
568 | 0 | extent->e_len = end_blk - extent->e_lblk; |
569 | 0 | } |
570 | 0 | if (path->visit_num) |
571 | 0 | extent->e_flags |= EXT2_EXTENT_FLAGS_SECOND_VISIT; |
572 | |
|
573 | 0 | if (((orig_op == EXT2_EXTENT_NEXT_LEAF) || |
574 | 0 | (orig_op == EXT2_EXTENT_PREV_LEAF)) && |
575 | 0 | (handle->level != handle->max_depth)) |
576 | 0 | goto retry; |
577 | | |
578 | 0 | if ((orig_op == EXT2_EXTENT_LAST_LEAF) && |
579 | 0 | ((handle->level != handle->max_depth) || |
580 | 0 | (path->left != 0))) |
581 | 0 | goto retry; |
582 | | |
583 | 0 | if (failed_csum) |
584 | 0 | return EXT2_ET_EXTENT_CSUM_INVALID; |
585 | | |
586 | 0 | return 0; |
587 | 0 | } |
588 | | |
589 | | static errcode_t update_path(ext2_extent_handle_t handle) |
590 | 0 | { |
591 | 0 | blk64_t blk; |
592 | 0 | errcode_t retval; |
593 | 0 | struct ext3_extent_idx *ix; |
594 | 0 | struct ext3_extent_header *eh; |
595 | |
|
596 | 0 | if (handle->level == 0) { |
597 | 0 | retval = ext2fs_write_inode(handle->fs, handle->ino, |
598 | 0 | handle->inode); |
599 | 0 | } else { |
600 | 0 | ix = handle->path[handle->level - 1].curr; |
601 | 0 | blk = ext2fs_le32_to_cpu(ix->ei_leaf) + |
602 | 0 | ((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32); |
603 | | |
604 | | /* then update the checksum */ |
605 | 0 | eh = (struct ext3_extent_header *) |
606 | 0 | handle->path[handle->level].buf; |
607 | 0 | retval = ext2fs_extent_block_csum_set(handle->fs, handle->ino, |
608 | 0 | eh); |
609 | 0 | if (retval) |
610 | 0 | return retval; |
611 | | |
612 | 0 | retval = io_channel_write_blk64(handle->fs->io, |
613 | 0 | blk, 1, handle->path[handle->level].buf); |
614 | 0 | } |
615 | 0 | return retval; |
616 | 0 | } |
617 | | |
618 | | #if 0 |
619 | | errcode_t ext2fs_extent_save_path(ext2_extent_handle_t handle, |
620 | | ext2_extent_path_t *ret_path) |
621 | | { |
622 | | ext2_extent_path_t save_path; |
623 | | struct ext2fs_extent extent; |
624 | | struct ext2_extent_info info; |
625 | | errcode_t retval; |
626 | | |
627 | | retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); |
628 | | if (retval) |
629 | | return retval; |
630 | | |
631 | | retval = ext2fs_extent_get_info(handle, &info); |
632 | | if (retval) |
633 | | return retval; |
634 | | |
635 | | retval = ext2fs_get_mem(sizeof(struct ext2_extent_path), &save_path); |
636 | | if (retval) |
637 | | return retval; |
638 | | memset(save_path, 0, sizeof(struct ext2_extent_path)); |
639 | | |
640 | | save_path->magic = EXT2_ET_MAGIC_EXTENT_PATH; |
641 | | save_path->leaf_height = info.max_depth - info.curr_level - 1; |
642 | | save_path->lblk = extent.e_lblk; |
643 | | |
644 | | *ret_path = save_path; |
645 | | return 0; |
646 | | } |
647 | | |
648 | | errcode_t ext2fs_extent_free_path(ext2_extent_path_t path) |
649 | | { |
650 | | EXT2_CHECK_MAGIC(path, EXT2_ET_MAGIC_EXTENT_PATH); |
651 | | |
652 | | ext2fs_free_mem(&path); |
653 | | return 0; |
654 | | } |
655 | | #endif |
656 | | |
657 | | /* |
658 | | * Go to the node at leaf_level which contains logical block blk. |
659 | | * |
660 | | * leaf_level is height from the leaf node level, i.e. |
661 | | * leaf_level 0 is at leaf node, leaf_level 1 is 1 above etc. |
662 | | * |
663 | | * If "blk" has no mapping (hole) then handle is left at last |
664 | | * extent before blk. |
665 | | */ |
666 | | errcode_t ext2fs_extent_goto2(ext2_extent_handle_t handle, |
667 | | int leaf_level, blk64_t blk) |
668 | 0 | { |
669 | 0 | struct ext2fs_extent extent; |
670 | 0 | errcode_t retval; |
671 | |
|
672 | 0 | retval = ext2fs_extent_get(handle, EXT2_EXTENT_ROOT, &extent); |
673 | 0 | if (retval) { |
674 | 0 | if (retval == EXT2_ET_EXTENT_NO_NEXT) |
675 | 0 | retval = EXT2_ET_EXTENT_NOT_FOUND; |
676 | 0 | return retval; |
677 | 0 | } |
678 | | |
679 | 0 | if (leaf_level > handle->max_depth) { |
680 | | #ifdef DEBUG |
681 | | printf("leaf level %d greater than tree depth %d\n", |
682 | | leaf_level, handle->max_depth); |
683 | | #endif |
684 | 0 | return EXT2_ET_OP_NOT_SUPPORTED; |
685 | 0 | } |
686 | | |
687 | | #ifdef DEBUG |
688 | | printf("goto extent ino %u, level %d, %llu\n", handle->ino, |
689 | | leaf_level, blk); |
690 | | #endif |
691 | | |
692 | | #ifdef DEBUG_GOTO_EXTENTS |
693 | | dbg_print_extent("root", &extent); |
694 | | #endif |
695 | 0 | while (1) { |
696 | 0 | if (handle->max_depth - handle->level == leaf_level) { |
697 | | /* block is in this &extent */ |
698 | 0 | if ((blk >= extent.e_lblk) && |
699 | 0 | (blk < extent.e_lblk + extent.e_len)) |
700 | 0 | return 0; |
701 | 0 | if (blk < extent.e_lblk) { |
702 | 0 | retval = ext2fs_extent_get(handle, |
703 | 0 | EXT2_EXTENT_PREV_SIB, |
704 | 0 | &extent); |
705 | 0 | return EXT2_ET_EXTENT_NOT_FOUND; |
706 | 0 | } |
707 | 0 | retval = ext2fs_extent_get(handle, |
708 | 0 | EXT2_EXTENT_NEXT_SIB, |
709 | 0 | &extent); |
710 | 0 | if (retval == EXT2_ET_EXTENT_NO_NEXT) |
711 | 0 | return EXT2_ET_EXTENT_NOT_FOUND; |
712 | 0 | if (retval) |
713 | 0 | return retval; |
714 | 0 | continue; |
715 | 0 | } |
716 | | |
717 | 0 | retval = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_SIB, |
718 | 0 | &extent); |
719 | 0 | if (retval == EXT2_ET_EXTENT_NO_NEXT) |
720 | 0 | goto go_down; |
721 | 0 | if (retval) |
722 | 0 | return retval; |
723 | | |
724 | | #ifdef DEBUG_GOTO_EXTENTS |
725 | | dbg_print_extent("next", &extent); |
726 | | #endif |
727 | 0 | if (blk == extent.e_lblk) |
728 | 0 | goto go_down; |
729 | 0 | if (blk > extent.e_lblk) |
730 | 0 | continue; |
731 | | |
732 | 0 | retval = ext2fs_extent_get(handle, EXT2_EXTENT_PREV_SIB, |
733 | 0 | &extent); |
734 | 0 | if (retval) |
735 | 0 | return retval; |
736 | | |
737 | | #ifdef DEBUG_GOTO_EXTENTS |
738 | | dbg_print_extent("prev", &extent); |
739 | | #endif |
740 | | |
741 | 0 | go_down: |
742 | 0 | retval = ext2fs_extent_get(handle, EXT2_EXTENT_DOWN, |
743 | 0 | &extent); |
744 | 0 | if (retval) |
745 | 0 | return retval; |
746 | |
|
747 | | #ifdef DEBUG_GOTO_EXTENTS |
748 | | dbg_print_extent("down", &extent); |
749 | | #endif |
750 | 0 | } |
751 | 0 | } |
752 | | |
753 | | errcode_t ext2fs_extent_goto(ext2_extent_handle_t handle, |
754 | | blk64_t blk) |
755 | 0 | { |
756 | 0 | return ext2fs_extent_goto2(handle, 0, blk); |
757 | 0 | } |
758 | | |
759 | | /* |
760 | | * Traverse back up to root fixing parents of current node as needed. |
761 | | * |
762 | | * If we changed start of first entry in a node, fix parent index start |
763 | | * and so on. |
764 | | * |
765 | | * Safe to call for any position in node; if not at the first entry, |
766 | | * it will simply return. |
767 | | * |
768 | | * Note a subtlety of this function -- if there happen to be two extents |
769 | | * mapping the same lblk and someone calls fix_parents on the second of the two |
770 | | * extents, the position of the extent handle after the call will be the second |
771 | | * extent if nothing happened, or the first extent if something did. A caller |
772 | | * in this situation must use ext2fs_extent_goto() after calling this function. |
773 | | * Or simply don't map the same lblk with two extents, ever. |
774 | | */ |
775 | | errcode_t ext2fs_extent_fix_parents(ext2_extent_handle_t handle) |
776 | 0 | { |
777 | 0 | int retval = 0; |
778 | 0 | int orig_height; |
779 | 0 | blk64_t start; |
780 | 0 | struct extent_path *path; |
781 | 0 | struct ext2fs_extent extent; |
782 | 0 | struct ext2_extent_info info; |
783 | |
|
784 | 0 | EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); |
785 | | |
786 | 0 | if (!(handle->fs->flags & EXT2_FLAG_RW)) |
787 | 0 | return EXT2_ET_RO_FILSYS; |
788 | | |
789 | 0 | if (!handle->path) |
790 | 0 | return EXT2_ET_NO_CURRENT_NODE; |
791 | | |
792 | 0 | path = handle->path + handle->level; |
793 | 0 | if (!path->curr) |
794 | 0 | return EXT2_ET_NO_CURRENT_NODE; |
795 | | |
796 | 0 | retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); |
797 | 0 | if (retval) |
798 | 0 | goto done; |
799 | | |
800 | | /* modified node's start block */ |
801 | 0 | start = extent.e_lblk; |
802 | |
|
803 | 0 | if ((retval = ext2fs_extent_get_info(handle, &info))) |
804 | 0 | return retval; |
805 | 0 | orig_height = info.max_depth - info.curr_level; |
806 | | |
807 | | /* traverse up until index not first, or startblk matches, or top */ |
808 | 0 | while (handle->level > 0 && |
809 | 0 | (path->left == path->entries - 1)) { |
810 | 0 | retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP, &extent); |
811 | 0 | if (retval) |
812 | 0 | goto done; |
813 | 0 | if (extent.e_lblk == start) |
814 | 0 | break; |
815 | 0 | path = handle->path + handle->level; |
816 | 0 | extent.e_len += (extent.e_lblk - start); |
817 | 0 | extent.e_lblk = start; |
818 | 0 | retval = ext2fs_extent_replace(handle, 0, &extent); |
819 | 0 | if (retval) |
820 | 0 | goto done; |
821 | 0 | update_path(handle); |
822 | 0 | } |
823 | | |
824 | | /* put handle back to where we started */ |
825 | 0 | retval = ext2fs_extent_goto2(handle, orig_height, start); |
826 | 0 | done: |
827 | 0 | return retval; |
828 | 0 | } |
829 | | |
830 | | errcode_t ext2fs_extent_replace(ext2_extent_handle_t handle, |
831 | | int flags EXT2FS_ATTR((unused)), |
832 | | struct ext2fs_extent *extent) |
833 | 0 | { |
834 | 0 | struct extent_path *path; |
835 | 0 | struct ext3_extent_idx *ix; |
836 | 0 | struct ext3_extent *ex; |
837 | |
|
838 | 0 | EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); |
839 | | |
840 | 0 | if (!(handle->fs->flags & EXT2_FLAG_RW)) |
841 | 0 | return EXT2_ET_RO_FILSYS; |
842 | | |
843 | 0 | if (!handle->path) |
844 | 0 | return EXT2_ET_NO_CURRENT_NODE; |
845 | | |
846 | 0 | path = handle->path + handle->level; |
847 | 0 | if (!path->curr) |
848 | 0 | return EXT2_ET_NO_CURRENT_NODE; |
849 | | |
850 | | #ifdef DEBUG |
851 | | printf("extent replace: %u ", handle->ino); |
852 | | dbg_print_extent(0, extent); |
853 | | #endif |
854 | | |
855 | 0 | if (handle->level == handle->max_depth) { |
856 | 0 | ex = path->curr; |
857 | |
|
858 | 0 | ex->ee_block = ext2fs_cpu_to_le32(extent->e_lblk); |
859 | 0 | ex->ee_start = ext2fs_cpu_to_le32(extent->e_pblk & 0xFFFFFFFF); |
860 | 0 | ex->ee_start_hi = ext2fs_cpu_to_le16(extent->e_pblk >> 32); |
861 | 0 | if (extent->e_flags & EXT2_EXTENT_FLAGS_UNINIT) { |
862 | 0 | if (extent->e_len > EXT_UNINIT_MAX_LEN) |
863 | 0 | return EXT2_ET_EXTENT_INVALID_LENGTH; |
864 | 0 | ex->ee_len = ext2fs_cpu_to_le16(extent->e_len + |
865 | 0 | EXT_INIT_MAX_LEN); |
866 | 0 | } else { |
867 | 0 | if (extent->e_len > EXT_INIT_MAX_LEN) |
868 | 0 | return EXT2_ET_EXTENT_INVALID_LENGTH; |
869 | 0 | ex->ee_len = ext2fs_cpu_to_le16(extent->e_len); |
870 | 0 | } |
871 | 0 | } else { |
872 | 0 | ix = path->curr; |
873 | |
|
874 | 0 | ix->ei_leaf = ext2fs_cpu_to_le32(extent->e_pblk & 0xFFFFFFFF); |
875 | 0 | ix->ei_leaf_hi = ext2fs_cpu_to_le16(extent->e_pblk >> 32); |
876 | 0 | ix->ei_block = ext2fs_cpu_to_le32(extent->e_lblk); |
877 | 0 | ix->ei_unused = 0; |
878 | 0 | } |
879 | 0 | update_path(handle); |
880 | 0 | return 0; |
881 | 0 | } |
882 | | |
883 | | static int splitting_at_eof(struct ext2_extent_handle *handle, |
884 | | struct extent_path *path) |
885 | 0 | { |
886 | 0 | struct extent_path *ppp = path; |
887 | 0 | dump_path(__func__, handle, path); |
888 | |
|
889 | 0 | if (handle->level == 0) |
890 | 0 | return 0; |
891 | | |
892 | 0 | do { |
893 | 0 | if (ppp->left) |
894 | 0 | return 0; |
895 | 0 | ppp--; |
896 | 0 | } while (ppp >= handle->path); |
897 | | |
898 | 0 | return 1; |
899 | 0 | } |
900 | | |
901 | | /* |
902 | | * allocate a new block, move half the current node to it, and update parent |
903 | | * |
904 | | * handle will be left pointing at original record. |
905 | | */ |
906 | | static errcode_t extent_node_split(ext2_extent_handle_t handle, |
907 | | int expand_allowed) |
908 | 0 | { |
909 | 0 | errcode_t retval = 0; |
910 | 0 | blk64_t new_node_pblk; |
911 | 0 | blk64_t new_node_start; |
912 | 0 | blk64_t orig_lblk; |
913 | 0 | blk64_t goal_blk = 0; |
914 | 0 | int orig_height; |
915 | 0 | char *block_buf = NULL; |
916 | 0 | struct ext2fs_extent extent; |
917 | 0 | struct extent_path *path, *newpath = 0; |
918 | 0 | struct ext3_extent_header *eh, *neweh; |
919 | 0 | int tocopy; |
920 | 0 | int new_root = 0; |
921 | 0 | struct ext2_extent_info info; |
922 | 0 | int no_balance; |
923 | | |
924 | | /* basic sanity */ |
925 | 0 | EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); |
926 | | |
927 | 0 | if (!(handle->fs->flags & EXT2_FLAG_RW)) |
928 | 0 | return EXT2_ET_RO_FILSYS; |
929 | | |
930 | 0 | if (!handle->path) |
931 | 0 | return EXT2_ET_NO_CURRENT_NODE; |
932 | | |
933 | | #ifdef DEBUG |
934 | | printf("splitting node at level %d\n", handle->level); |
935 | | #endif |
936 | 0 | retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); |
937 | 0 | if (retval) |
938 | 0 | goto done; |
939 | | |
940 | 0 | retval = ext2fs_extent_get_info(handle, &info); |
941 | 0 | if (retval) |
942 | 0 | goto done; |
943 | | |
944 | | /* save the position we were originally splitting... */ |
945 | 0 | orig_height = info.max_depth - info.curr_level; |
946 | 0 | orig_lblk = extent.e_lblk; |
947 | | |
948 | | /* Try to put the index block before the first extent */ |
949 | 0 | path = handle->path + handle->level; |
950 | 0 | eh = (struct ext3_extent_header *) path->buf; |
951 | 0 | if (handle->level == handle->max_depth) { |
952 | 0 | struct ext3_extent *ex; |
953 | |
|
954 | 0 | ex = EXT_FIRST_EXTENT(eh); |
955 | 0 | goal_blk = ext2fs_le32_to_cpu(ex->ee_start) + |
956 | 0 | ((__u64) ext2fs_le16_to_cpu(ex->ee_start_hi) << 32); |
957 | 0 | } else { |
958 | 0 | struct ext3_extent_idx *ix; |
959 | |
|
960 | 0 | ix = EXT_FIRST_INDEX(eh); |
961 | 0 | goal_blk = ext2fs_le32_to_cpu(ix->ei_leaf) + |
962 | 0 | ((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32); |
963 | 0 | } |
964 | 0 | goal_blk -= EXT2FS_CLUSTER_RATIO(handle->fs); |
965 | 0 | goal_blk &= ~EXT2FS_CLUSTER_MASK(handle->fs); |
966 | | |
967 | | /* Is there room in the parent for a new entry? */ |
968 | 0 | if (handle->level && |
969 | 0 | (handle->path[handle->level - 1].entries >= |
970 | 0 | handle->path[handle->level - 1].max_entries)) { |
971 | |
|
972 | | #ifdef DEBUG |
973 | | printf("parent level %d full; splitting it too\n", |
974 | | handle->level - 1); |
975 | | #endif |
976 | | /* split the parent */ |
977 | 0 | retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP, &extent); |
978 | 0 | if (retval) |
979 | 0 | goto done; |
980 | | |
981 | 0 | retval = extent_node_split(handle, expand_allowed); |
982 | 0 | if (retval) |
983 | 0 | goto done; |
984 | | |
985 | | /* get handle back to our original split position */ |
986 | 0 | retval = ext2fs_extent_goto2(handle, orig_height, orig_lblk); |
987 | 0 | if (retval) |
988 | 0 | goto done; |
989 | 0 | } |
990 | | |
991 | | /* At this point, parent should have room for this split */ |
992 | 0 | path = handle->path + handle->level; |
993 | 0 | if (!path->curr) |
994 | 0 | return EXT2_ET_NO_CURRENT_NODE; |
995 | | |
996 | | /* |
997 | | * Normally, we try to split a full node in half. This doesn't turn |
998 | | * out so well if we're tacking extents on the end of the file because |
999 | | * then we're stuck with a tree of half-full extent blocks. This of |
1000 | | * course doesn't apply to the root level. |
1001 | | */ |
1002 | 0 | no_balance = expand_allowed ? splitting_at_eof(handle, path) : 0; |
1003 | | |
1004 | | /* extent header of the current node we'll split */ |
1005 | 0 | eh = (struct ext3_extent_header *)path->buf; |
1006 | | |
1007 | | /* splitting root level means moving them all out */ |
1008 | 0 | if (handle->level == 0) { |
1009 | 0 | new_root = 1; |
1010 | 0 | tocopy = ext2fs_le16_to_cpu(eh->eh_entries); |
1011 | 0 | retval = ext2fs_get_memzero((handle->max_paths + 1) * |
1012 | 0 | sizeof(struct extent_path), |
1013 | 0 | &newpath); |
1014 | 0 | if (retval) |
1015 | 0 | goto done; |
1016 | 0 | } else { |
1017 | 0 | if (no_balance) |
1018 | 0 | tocopy = 1; |
1019 | 0 | else |
1020 | 0 | tocopy = ext2fs_le16_to_cpu(eh->eh_entries) / 2; |
1021 | 0 | } |
1022 | | |
1023 | | #ifdef DEBUG |
1024 | | printf("will copy out %d of %d entries at level %d\n", |
1025 | | tocopy, ext2fs_le16_to_cpu(eh->eh_entries), |
1026 | | handle->level); |
1027 | | #endif |
1028 | | |
1029 | 0 | if (!tocopy && !no_balance) { |
1030 | | #ifdef DEBUG |
1031 | | printf("Nothing to copy to new block!\n"); |
1032 | | #endif |
1033 | 0 | retval = EXT2_ET_CANT_SPLIT_EXTENT; |
1034 | 0 | goto done; |
1035 | 0 | } |
1036 | | |
1037 | | /* first we need a new block, or can do nothing. */ |
1038 | 0 | block_buf = malloc(handle->fs->blocksize); |
1039 | 0 | if (!block_buf) { |
1040 | 0 | retval = ENOMEM; |
1041 | 0 | goto done; |
1042 | 0 | } |
1043 | | |
1044 | 0 | if (!goal_blk) |
1045 | 0 | goal_blk = ext2fs_find_inode_goal(handle->fs, handle->ino, |
1046 | 0 | handle->inode, 0); |
1047 | 0 | retval = ext2fs_alloc_block2(handle->fs, goal_blk, block_buf, |
1048 | 0 | &new_node_pblk); |
1049 | 0 | if (retval) |
1050 | 0 | goto done; |
1051 | | |
1052 | | #ifdef DEBUG |
1053 | | printf("will copy to new node at block %lu\n", |
1054 | | (unsigned long) new_node_pblk); |
1055 | | #endif |
1056 | | |
1057 | | /* Copy data into new block buffer */ |
1058 | | /* First the header for the new block... */ |
1059 | 0 | neweh = (struct ext3_extent_header *) block_buf; |
1060 | 0 | memcpy(neweh, eh, sizeof(struct ext3_extent_header)); |
1061 | 0 | neweh->eh_entries = ext2fs_cpu_to_le16(tocopy); |
1062 | 0 | neweh->eh_max = ext2fs_cpu_to_le16((handle->fs->blocksize - |
1063 | 0 | sizeof(struct ext3_extent_header)) / |
1064 | 0 | sizeof(struct ext3_extent)); |
1065 | | |
1066 | | /* then the entries for the new block... */ |
1067 | 0 | memcpy(EXT_FIRST_INDEX(neweh), |
1068 | 0 | EXT_FIRST_INDEX(eh) + |
1069 | 0 | (ext2fs_le16_to_cpu(eh->eh_entries) - tocopy), |
1070 | 0 | sizeof(struct ext3_extent_idx) * tocopy); |
1071 | |
|
1072 | 0 | new_node_start = ext2fs_le32_to_cpu(EXT_FIRST_INDEX(neweh)->ei_block); |
1073 | | |
1074 | | /* then update the checksum */ |
1075 | 0 | retval = ext2fs_extent_block_csum_set(handle->fs, handle->ino, neweh); |
1076 | 0 | if (retval) |
1077 | 0 | goto done; |
1078 | | |
1079 | | /* ...and write the new node block out to disk. */ |
1080 | 0 | retval = io_channel_write_blk64(handle->fs->io, new_node_pblk, 1, |
1081 | 0 | block_buf); |
1082 | |
|
1083 | 0 | if (retval) |
1084 | 0 | goto done; |
1085 | | |
1086 | | /* OK! we've created the new node; now adjust the tree */ |
1087 | | |
1088 | | /* current path now has fewer active entries, we copied some out */ |
1089 | 0 | if (handle->level == 0) { |
1090 | 0 | memcpy(newpath, path, |
1091 | 0 | sizeof(struct extent_path) * handle->max_paths); |
1092 | 0 | handle->path = newpath; |
1093 | 0 | newpath = path; |
1094 | 0 | path = handle->path; |
1095 | 0 | path->entries = 1; |
1096 | 0 | path->left = path->max_entries - 1; |
1097 | 0 | handle->max_depth++; |
1098 | 0 | handle->max_paths++; |
1099 | 0 | eh->eh_depth = ext2fs_cpu_to_le16(handle->max_depth); |
1100 | 0 | } else { |
1101 | 0 | path->entries -= tocopy; |
1102 | 0 | path->left -= tocopy; |
1103 | 0 | } |
1104 | |
|
1105 | 0 | eh->eh_entries = ext2fs_cpu_to_le16(path->entries); |
1106 | | /* this writes out the node, incl. the modified header */ |
1107 | 0 | retval = update_path(handle); |
1108 | 0 | if (retval) |
1109 | 0 | goto done; |
1110 | | |
1111 | | /* now go up and insert/replace index for new node we created */ |
1112 | 0 | if (new_root) { |
1113 | 0 | retval = ext2fs_extent_get(handle, EXT2_EXTENT_FIRST_SIB, &extent); |
1114 | 0 | if (retval) |
1115 | 0 | goto done; |
1116 | | |
1117 | 0 | extent.e_lblk = new_node_start; |
1118 | 0 | extent.e_pblk = new_node_pblk; |
1119 | 0 | extent.e_len = handle->path[0].end_blk - extent.e_lblk; |
1120 | 0 | retval = ext2fs_extent_replace(handle, 0, &extent); |
1121 | 0 | if (retval) |
1122 | 0 | goto done; |
1123 | 0 | } else { |
1124 | 0 | __u32 new_node_length; |
1125 | |
|
1126 | 0 | retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP, &extent); |
1127 | | /* will insert after this one; it's length is shorter now */ |
1128 | 0 | new_node_length = new_node_start - extent.e_lblk; |
1129 | 0 | extent.e_len -= new_node_length; |
1130 | 0 | retval = ext2fs_extent_replace(handle, 0, &extent); |
1131 | 0 | if (retval) |
1132 | 0 | goto done; |
1133 | | |
1134 | | /* now set up the new extent and insert it */ |
1135 | 0 | extent.e_lblk = new_node_start; |
1136 | 0 | extent.e_pblk = new_node_pblk; |
1137 | 0 | extent.e_len = new_node_length; |
1138 | 0 | retval = ext2fs_extent_insert(handle, EXT2_EXTENT_INSERT_AFTER, &extent); |
1139 | 0 | if (retval) |
1140 | 0 | goto done; |
1141 | 0 | } |
1142 | | |
1143 | | /* get handle back to our original position */ |
1144 | 0 | retval = ext2fs_extent_goto2(handle, orig_height, orig_lblk); |
1145 | 0 | if (retval) |
1146 | 0 | goto done; |
1147 | | |
1148 | | /* new node hooked in, so update inode block count (do this here?) */ |
1149 | 0 | ext2fs_iblk_add_blocks(handle->fs, handle->inode, 1); |
1150 | 0 | retval = ext2fs_write_inode(handle->fs, handle->ino, |
1151 | 0 | handle->inode); |
1152 | 0 | if (retval) |
1153 | 0 | goto done; |
1154 | | |
1155 | 0 | done: |
1156 | 0 | if (newpath) |
1157 | 0 | ext2fs_free_mem(&newpath); |
1158 | 0 | free(block_buf); |
1159 | |
|
1160 | 0 | return retval; |
1161 | 0 | } |
1162 | | |
1163 | | errcode_t ext2fs_extent_node_split(ext2_extent_handle_t handle) |
1164 | 0 | { |
1165 | 0 | return extent_node_split(handle, 0); |
1166 | 0 | } |
1167 | | |
1168 | | errcode_t ext2fs_extent_insert(ext2_extent_handle_t handle, int flags, |
1169 | | struct ext2fs_extent *extent) |
1170 | 0 | { |
1171 | 0 | struct extent_path *path; |
1172 | 0 | struct ext3_extent_idx *ix; |
1173 | 0 | struct ext3_extent_header *eh; |
1174 | 0 | errcode_t retval; |
1175 | |
|
1176 | 0 | EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); |
1177 | | |
1178 | 0 | if (!(handle->fs->flags & EXT2_FLAG_RW)) |
1179 | 0 | return EXT2_ET_RO_FILSYS; |
1180 | | |
1181 | 0 | if (!handle->path) |
1182 | 0 | return EXT2_ET_NO_CURRENT_NODE; |
1183 | | |
1184 | | #ifdef DEBUG |
1185 | | printf("extent insert: %u ", handle->ino); |
1186 | | dbg_print_extent(0, extent); |
1187 | | #endif |
1188 | | |
1189 | 0 | path = handle->path + handle->level; |
1190 | |
|
1191 | 0 | if (path->entries >= path->max_entries) { |
1192 | 0 | if (flags & EXT2_EXTENT_INSERT_NOSPLIT) { |
1193 | 0 | return EXT2_ET_CANT_INSERT_EXTENT; |
1194 | 0 | } else { |
1195 | | #ifdef DEBUG |
1196 | | printf("node full (level %d) - splitting\n", |
1197 | | handle->level); |
1198 | | #endif |
1199 | 0 | retval = extent_node_split(handle, 1); |
1200 | 0 | if (retval) |
1201 | 0 | return retval; |
1202 | 0 | path = handle->path + handle->level; |
1203 | 0 | } |
1204 | 0 | } |
1205 | | |
1206 | 0 | eh = (struct ext3_extent_header *) path->buf; |
1207 | 0 | if (path->curr) { |
1208 | 0 | ix = path->curr; |
1209 | 0 | if (flags & EXT2_EXTENT_INSERT_AFTER) { |
1210 | 0 | ix++; |
1211 | 0 | path->left--; |
1212 | 0 | } |
1213 | 0 | } else { |
1214 | 0 | ix = EXT_FIRST_INDEX(eh); |
1215 | 0 | path->left = -1; |
1216 | 0 | } |
1217 | |
|
1218 | 0 | path->curr = ix; |
1219 | |
|
1220 | 0 | if (path->left >= 0) |
1221 | 0 | memmove(ix + 1, ix, |
1222 | 0 | (path->left+1) * sizeof(struct ext3_extent_idx)); |
1223 | 0 | path->left++; |
1224 | 0 | path->entries++; |
1225 | |
|
1226 | 0 | eh = (struct ext3_extent_header *) path->buf; |
1227 | 0 | eh->eh_entries = ext2fs_cpu_to_le16(path->entries); |
1228 | |
|
1229 | 0 | retval = ext2fs_extent_replace(handle, 0, extent); |
1230 | 0 | if (retval) |
1231 | 0 | goto errout; |
1232 | | |
1233 | 0 | retval = update_path(handle); |
1234 | 0 | if (retval) |
1235 | 0 | goto errout; |
1236 | | |
1237 | 0 | return 0; |
1238 | | |
1239 | 0 | errout: |
1240 | 0 | ext2fs_extent_delete(handle, 0); |
1241 | 0 | return retval; |
1242 | 0 | } |
1243 | | |
1244 | | /* |
1245 | | * Sets the physical block for a logical file block in the extent tree. |
1246 | | * |
1247 | | * May: map unmapped, unmap mapped, or remap mapped blocks. |
1248 | | * |
1249 | | * Mapping an unmapped block adds a single-block extent. |
1250 | | * |
1251 | | * Unmapping first or last block modifies extent in-place |
1252 | | * - But may need to fix parent's starts too in first-block case |
1253 | | * |
1254 | | * Mapping any unmapped block requires adding a (single-block) extent |
1255 | | * and inserting into proper point in tree. |
1256 | | * |
1257 | | * Modifying (unmapping or remapping) a block in the middle |
1258 | | * of an extent requires splitting the extent. |
1259 | | * - Remapping case requires new single-block extent. |
1260 | | * |
1261 | | * Remapping first or last block adds an extent. |
1262 | | * |
1263 | | * We really need extent adding to be smart about merging. |
1264 | | */ |
1265 | | |
1266 | | errcode_t ext2fs_extent_set_bmap(ext2_extent_handle_t handle, |
1267 | | blk64_t logical, blk64_t physical, int flags) |
1268 | 0 | { |
1269 | 0 | errcode_t ec, retval = 0; |
1270 | 0 | int mapped = 1; /* logical is mapped? */ |
1271 | 0 | int orig_height; |
1272 | 0 | int extent_uninit = 0; |
1273 | 0 | int prev_uninit = 0; |
1274 | 0 | int next_uninit = 0; |
1275 | 0 | int new_uninit = 0; |
1276 | 0 | int max_len = EXT_INIT_MAX_LEN; |
1277 | 0 | int has_prev, has_next; |
1278 | 0 | blk64_t orig_lblk; |
1279 | 0 | struct extent_path *path; |
1280 | 0 | struct ext2fs_extent extent, next_extent, prev_extent; |
1281 | 0 | struct ext2fs_extent newextent; |
1282 | 0 | struct ext2_extent_info info; |
1283 | |
|
1284 | 0 | EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); |
1285 | | |
1286 | | #ifdef DEBUG |
1287 | | printf("set_bmap ino %u log %lld phys %lld flags %d\n", |
1288 | | handle->ino, logical, physical, flags); |
1289 | | #endif |
1290 | | |
1291 | 0 | if (!(handle->fs->flags & EXT2_FLAG_RW)) |
1292 | 0 | return EXT2_ET_RO_FILSYS; |
1293 | | |
1294 | 0 | if (!handle->path) |
1295 | 0 | return EXT2_ET_NO_CURRENT_NODE; |
1296 | | |
1297 | 0 | path = handle->path + handle->level; |
1298 | |
|
1299 | 0 | if (flags & EXT2_EXTENT_SET_BMAP_UNINIT) { |
1300 | 0 | new_uninit = 1; |
1301 | 0 | max_len = EXT_UNINIT_MAX_LEN; |
1302 | 0 | } |
1303 | | |
1304 | | /* if (re)mapping, set up new extent to insert */ |
1305 | 0 | if (physical) { |
1306 | 0 | newextent.e_len = 1; |
1307 | 0 | newextent.e_pblk = physical; |
1308 | 0 | newextent.e_lblk = logical; |
1309 | 0 | newextent.e_flags = EXT2_EXTENT_FLAGS_LEAF; |
1310 | 0 | if (new_uninit) |
1311 | 0 | newextent.e_flags |= EXT2_EXTENT_FLAGS_UNINIT; |
1312 | 0 | } |
1313 | | |
1314 | | /* special case if the extent tree is completely empty */ |
1315 | 0 | if ((handle->max_depth == 0) && (path->entries == 0)) { |
1316 | 0 | retval = ext2fs_extent_insert(handle, 0, &newextent); |
1317 | 0 | return retval; |
1318 | 0 | } |
1319 | | |
1320 | | /* save our original location in the extent tree */ |
1321 | 0 | if ((retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, |
1322 | 0 | &extent))) { |
1323 | 0 | if (retval != EXT2_ET_NO_CURRENT_NODE) |
1324 | 0 | return retval; |
1325 | 0 | memset(&extent, 0, sizeof(extent)); |
1326 | 0 | } |
1327 | 0 | if ((retval = ext2fs_extent_get_info(handle, &info))) |
1328 | 0 | return retval; |
1329 | 0 | orig_height = info.max_depth - info.curr_level; |
1330 | 0 | orig_lblk = extent.e_lblk; |
1331 | | |
1332 | | /* go to the logical spot we want to (re/un)map */ |
1333 | 0 | retval = ext2fs_extent_goto(handle, logical); |
1334 | 0 | if (retval) { |
1335 | 0 | if (retval == EXT2_ET_EXTENT_NOT_FOUND) { |
1336 | 0 | retval = 0; |
1337 | 0 | mapped = 0; |
1338 | 0 | if (!physical) { |
1339 | | #ifdef DEBUG |
1340 | | printf("block %llu already unmapped\n", |
1341 | | logical); |
1342 | | #endif |
1343 | 0 | goto done; |
1344 | 0 | } |
1345 | 0 | } else |
1346 | 0 | goto done; |
1347 | 0 | } |
1348 | | |
1349 | | /* |
1350 | | * This may be the extent *before* the requested logical, |
1351 | | * if it's currently unmapped. |
1352 | | * |
1353 | | * Get the previous and next leaf extents, if they are present. |
1354 | | */ |
1355 | 0 | retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); |
1356 | 0 | if (retval) |
1357 | 0 | goto done; |
1358 | 0 | if (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) |
1359 | 0 | extent_uninit = 1; |
1360 | 0 | retval = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_LEAF, &next_extent); |
1361 | 0 | if (retval) { |
1362 | 0 | has_next = 0; |
1363 | 0 | if (retval != EXT2_ET_EXTENT_NO_NEXT) |
1364 | 0 | goto done; |
1365 | 0 | } else { |
1366 | 0 | dbg_print_extent("set_bmap: next_extent", |
1367 | 0 | &next_extent); |
1368 | 0 | has_next = 1; |
1369 | 0 | if (next_extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) |
1370 | 0 | next_uninit = 1; |
1371 | 0 | } |
1372 | 0 | retval = ext2fs_extent_goto(handle, logical); |
1373 | 0 | if (retval && retval != EXT2_ET_EXTENT_NOT_FOUND) |
1374 | 0 | goto done; |
1375 | 0 | retval = ext2fs_extent_get(handle, EXT2_EXTENT_PREV_LEAF, &prev_extent); |
1376 | 0 | if (retval) { |
1377 | 0 | has_prev = 0; |
1378 | 0 | if (retval != EXT2_ET_EXTENT_NO_PREV) |
1379 | 0 | goto done; |
1380 | 0 | } else { |
1381 | 0 | has_prev = 1; |
1382 | 0 | dbg_print_extent("set_bmap: prev_extent", |
1383 | 0 | &prev_extent); |
1384 | 0 | if (prev_extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) |
1385 | 0 | prev_uninit = 1; |
1386 | 0 | } |
1387 | 0 | retval = ext2fs_extent_goto(handle, logical); |
1388 | 0 | if (retval && retval != EXT2_ET_EXTENT_NOT_FOUND) |
1389 | 0 | goto done; |
1390 | | |
1391 | | /* check if already pointing to the requested physical */ |
1392 | 0 | if (mapped && (new_uninit == extent_uninit) && |
1393 | 0 | (extent.e_pblk + (logical - extent.e_lblk) == physical)) { |
1394 | | #ifdef DEBUG |
1395 | | printf("physical block (at %llu) unchanged\n", logical); |
1396 | | #endif |
1397 | 0 | goto done; |
1398 | 0 | } |
1399 | | |
1400 | 0 | if (!mapped) { |
1401 | | #ifdef DEBUG |
1402 | | printf("mapping unmapped logical block %llu\n", logical); |
1403 | | #endif |
1404 | 0 | if ((logical == extent.e_lblk + extent.e_len) && |
1405 | 0 | (physical == extent.e_pblk + extent.e_len) && |
1406 | 0 | (new_uninit == extent_uninit) && |
1407 | 0 | ((int) extent.e_len < max_len-1)) { |
1408 | 0 | extent.e_len++; |
1409 | 0 | retval = ext2fs_extent_replace(handle, 0, &extent); |
1410 | 0 | } else if ((logical == extent.e_lblk - 1) && |
1411 | 0 | (physical == extent.e_pblk - 1) && |
1412 | 0 | (new_uninit == extent_uninit) && |
1413 | 0 | ((int) extent.e_len < max_len - 1)) { |
1414 | 0 | extent.e_len++; |
1415 | 0 | extent.e_lblk--; |
1416 | 0 | extent.e_pblk--; |
1417 | 0 | retval = ext2fs_extent_replace(handle, 0, &extent); |
1418 | 0 | } else if (has_next && |
1419 | 0 | (logical == next_extent.e_lblk - 1) && |
1420 | 0 | (physical == next_extent.e_pblk - 1) && |
1421 | 0 | (new_uninit == next_uninit) && |
1422 | 0 | ((int) next_extent.e_len < max_len - 1)) { |
1423 | 0 | retval = ext2fs_extent_get(handle, |
1424 | 0 | EXT2_EXTENT_NEXT_LEAF, |
1425 | 0 | &next_extent); |
1426 | 0 | if (retval) |
1427 | 0 | goto done; |
1428 | 0 | next_extent.e_len++; |
1429 | 0 | next_extent.e_lblk--; |
1430 | 0 | next_extent.e_pblk--; |
1431 | 0 | retval = ext2fs_extent_replace(handle, 0, &next_extent); |
1432 | 0 | } else if (logical < extent.e_lblk) |
1433 | 0 | retval = ext2fs_extent_insert(handle, 0, &newextent); |
1434 | 0 | else |
1435 | 0 | retval = ext2fs_extent_insert(handle, |
1436 | 0 | EXT2_EXTENT_INSERT_AFTER, &newextent); |
1437 | 0 | if (retval) |
1438 | 0 | goto done; |
1439 | 0 | retval = ext2fs_extent_fix_parents(handle); |
1440 | 0 | if (retval) |
1441 | 0 | goto done; |
1442 | 0 | } else if ((logical == extent.e_lblk) && (extent.e_len == 1)) { |
1443 | | #ifdef DEBUG |
1444 | | printf("(re/un)mapping only block in extent\n"); |
1445 | | #endif |
1446 | 0 | if (physical) { |
1447 | 0 | if (has_prev && |
1448 | 0 | (logical == (prev_extent.e_lblk + |
1449 | 0 | prev_extent.e_len)) && |
1450 | 0 | (physical == (prev_extent.e_pblk + |
1451 | 0 | prev_extent.e_len)) && |
1452 | 0 | (new_uninit == prev_uninit) && |
1453 | 0 | ((int) prev_extent.e_len < max_len-1)) { |
1454 | 0 | retval = ext2fs_extent_get(handle, |
1455 | 0 | EXT2_EXTENT_PREV_LEAF, &prev_extent); |
1456 | 0 | if (retval) |
1457 | 0 | goto done; |
1458 | 0 | prev_extent.e_len++; |
1459 | 0 | retval = ext2fs_extent_replace(handle, 0, |
1460 | 0 | &prev_extent); |
1461 | 0 | retval = ext2fs_extent_get(handle, |
1462 | 0 | EXT2_EXTENT_NEXT_LEAF, |
1463 | 0 | &extent); |
1464 | 0 | if (retval) |
1465 | 0 | goto done; |
1466 | 0 | goto delete_node; |
1467 | |
|
1468 | 0 | } else |
1469 | 0 | retval = ext2fs_extent_replace(handle, 0, &newextent); |
1470 | 0 | } else { |
1471 | 0 | delete_node: |
1472 | 0 | retval = ext2fs_extent_delete(handle, 0); |
1473 | 0 | if (retval) |
1474 | 0 | goto done; |
1475 | 0 | ec = ext2fs_extent_fix_parents(handle); |
1476 | 0 | if (ec != EXT2_ET_NO_CURRENT_NODE) |
1477 | 0 | retval = ec; |
1478 | 0 | } |
1479 | | |
1480 | 0 | if (retval) |
1481 | 0 | goto done; |
1482 | 0 | } else if (logical == extent.e_lblk + extent.e_len - 1) { |
1483 | | #ifdef DEBUG |
1484 | | printf("(re/un)mapping last block in extent\n"); |
1485 | | #endif |
1486 | 0 | if (physical) { |
1487 | 0 | if (has_next && |
1488 | 0 | (logical == (next_extent.e_lblk - 1)) && |
1489 | 0 | (physical == (next_extent.e_pblk - 1)) && |
1490 | 0 | (new_uninit == next_uninit) && |
1491 | 0 | ((int) next_extent.e_len < max_len - 1)) { |
1492 | 0 | retval = ext2fs_extent_get(handle, |
1493 | 0 | EXT2_EXTENT_NEXT_LEAF, &next_extent); |
1494 | 0 | if (retval) |
1495 | 0 | goto done; |
1496 | 0 | next_extent.e_len++; |
1497 | 0 | next_extent.e_lblk--; |
1498 | 0 | next_extent.e_pblk--; |
1499 | 0 | retval = ext2fs_extent_replace(handle, 0, |
1500 | 0 | &next_extent); |
1501 | 0 | if (retval) |
1502 | 0 | goto done; |
1503 | 0 | } else |
1504 | 0 | retval = ext2fs_extent_insert(handle, |
1505 | 0 | EXT2_EXTENT_INSERT_AFTER, &newextent); |
1506 | 0 | if (retval) |
1507 | 0 | goto done; |
1508 | 0 | retval = ext2fs_extent_fix_parents(handle); |
1509 | 0 | if (retval) |
1510 | 0 | goto done; |
1511 | | /* |
1512 | | * Now pointing at inserted extent; move back to prev. |
1513 | | * |
1514 | | * We cannot use EXT2_EXTENT_PREV to go back; note the |
1515 | | * subtlety in the comment for fix_parents(). |
1516 | | */ |
1517 | 0 | retval = ext2fs_extent_goto(handle, logical); |
1518 | 0 | if (retval) |
1519 | 0 | goto done; |
1520 | 0 | retval = ext2fs_extent_get(handle, |
1521 | 0 | EXT2_EXTENT_CURRENT, |
1522 | 0 | &extent); |
1523 | 0 | if (retval) |
1524 | 0 | goto done; |
1525 | 0 | } |
1526 | 0 | extent.e_len--; |
1527 | 0 | retval = ext2fs_extent_replace(handle, 0, &extent); |
1528 | 0 | if (retval) |
1529 | 0 | goto done; |
1530 | 0 | } else if (logical == extent.e_lblk) { |
1531 | | #ifdef DEBUG |
1532 | | printf("(re/un)mapping first block in extent\n"); |
1533 | | #endif |
1534 | 0 | extent.e_pblk++; |
1535 | 0 | extent.e_lblk++; |
1536 | 0 | extent.e_len--; |
1537 | 0 | retval = ext2fs_extent_replace(handle, 0, &extent); |
1538 | 0 | if (retval) |
1539 | 0 | goto done; |
1540 | 0 | retval = ext2fs_extent_fix_parents(handle); |
1541 | 0 | if (retval) |
1542 | 0 | goto done; |
1543 | 0 | if (physical) { |
1544 | 0 | if (has_prev && |
1545 | 0 | (logical == (prev_extent.e_lblk + |
1546 | 0 | prev_extent.e_len)) && |
1547 | 0 | (physical == (prev_extent.e_pblk + |
1548 | 0 | prev_extent.e_len)) && |
1549 | 0 | (new_uninit == prev_uninit) && |
1550 | 0 | ((int) prev_extent.e_len < max_len-1)) { |
1551 | 0 | retval = ext2fs_extent_get(handle, |
1552 | 0 | EXT2_EXTENT_PREV_LEAF, &prev_extent); |
1553 | 0 | if (retval) |
1554 | 0 | goto done; |
1555 | 0 | prev_extent.e_len++; |
1556 | 0 | retval = ext2fs_extent_replace(handle, 0, |
1557 | 0 | &prev_extent); |
1558 | 0 | } else |
1559 | 0 | retval = ext2fs_extent_insert(handle, |
1560 | 0 | 0, &newextent); |
1561 | 0 | if (retval) |
1562 | 0 | goto done; |
1563 | 0 | retval = ext2fs_extent_fix_parents(handle); |
1564 | 0 | if (retval) |
1565 | 0 | goto done; |
1566 | 0 | retval = ext2fs_extent_get(handle, |
1567 | 0 | EXT2_EXTENT_NEXT_LEAF, |
1568 | 0 | &extent); |
1569 | 0 | if (retval) |
1570 | 0 | goto done; |
1571 | 0 | } |
1572 | 0 | } else { |
1573 | 0 | __u32 save_length; |
1574 | 0 | blk64_t save_lblk; |
1575 | 0 | struct ext2fs_extent save_extent; |
1576 | 0 | errcode_t r2; |
1577 | |
|
1578 | | #ifdef DEBUG |
1579 | | printf("(re/un)mapping in middle of extent\n"); |
1580 | | #endif |
1581 | | /* need to split this extent; later */ |
1582 | 0 | save_lblk = extent.e_lblk; |
1583 | 0 | save_length = extent.e_len; |
1584 | 0 | save_extent = extent; |
1585 | | |
1586 | | /* shorten pre-split extent */ |
1587 | 0 | extent.e_len = (logical - extent.e_lblk); |
1588 | 0 | retval = ext2fs_extent_replace(handle, 0, &extent); |
1589 | 0 | if (retval) |
1590 | 0 | goto done; |
1591 | | /* insert our new extent, if any */ |
1592 | 0 | if (physical) { |
1593 | | /* insert new extent after current */ |
1594 | 0 | retval = ext2fs_extent_insert(handle, |
1595 | 0 | EXT2_EXTENT_INSERT_AFTER, &newextent); |
1596 | 0 | if (retval) { |
1597 | 0 | r2 = ext2fs_extent_goto(handle, save_lblk); |
1598 | 0 | if (r2 == 0) |
1599 | 0 | (void)ext2fs_extent_replace(handle, 0, |
1600 | 0 | &save_extent); |
1601 | 0 | goto done; |
1602 | 0 | } |
1603 | 0 | } |
1604 | | /* add post-split extent */ |
1605 | 0 | extent.e_pblk += extent.e_len + 1; |
1606 | 0 | extent.e_lblk += extent.e_len + 1; |
1607 | 0 | extent.e_len = save_length - extent.e_len - 1; |
1608 | 0 | retval = ext2fs_extent_insert(handle, |
1609 | 0 | EXT2_EXTENT_INSERT_AFTER, &extent); |
1610 | 0 | if (retval) { |
1611 | 0 | if (physical) { |
1612 | 0 | r2 = ext2fs_extent_goto(handle, |
1613 | 0 | newextent.e_lblk); |
1614 | 0 | if (r2 == 0) |
1615 | 0 | (void)ext2fs_extent_delete(handle, 0); |
1616 | 0 | } |
1617 | 0 | r2 = ext2fs_extent_goto(handle, save_lblk); |
1618 | 0 | if (r2 == 0) |
1619 | 0 | (void)ext2fs_extent_replace(handle, 0, |
1620 | 0 | &save_extent); |
1621 | 0 | goto done; |
1622 | 0 | } |
1623 | 0 | } |
1624 | | |
1625 | 0 | done: |
1626 | | /* get handle back to its position */ |
1627 | 0 | if (orig_height > handle->max_depth) |
1628 | 0 | orig_height = handle->max_depth; /* In case we shortened the tree */ |
1629 | 0 | ext2fs_extent_goto2(handle, orig_height, orig_lblk); |
1630 | 0 | return retval; |
1631 | 0 | } |
1632 | | |
1633 | | errcode_t ext2fs_extent_delete(ext2_extent_handle_t handle, int flags) |
1634 | 0 | { |
1635 | 0 | struct extent_path *path; |
1636 | 0 | char *cp; |
1637 | 0 | struct ext3_extent_header *eh; |
1638 | 0 | errcode_t retval = 0; |
1639 | |
|
1640 | 0 | EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); |
1641 | | |
1642 | 0 | if (!(handle->fs->flags & EXT2_FLAG_RW)) |
1643 | 0 | return EXT2_ET_RO_FILSYS; |
1644 | | |
1645 | 0 | if (!handle->path) |
1646 | 0 | return EXT2_ET_NO_CURRENT_NODE; |
1647 | | |
1648 | | #ifdef DEBUG |
1649 | | { |
1650 | | struct ext2fs_extent extent; |
1651 | | |
1652 | | retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, |
1653 | | &extent); |
1654 | | if (retval == 0) { |
1655 | | printf("extent delete %u ", handle->ino); |
1656 | | dbg_print_extent(0, &extent); |
1657 | | } |
1658 | | } |
1659 | | #endif |
1660 | | |
1661 | 0 | path = handle->path + handle->level; |
1662 | 0 | if (!path->curr) |
1663 | 0 | return EXT2_ET_NO_CURRENT_NODE; |
1664 | | |
1665 | 0 | cp = path->curr; |
1666 | | |
1667 | | /* Sanity check before memmove() */ |
1668 | 0 | if (path->left < 0) |
1669 | 0 | return EXT2_ET_EXTENT_LEAF_BAD; |
1670 | | |
1671 | 0 | if (path->left) { |
1672 | 0 | memmove(cp, cp + sizeof(struct ext3_extent_idx), |
1673 | 0 | path->left * sizeof(struct ext3_extent_idx)); |
1674 | 0 | path->left--; |
1675 | 0 | } else { |
1676 | 0 | struct ext3_extent_idx *ix = path->curr; |
1677 | 0 | ix--; |
1678 | 0 | path->curr = ix; |
1679 | 0 | } |
1680 | 0 | if (--path->entries == 0) |
1681 | 0 | path->curr = 0; |
1682 | | |
1683 | | /* if non-root node has no entries left, remove it & parent ptr to it */ |
1684 | 0 | if (path->entries == 0 && handle->level) { |
1685 | 0 | if (!(flags & EXT2_EXTENT_DELETE_KEEP_EMPTY)) { |
1686 | 0 | struct ext2fs_extent extent; |
1687 | |
|
1688 | 0 | retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP, |
1689 | 0 | &extent); |
1690 | 0 | if (retval) |
1691 | 0 | return retval; |
1692 | | |
1693 | 0 | retval = ext2fs_extent_delete(handle, flags); |
1694 | 0 | handle->inode->i_blocks -= |
1695 | 0 | (handle->fs->blocksize * |
1696 | 0 | EXT2FS_CLUSTER_RATIO(handle->fs)) / 512; |
1697 | 0 | retval = ext2fs_write_inode(handle->fs, handle->ino, |
1698 | 0 | handle->inode); |
1699 | 0 | ext2fs_block_alloc_stats2(handle->fs, |
1700 | 0 | extent.e_pblk, -1); |
1701 | 0 | } |
1702 | 0 | } else { |
1703 | 0 | eh = (struct ext3_extent_header *) path->buf; |
1704 | 0 | eh->eh_entries = ext2fs_cpu_to_le16(path->entries); |
1705 | 0 | if ((path->entries == 0) && (handle->level == 0)) { |
1706 | 0 | eh->eh_depth = 0; |
1707 | 0 | handle->max_depth = 0; |
1708 | 0 | } |
1709 | 0 | retval = update_path(handle); |
1710 | 0 | } |
1711 | 0 | return retval; |
1712 | 0 | } |
1713 | | |
1714 | | errcode_t ext2fs_extent_get_info(ext2_extent_handle_t handle, |
1715 | | struct ext2_extent_info *info) |
1716 | 0 | { |
1717 | 0 | struct extent_path *path; |
1718 | |
|
1719 | 0 | EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); |
1720 | | |
1721 | 0 | memset(info, 0, sizeof(struct ext2_extent_info)); |
1722 | |
|
1723 | 0 | path = handle->path + handle->level; |
1724 | 0 | if (path) { |
1725 | 0 | if (path->curr) |
1726 | 0 | info->curr_entry = ((char *) path->curr - path->buf) / |
1727 | 0 | sizeof(struct ext3_extent_idx); |
1728 | 0 | else |
1729 | 0 | info->curr_entry = 0; |
1730 | 0 | info->num_entries = path->entries; |
1731 | 0 | info->max_entries = path->max_entries; |
1732 | 0 | info->bytes_avail = (path->max_entries - path->entries) * |
1733 | 0 | sizeof(struct ext3_extent); |
1734 | 0 | } |
1735 | |
|
1736 | 0 | info->curr_level = handle->level; |
1737 | 0 | info->max_depth = handle->max_depth; |
1738 | 0 | info->max_lblk = EXT_MAX_EXTENT_LBLK; |
1739 | 0 | info->max_pblk = EXT_MAX_EXTENT_PBLK; |
1740 | 0 | info->max_len = EXT_INIT_MAX_LEN; |
1741 | 0 | info->max_uninit_len = EXT_UNINIT_MAX_LEN; |
1742 | |
|
1743 | 0 | return 0; |
1744 | 0 | } |
1745 | | |
1746 | | size_t ext2fs_max_extent_depth(ext2_extent_handle_t handle) |
1747 | 0 | { |
1748 | 0 | size_t iblock_sz = sizeof(((struct ext2_inode *)NULL)->i_block); |
1749 | 0 | size_t iblock_extents = (iblock_sz - sizeof(struct ext3_extent_header)) / |
1750 | 0 | sizeof(struct ext3_extent); |
1751 | 0 | size_t extents_per_block = (handle->fs->blocksize - |
1752 | 0 | sizeof(struct ext3_extent_header)) / |
1753 | 0 | sizeof(struct ext3_extent); |
1754 | 0 | static unsigned int last_blocksize = 0; |
1755 | 0 | static size_t last_result = 0; |
1756 | |
|
1757 | 0 | if (last_blocksize && last_blocksize == handle->fs->blocksize) |
1758 | 0 | return last_result; |
1759 | | |
1760 | 0 | last_result = 1 + ((ext2fs_log2_u64(EXT_MAX_EXTENT_LBLK) - |
1761 | 0 | ext2fs_log2_u64(iblock_extents)) / |
1762 | 0 | ext2fs_log2_u32(extents_per_block)); |
1763 | 0 | last_blocksize = handle->fs->blocksize; |
1764 | 0 | return last_result; |
1765 | 0 | } |
1766 | | |
1767 | | errcode_t ext2fs_fix_extents_checksums(ext2_filsys fs, ext2_ino_t ino, |
1768 | | struct ext2_inode *inode) |
1769 | 0 | { |
1770 | 0 | ext2_extent_handle_t handle; |
1771 | 0 | struct ext2fs_extent extent; |
1772 | 0 | errcode_t errcode; |
1773 | 0 | int save_flags = fs->flags; |
1774 | |
|
1775 | 0 | if (!ext2fs_has_feature_metadata_csum(fs->super) || |
1776 | 0 | (inode && !(inode->i_flags & EXT4_EXTENTS_FL))) |
1777 | 0 | return 0; |
1778 | | |
1779 | 0 | errcode = ext2fs_extent_open2(fs, ino, inode, &handle); |
1780 | 0 | if (errcode) { |
1781 | 0 | if (errcode == EXT2_ET_INODE_NOT_EXTENT) |
1782 | 0 | errcode = 0; |
1783 | 0 | return errcode; |
1784 | 0 | } |
1785 | | |
1786 | 0 | fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS; |
1787 | 0 | errcode = ext2fs_extent_get(handle, EXT2_EXTENT_ROOT, &extent); |
1788 | 0 | if (errcode) |
1789 | 0 | goto out; |
1790 | | |
1791 | 0 | do { |
1792 | | /* Skip to the end of a block of leaf nodes */ |
1793 | 0 | if (extent.e_flags & EXT2_EXTENT_FLAGS_LEAF) { |
1794 | 0 | errcode = ext2fs_extent_get(handle, |
1795 | 0 | EXT2_EXTENT_LAST_SIB, |
1796 | 0 | &extent); |
1797 | 0 | if (errcode) |
1798 | 0 | break; |
1799 | 0 | } |
1800 | | |
1801 | 0 | errcode = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT, &extent); |
1802 | 0 | if (errcode == EXT2_ET_EXTENT_CSUM_INVALID) |
1803 | 0 | errcode = update_path(handle); |
1804 | 0 | } while (errcode == 0); |
1805 | |
|
1806 | 0 | out: |
1807 | | /* Ok if we run off the end */ |
1808 | 0 | if (errcode == EXT2_ET_EXTENT_NO_NEXT) |
1809 | 0 | errcode = 0; |
1810 | 0 | ext2fs_extent_free(handle); |
1811 | 0 | fs->flags = save_flags; |
1812 | 0 | return errcode; |
1813 | 0 | } |
1814 | | |
1815 | | errcode_t ext2fs_decode_extent(struct ext2fs_extent *to, void *addr, int len) |
1816 | 0 | { |
1817 | 0 | struct ext3_extent *from = (struct ext3_extent *)addr; |
1818 | |
|
1819 | 0 | if (len != sizeof(struct ext3_extent)) |
1820 | 0 | return EXT2_ET_INVALID_ARGUMENT; |
1821 | | |
1822 | 0 | to->e_pblk = ext2fs_le32_to_cpu(from->ee_start) + |
1823 | 0 | ((__u64) ext2fs_le16_to_cpu(from->ee_start_hi) |
1824 | 0 | << 32); |
1825 | 0 | to->e_lblk = ext2fs_le32_to_cpu(from->ee_block); |
1826 | 0 | to->e_len = ext2fs_le16_to_cpu(from->ee_len); |
1827 | 0 | to->e_flags = EXT2_EXTENT_FLAGS_LEAF; |
1828 | 0 | if (to->e_len > EXT_INIT_MAX_LEN) { |
1829 | 0 | to->e_len -= EXT_INIT_MAX_LEN; |
1830 | 0 | to->e_flags |= EXT2_EXTENT_FLAGS_UNINIT; |
1831 | 0 | } |
1832 | |
|
1833 | 0 | return 0; |
1834 | 0 | } |
1835 | | |
1836 | | errcode_t ext2fs_count_blocks(ext2_filsys fs, ext2_ino_t ino, |
1837 | | struct ext2_inode *inode, blk64_t *ret_count) |
1838 | 0 | { |
1839 | 0 | ext2_extent_handle_t handle = NULL; |
1840 | 0 | struct ext2fs_extent extent; |
1841 | 0 | errcode_t errcode; |
1842 | 0 | int i; |
1843 | 0 | blk64_t blkcount = 0; |
1844 | 0 | blk64_t *intermediate_nodes; |
1845 | |
|
1846 | 0 | errcode = ext2fs_extent_open2(fs, ino, inode, &handle); |
1847 | 0 | if (errcode) |
1848 | 0 | goto out; |
1849 | | |
1850 | 0 | errcode = ext2fs_extent_get(handle, EXT2_EXTENT_ROOT, &extent); |
1851 | 0 | if (errcode) |
1852 | 0 | goto out; |
1853 | | |
1854 | 0 | errcode = ext2fs_get_array(handle->max_depth, sizeof(blk64_t), |
1855 | 0 | &intermediate_nodes); |
1856 | 0 | if (errcode) |
1857 | 0 | goto out; |
1858 | | |
1859 | 0 | blkcount = handle->level; |
1860 | 0 | while (!errcode) { |
1861 | 0 | if (extent.e_flags & EXT2_EXTENT_FLAGS_LEAF) { |
1862 | 0 | blkcount += extent.e_len; |
1863 | 0 | for (i = 0; i < handle->level; i++) { |
1864 | 0 | if (intermediate_nodes[i] != |
1865 | 0 | handle->path[i].end_blk) { |
1866 | 0 | blkcount++; |
1867 | 0 | intermediate_nodes[i] = |
1868 | 0 | handle->path[i].end_blk; |
1869 | 0 | } |
1870 | 0 | } |
1871 | 0 | } |
1872 | 0 | errcode = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT, &extent); |
1873 | 0 | } |
1874 | 0 | ext2fs_free_mem(&intermediate_nodes); |
1875 | 0 | out: |
1876 | 0 | *ret_count = blkcount; |
1877 | 0 | ext2fs_extent_free(handle); |
1878 | |
|
1879 | 0 | return 0; |
1880 | 0 | } |
1881 | | |
1882 | | #ifdef DEBUG |
1883 | | /* |
1884 | | * Override debugfs's prompt |
1885 | | */ |
1886 | | const char *debug_prog_name = "tst_extents"; |
1887 | | |
1888 | | #endif |
1889 | | |