blob: a409252b89ed1a04afed2d7c7fa7954345c3fd20 [file] [log] [blame]
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001/*
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 Public
8 * License.
9 * %End-Header%
10 */
11
12#include <stdio.h>
13#include <string.h>
14#if HAVE_UNISTD_H
15#include <unistd.h>
16#endif
17#if HAVE_ERRNO_H
18#include <errno.h>
19#endif
20#if HAVE_SYS_STAT_H
21#include <sys/stat.h>
22#endif
23#if HAVE_SYS_TYPES_H
24#include <sys/types.h>
25#endif
26
27#include "ext2_fs.h"
28#include "ext2fsP.h"
29#include "e2image.h"
Theodore Ts'o3eb07f62007-10-14 23:04:58 -040030
31/*
32 * Definitions to be dropped in lib/ext2fs/ext2fs.h
33 */
34
35/*
36 * Private definitions
37 */
38
39struct extent_path {
40 char *buf;
41 int entries;
42 int max_entries;
43 int left;
44 int visit_num;
45 int flags;
46 blk64_t end_blk;
47 void *curr;
48};
49
50
51struct ext2_extent_handle {
52 errcode_t magic;
53 ext2_filsys fs;
54 ext2_ino_t ino;
55 struct ext2_inode *inode;
56 int type;
57 int level;
58 int max_depth;
59 struct extent_path *path;
60};
61
62struct ext2_extent_path {
63 errcode_t magic;
64 int leaf_height;
65 blk64_t lblk;
66};
67
68/*
69 * Useful Debugging stuff
70 */
71
72#ifdef DEBUG
73static void dbg_show_header(struct ext3_extent_header *eh)
74{
75 printf("header: magic=%x entries=%u max=%u depth=%u generation=%u\n",
76 eh->eh_magic, eh->eh_entries, eh->eh_max, eh->eh_depth,
77 eh->eh_generation);
78}
79
80static void dbg_show_index(struct ext3_extent_idx *ix)
81{
82 printf("index: block=%u leaf=%u leaf_hi=%u unused=%u\n",
83 ix->ei_block, ix->ei_leaf, ix->ei_leaf_hi, ix->ei_unused);
84}
85
86static void dbg_show_extent(struct ext3_extent *ex)
87{
88 printf("extent: block=%u-%u len=%u start=%u start_hi=%u\n",
89 ex->ee_block, ex->ee_block + ex->ee_len - 1,
90 ex->ee_len, ex->ee_start, ex->ee_start_hi);
91}
92
93static void dbg_print_extent(char *desc, struct ext2fs_extent *extent)
94{
95 if (desc)
96 printf("%s: ", desc);
97 printf("extent: lblk %llu--%llu, len %lu, pblk %llu, flags: ",
98 extent->e_lblk, extent->e_lblk + extent->e_len - 1,
99 extent->e_len, extent->e_pblk);
100 if (extent->e_flags & EXT2_EXTENT_FLAGS_LEAF)
101 fputs("LEAF ", stdout);
102 if (extent->e_flags & EXT2_EXTENT_FLAGS_UNINIT)
103 fputs("UNINIT ", stdout);
104 if (extent->e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT)
105 fputs("2ND_VISIT ", stdout);
106 if (!extent->e_flags)
107 fputs("(none)", stdout);
108 fputc('\n', stdout);
109
110}
111
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400112#else
113#define dbg_show_header(eh) do { } while (0)
114#define dbg_show_index(ix) do { } while (0)
115#define dbg_show_extent(ex) do { } while (0)
116#define dbg_print_extent(desc, ex) do { } while (0)
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400117#endif
118
119/*
120 * Verify the extent header as being sane
121 */
122errcode_t ext2fs_extent_header_verify(void *ptr, int size)
123{
124 int eh_max, entry_size;
125 struct ext3_extent_header *eh = ptr;
126
127 dbg_show_header(eh);
128 if (ext2fs_le16_to_cpu(eh->eh_magic) != EXT3_EXT_MAGIC)
129 return EXT2_ET_EXTENT_HEADER_BAD;
130 if (ext2fs_le16_to_cpu(eh->eh_entries) > ext2fs_le16_to_cpu(eh->eh_max))
131 return EXT2_ET_EXTENT_HEADER_BAD;
132 if (eh->eh_depth == 0)
133 entry_size = sizeof(struct ext3_extent);
134 else
135 entry_size = sizeof(struct ext3_extent_idx);
136
137 eh_max = (size - sizeof(*eh)) / entry_size;
138 /* Allow two extent-sized items at the end of the block, for
139 * ext4_extent_tail with checksum in the future. */
140 if ((ext2fs_le16_to_cpu(eh->eh_max) > eh_max) ||
141 (ext2fs_le16_to_cpu(eh->eh_max) < (eh_max - 2)))
142 return EXT2_ET_EXTENT_HEADER_BAD;
143
144 return 0;
145}
146
147
148/*
149 * Begin functions to handle an inode's extent information
150 */
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400151extern void ext2fs_extent_free(ext2_extent_handle_t handle)
152{
153 int i;
154
155 if (!handle)
156 return;
157
158 if (handle->inode)
159 ext2fs_free_mem(&handle->inode);
160 if (handle->path) {
161 for (i=1; i < handle->max_depth; i++) {
162 if (handle->path[i].buf)
163 ext2fs_free_mem(&handle->path[i].buf);
164 }
Valerie Clementd46d67a2008-02-21 16:39:23 +0100165 ext2fs_free_mem(&handle->path);
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400166 }
167 ext2fs_free_mem(&handle);
168}
169
170extern errcode_t ext2fs_extent_open(ext2_filsys fs, ext2_ino_t ino,
171 ext2_extent_handle_t *ret_handle)
172{
173 struct ext2_extent_handle *handle;
174 errcode_t retval;
175 int isize = EXT2_INODE_SIZE(fs->super);
Theodore Ts'oaa8e2f12008-06-02 00:08:19 -0400176 int i;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400177 struct ext3_extent_header *eh;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400178
179 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
180
181 if ((ino == 0) || (ino > fs->super->s_inodes_count))
182 return EXT2_ET_BAD_INODE_NUM;
183
184 retval = ext2fs_get_mem(sizeof(struct ext2_extent_handle), &handle);
185 if (retval)
186 return retval;
187 memset(handle, 0, sizeof(struct ext2_extent_handle));
188
189 retval = ext2fs_get_mem(isize, &handle->inode);
190 if (retval)
191 goto errout;
192
193 handle->ino = ino;
194 handle->fs = fs;
195
196 retval = ext2fs_read_inode_full(fs, ino, handle->inode, isize);
197 if (retval)
198 goto errout;
199
Theodore Ts'oaa8e2f12008-06-02 00:08:19 -0400200 eh = (struct ext3_extent_header *) &handle->inode->i_block[0];
201
202 for (i=0; i < EXT2_N_BLOCKS; i++)
203 if (handle->inode->i_block[i])
204 break;
205 if (i >= EXT2_N_BLOCKS) {
206 eh->eh_magic = ext2fs_cpu_to_le16(EXT3_EXT_MAGIC);
207 eh->eh_depth = 0;
208 eh->eh_entries = 0;
209 i = (sizeof(handle->inode->i_block) - sizeof(*eh)) /
210 sizeof(struct ext3_extent);
211 eh->eh_max = ext2fs_cpu_to_le16(i);
212 handle->inode->i_flags |= EXT4_EXTENTS_FL;
213 }
214
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400215 if (!(handle->inode->i_flags & EXT4_EXTENTS_FL))
216 return EXT2_ET_INODE_NOT_EXTENT;
217
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400218 retval = ext2fs_extent_header_verify(eh, sizeof(handle->inode->i_block));
219 if (retval)
220 return (retval);
221
222 handle->max_depth = ext2fs_le16_to_cpu(eh->eh_depth);
223 handle->type = ext2fs_le16_to_cpu(eh->eh_magic);
224
225 retval = ext2fs_get_mem(((handle->max_depth+1) *
226 sizeof(struct extent_path)),
227 &handle->path);
228 memset(handle->path, 0,
229 (handle->max_depth+1) * sizeof(struct extent_path));
230 handle->path[0].buf = (char *) handle->inode->i_block;
231
232 handle->path[0].left = handle->path[0].entries =
233 ext2fs_le16_to_cpu(eh->eh_entries);
234 handle->path[0].max_entries = ext2fs_le16_to_cpu(eh->eh_max);
235 handle->path[0].curr = 0;
236 handle->path[0].end_blk =
237 ((((__u64) handle->inode->i_size_high << 32) +
238 handle->inode->i_size + (fs->blocksize - 1))
239 >> EXT2_BLOCK_SIZE_BITS(fs->super));
240 handle->path[0].visit_num = 1;
241 handle->level = 0;
242 handle->magic = EXT2_ET_MAGIC_EXTENT_HANDLE;
243
244 *ret_handle = handle;
245 return 0;
246
247errout:
248 ext2fs_extent_free(handle);
249 return retval;
250}
251
252/*
253 * This function is responsible for (optionally) moving through the
254 * extent tree and then returning the current extent
255 */
256errcode_t ext2fs_extent_get(ext2_extent_handle_t handle,
257 int flags, struct ext2fs_extent *extent)
258{
259 struct extent_path *path, *newpath;
260 struct ext3_extent_header *eh;
261 struct ext3_extent_idx *ix = 0;
262 struct ext3_extent *ex;
263 errcode_t retval;
264 blk_t blk;
265 blk64_t end_blk;
266 int orig_op, op;
267
268 EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
269
270 if (!handle->path)
271 return EXT2_ET_NO_CURRENT_NODE;
272
273 orig_op = op = flags & EXT2_EXTENT_MOVE_MASK;
274
275retry:
276 path = handle->path + handle->level;
277 if ((orig_op == EXT2_EXTENT_NEXT) ||
278 (orig_op == EXT2_EXTENT_NEXT_LEAF)) {
279 if (handle->level < handle->max_depth) {
280 /* interior node */
281 if (path->visit_num == 0) {
282 path->visit_num++;
283 op = EXT2_EXTENT_DOWN;
284 } else if (path->left > 0)
285 op = EXT2_EXTENT_NEXT_SIB;
286 else if (handle->level > 0)
287 op = EXT2_EXTENT_UP;
288 else
289 return EXT2_ET_EXTENT_NO_NEXT;
290 } else {
291 /* leaf node */
292 if (path->left > 0)
293 op = EXT2_EXTENT_NEXT_SIB;
294 else if (handle->level > 0)
295 op = EXT2_EXTENT_UP;
296 else
297 return EXT2_ET_EXTENT_NO_NEXT;
298 }
299 if (op != EXT2_EXTENT_NEXT_SIB) {
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400300#ifdef DEBUG
301 printf("<<<< OP = %s\n",
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400302 (op == EXT2_EXTENT_DOWN) ? "down" :
303 ((op == EXT2_EXTENT_UP) ? "up" : "unknown"));
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400304#endif
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400305 }
306 }
307
308 if ((orig_op == EXT2_EXTENT_PREV) ||
309 (orig_op == EXT2_EXTENT_PREV_LEAF)) {
310 if (handle->level < handle->max_depth) {
311 /* interior node */
312 if (path->visit_num > 0 ) {
313 /* path->visit_num = 0; */
314 op = EXT2_EXTENT_DOWN_AND_LAST;
315 } else if (path->left < path->entries-1)
316 op = EXT2_EXTENT_PREV_SIB;
317 else if (handle->level > 0)
318 op = EXT2_EXTENT_UP;
319 else
320 return EXT2_ET_EXTENT_NO_PREV;
321 } else {
322 /* leaf node */
323 if (path->left < path->entries-1)
324 op = EXT2_EXTENT_PREV_SIB;
325 else if (handle->level > 0)
326 op = EXT2_EXTENT_UP;
327 else
328 return EXT2_ET_EXTENT_NO_PREV;
329 }
330 if (op != EXT2_EXTENT_PREV_SIB) {
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400331#ifdef DEBUG
332 printf("<<<< OP = %s\n",
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400333 (op == EXT2_EXTENT_DOWN_AND_LAST) ? "down/last" :
334 ((op == EXT2_EXTENT_UP) ? "up" : "unknown"));
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400335#endif
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400336 }
337 }
338
339 if (orig_op == EXT2_EXTENT_LAST_LEAF) {
340 if ((handle->level < handle->max_depth) &&
341 (path->left == 0))
342 op = EXT2_EXTENT_DOWN;
343 else
344 op = EXT2_EXTENT_LAST_SIB;
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400345#ifdef DEBUG
346 printf("<<<< OP = %s\n",
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400347 (op == EXT2_EXTENT_DOWN) ? "down" : "last_sib");
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400348#endif
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400349 }
350
351 switch (op) {
352 case EXT2_EXTENT_CURRENT:
353 ix = path->curr;
354 break;
355 case EXT2_EXTENT_ROOT:
356 handle->level = 0;
357 path = handle->path + handle->level;
358 case EXT2_EXTENT_FIRST_SIB:
359 path->left = path->entries;
360 path->curr = 0;
361 case EXT2_EXTENT_NEXT_SIB:
362 if (path->left <= 0)
363 return EXT2_ET_EXTENT_NO_NEXT;
364 if (path->curr) {
365 ix = path->curr;
366 ix++;
367 } else {
368 eh = (struct ext3_extent_header *) path->buf;
369 ix = EXT_FIRST_INDEX(eh);
370 }
371 path->left--;
372 path->curr = ix;
373 path->visit_num = 0;
374 break;
375 case EXT2_EXTENT_PREV_SIB:
376 if (!path->curr ||
377 path->left+1 >= path->entries)
378 return EXT2_ET_EXTENT_NO_PREV;
379 ix = path->curr;
380 ix--;
381 path->curr = ix;
382 path->left++;
383 if (handle->level < handle->max_depth)
384 path->visit_num = 1;
385 break;
386 case EXT2_EXTENT_LAST_SIB:
387 eh = (struct ext3_extent_header *) path->buf;
388 path->curr = EXT_LAST_EXTENT(eh);
389 ix = path->curr;
390 path->left = 0;
391 path->visit_num = 0;
392 break;
393 case EXT2_EXTENT_UP:
394 if (handle->level <= 0)
395 return EXT2_ET_EXTENT_NO_UP;
396 handle->level--;
397 path--;
398 ix = path->curr;
399 if ((orig_op == EXT2_EXTENT_PREV) ||
400 (orig_op == EXT2_EXTENT_PREV_LEAF))
401 path->visit_num = 0;
402 break;
403 case EXT2_EXTENT_DOWN:
404 case EXT2_EXTENT_DOWN_AND_LAST:
405 if (!path->curr ||(handle->level >= handle->max_depth))
406 return EXT2_ET_EXTENT_NO_DOWN;
407
408 ix = path->curr;
409 newpath = path + 1;
410 if (!newpath->buf) {
411 retval = ext2fs_get_mem(handle->fs->blocksize,
412 &newpath->buf);
413 if (retval)
414 return retval;
415 }
416 blk = ext2fs_le32_to_cpu(ix->ei_leaf) +
Valerie Clemente6bbc002008-02-21 16:39:23 +0100417 ((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32);
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400418 if ((handle->fs->flags & EXT2_FLAG_IMAGE_FILE) &&
419 (handle->fs->io != handle->fs->image_io))
420 memset(newpath->buf, 0, handle->fs->blocksize);
421 else {
422 retval = io_channel_read_blk(handle->fs->io,
423 blk, 1, newpath->buf);
424 if (retval)
425 return retval;
426 }
427 handle->level++;
428
429 eh = (struct ext3_extent_header *) newpath->buf;
430
431 retval = ext2fs_extent_header_verify(eh, handle->fs->blocksize);
432 if (retval)
433 return retval;
434
435 newpath->left = newpath->entries =
436 ext2fs_le16_to_cpu(eh->eh_entries);
437 newpath->max_entries = ext2fs_le16_to_cpu(eh->eh_max);
438
439 if (path->left > 0) {
440 ix++;
441 newpath->end_blk = ext2fs_le32_to_cpu(ix->ei_block);
442 } else
443 newpath->end_blk = path->end_blk;
444
445 path = newpath;
446 if (op == EXT2_EXTENT_DOWN) {
447 ix = EXT_FIRST_INDEX((struct ext3_extent_header *) eh);
448 path->curr = ix;
449 path->left = path->entries - 1;
450 path->visit_num = 0;
451 } else {
452 ix = EXT_LAST_INDEX((struct ext3_extent_header *) eh);
453 path->curr = ix;
454 path->left = 0;
455 if (handle->level < handle->max_depth)
456 path->visit_num = 1;
457 }
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400458#ifdef DEBUG
459 printf("Down to level %d/%d, end_blk=%llu\n",
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400460 handle->level, handle->max_depth,
461 path->end_blk);
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400462#endif
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400463 break;
464 default:
465 return EXT2_ET_OP_NOT_SUPPORTED;
466 }
467
468 if (!ix)
469 return EXT2_ET_NO_CURRENT_NODE;
470
471 extent->e_flags = 0;
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400472#ifdef DEBUG
473 printf("(Left %d)\n", path->left);
474#endif
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400475
476 if (handle->level == handle->max_depth) {
477 ex = (struct ext3_extent *) ix;
478
479 extent->e_pblk = ext2fs_le32_to_cpu(ex->ee_start) +
480 ((__u64) ext2fs_le16_to_cpu(ex->ee_start_hi) << 32);
481 extent->e_lblk = ext2fs_le32_to_cpu(ex->ee_block);
482 extent->e_len = ext2fs_le16_to_cpu(ex->ee_len);
Eric Sandeen85b59c42008-03-19 15:20:18 -0500483 extent->e_flags |= EXT2_EXTENT_FLAGS_LEAF;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400484 if (extent->e_len > EXT_INIT_MAX_LEN) {
485 extent->e_len -= EXT_INIT_MAX_LEN;
Eric Sandeen85b59c42008-03-19 15:20:18 -0500486 extent->e_flags |= EXT2_EXTENT_FLAGS_UNINIT;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400487 }
488 } else {
489 extent->e_pblk = ext2fs_le32_to_cpu(ix->ei_leaf) +
490 ((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32);
491 extent->e_lblk = ext2fs_le32_to_cpu(ix->ei_block);
492 if (path->left > 0) {
493 ix++;
494 end_blk = ext2fs_le32_to_cpu(ix->ei_block);
495 } else
496 end_blk = path->end_blk;
497
498 extent->e_len = end_blk - extent->e_lblk;
499 }
500 if (path->visit_num)
501 extent->e_flags |= EXT2_EXTENT_FLAGS_SECOND_VISIT;
502
503 if (((orig_op == EXT2_EXTENT_NEXT_LEAF) ||
504 (orig_op == EXT2_EXTENT_PREV_LEAF)) &&
505 (handle->level != handle->max_depth))
506 goto retry;
507
508 if ((orig_op == EXT2_EXTENT_LAST_LEAF) &&
509 ((handle->level != handle->max_depth) ||
510 (path->left != 0)))
511 goto retry;
512
513 return 0;
514}
515
516static errcode_t update_path(ext2_extent_handle_t handle)
517{
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400518 blk64_t blk;
519 errcode_t retval;
520 struct ext3_extent_idx *ix;
521
522 if (handle->level == 0) {
523 retval = ext2fs_write_inode_full(handle->fs, handle->ino,
524 handle->inode, EXT2_INODE_SIZE(handle->fs->super));
525 } else {
526 ix = handle->path[handle->level - 1].curr;
527 blk = ext2fs_le32_to_cpu(ix->ei_leaf) +
528 ((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32);
529
530 retval = io_channel_write_blk(handle->fs->io,
531 blk, 1, handle->path[handle->level].buf);
532 }
533 return retval;
534}
535
Theodore Ts'o2d328bb2008-03-17 23:17:13 -0400536#if 0
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400537errcode_t ext2fs_extent_save_path(ext2_extent_handle_t handle,
538 ext2_extent_path_t *ret_path)
539{
540 ext2_extent_path_t save_path;
541 struct ext2fs_extent extent;
542 struct ext2_extent_info info;
543 errcode_t retval;
544
545 retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent);
546 if (retval)
547 return retval;
548
549 retval = ext2fs_extent_get_info(handle, &info);
550 if (retval)
551 return retval;
552
553 retval = ext2fs_get_mem(sizeof(struct ext2_extent_path), &save_path);
554 if (retval)
555 return retval;
556 memset(save_path, 0, sizeof(struct ext2_extent_path));
557
558 save_path->magic = EXT2_ET_MAGIC_EXTENT_PATH;
559 save_path->leaf_height = info.max_depth - info.curr_level - 1;
560 save_path->lblk = extent.e_lblk;
561
562 *ret_path = save_path;
563 return 0;
564}
565
566errcode_t ext2fs_extent_free_path(ext2_extent_path_t path)
567{
568 EXT2_CHECK_MAGIC(path, EXT2_ET_MAGIC_EXTENT_PATH);
569
570 ext2fs_free_mem(&path);
571 return 0;
572}
Theodore Ts'o2d328bb2008-03-17 23:17:13 -0400573#endif
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400574
Eric Sandeenc21ed772008-04-07 22:00:59 -0500575/*
576 * Go to the node at leaf_level which contains logical block blk.
577 *
Eric Sandeen9817a2b2008-05-12 18:13:49 -0500578 * leaf_level is height from the leaf node level, i.e.
579 * leaf_level 0 is at leaf node, leaf_level 1 is 1 above etc.
580 *
Eric Sandeenc21ed772008-04-07 22:00:59 -0500581 * If "blk" has no mapping (hole) then handle is left at last
582 * extent before blk.
583 */
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400584static errcode_t extent_goto(ext2_extent_handle_t handle,
585 int leaf_level, blk64_t blk)
586{
Theodore Ts'o2d328bb2008-03-17 23:17:13 -0400587 struct ext2fs_extent extent;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400588 errcode_t retval;
589
590 retval = ext2fs_extent_get(handle, EXT2_EXTENT_ROOT, &extent);
591 if (retval)
592 return retval;
593
Eric Sandeen9817a2b2008-05-12 18:13:49 -0500594 if (leaf_level > handle->max_depth) {
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400595#ifdef DEBUG
596 printf("leaf level %d greater than tree depth %d\n",
Eric Sandeen9817a2b2008-05-12 18:13:49 -0500597 leaf_level, handle->max_depth);
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400598#endif
Eric Sandeen9817a2b2008-05-12 18:13:49 -0500599 return EXT2_ET_OP_NOT_SUPPORTED;
600 }
601
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400602 dbg_print_extent("root", &extent);
603 while (1) {
Eric Sandeen9817a2b2008-05-12 18:13:49 -0500604 if (handle->max_depth - handle->level == leaf_level) {
Eric Sandeenc21ed772008-04-07 22:00:59 -0500605 /* block is in this &extent */
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400606 if ((blk >= extent.e_lblk) &&
607 (blk < extent.e_lblk + extent.e_len))
608 return 0;
Eric Sandeenc21ed772008-04-07 22:00:59 -0500609 if (blk < extent.e_lblk) {
610 retval = ext2fs_extent_get(handle,
611 EXT2_EXTENT_PREV_SIB,
612 &extent);
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400613 return EXT2_ET_EXTENT_NOT_FOUND;
Eric Sandeenc21ed772008-04-07 22:00:59 -0500614 }
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400615 retval = ext2fs_extent_get(handle,
616 EXT2_EXTENT_NEXT_SIB,
617 &extent);
618 if (retval == EXT2_ET_EXTENT_NO_NEXT)
619 return EXT2_ET_EXTENT_NOT_FOUND;
620 if (retval)
621 return retval;
622 continue;
623 }
624
625 retval = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_SIB,
626 &extent);
627 if (retval == EXT2_ET_EXTENT_NO_NEXT)
628 goto go_down;
629 if (retval)
630 return retval;
631
632 dbg_print_extent("next", &extent);
633 if (blk == extent.e_lblk)
634 goto go_down;
635 if (blk > extent.e_lblk)
636 continue;
637
638 retval = ext2fs_extent_get(handle, EXT2_EXTENT_PREV_SIB,
639 &extent);
640 if (retval)
641 return retval;
642
643 dbg_print_extent("prev", &extent);
644
645 go_down:
646 retval = ext2fs_extent_get(handle, EXT2_EXTENT_DOWN,
647 &extent);
648 if (retval)
649 return retval;
650
651 dbg_print_extent("down", &extent);
652 }
653}
654
655errcode_t ext2fs_extent_goto(ext2_extent_handle_t handle,
656 blk64_t blk)
657{
658 return extent_goto(handle, 0, blk);
659}
660
Eric Sandeenf4e99632008-05-20 10:17:46 -0500661/*
662 * Traverse back up to root fixing parents of current node as needed.
663 *
664 * If we changed start of first entry in a node, fix parent index start
665 * and so on.
666 *
667 * Safe to call for any position in node; if not at the first entry,
668 * will simply return.
669 */
670static errcode_t ext2fs_extent_fix_parents(ext2_extent_handle_t handle)
671{
672 int retval = 0;
673 blk64_t start;
674 struct extent_path *path;
675 struct ext2fs_extent extent;
676
677 EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
678
679 if (!(handle->fs->flags & EXT2_FLAG_RW))
680 return EXT2_ET_RO_FILSYS;
681
682 if (!handle->path)
683 return EXT2_ET_NO_CURRENT_NODE;
684
685 path = handle->path + handle->level;
686 if (!path->curr)
687 return EXT2_ET_NO_CURRENT_NODE;
688
689 retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent);
690 if (retval)
691 goto done;
692
693 /* modified node's start block */
694 start = extent.e_lblk;
695
696 /* traverse up until index not first, or startblk matches, or top */
697 while (handle->level > 0 &&
698 (path->left == path->entries - 1)) {
699 retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP, &extent);
700 if (retval)
701 goto done;
702 if (extent.e_lblk == start)
703 break;
704 path = handle->path + handle->level;
705 extent.e_len += (extent.e_lblk - start);
706 extent.e_lblk = start;
707 retval = ext2fs_extent_replace(handle, 0, &extent);
708 if (retval)
709 goto done;
710 update_path(handle);
711 }
712
713 /* put handle back to where we started */
714 retval = ext2fs_extent_goto(handle, start);
715done:
716 return retval;
717}
718
Theodore Ts'o2d328bb2008-03-17 23:17:13 -0400719errcode_t ext2fs_extent_replace(ext2_extent_handle_t handle,
720 int flags EXT2FS_ATTR((unused)),
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400721 struct ext2fs_extent *extent)
722{
723 struct extent_path *path;
724 struct ext3_extent_idx *ix;
725 struct ext3_extent *ex;
726
727 EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
728
729 if (!(handle->fs->flags & EXT2_FLAG_RW))
730 return EXT2_ET_RO_FILSYS;
731
732 if (!handle->path)
733 return EXT2_ET_NO_CURRENT_NODE;
734
735 path = handle->path + handle->level;
736 if (!path->curr)
737 return EXT2_ET_NO_CURRENT_NODE;
738
739 if (handle->level == handle->max_depth) {
740 ex = path->curr;
741
742 ex->ee_block = ext2fs_cpu_to_le32(extent->e_lblk);
743 ex->ee_start = ext2fs_cpu_to_le32(extent->e_pblk & 0xFFFFFFFF);
744 ex->ee_start_hi = ext2fs_cpu_to_le16(extent->e_pblk >> 32);
Theodore Ts'o05a32de2008-06-01 22:56:37 -0400745 if (extent->e_flags & EXT2_EXTENT_FLAGS_UNINIT) {
746 if (extent->e_len > EXT_UNINIT_MAX_LEN)
747 return EXT2_ET_EXTENT_INVALID_LENGTH;
748 ex->ee_len = ext2fs_cpu_to_le16(extent->e_len +
749 EXT_INIT_MAX_LEN);
750 } else {
751 if (extent->e_len > EXT_INIT_MAX_LEN)
752 return EXT2_ET_EXTENT_INVALID_LENGTH;
753 ex->ee_len = ext2fs_cpu_to_le16(extent->e_len);
754 }
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400755 } else {
756 ix = path->curr;
757
758 ix->ei_leaf = ext2fs_cpu_to_le32(extent->e_pblk & 0xFFFFFFFF);
759 ix->ei_leaf_hi = ext2fs_cpu_to_le16(extent->e_pblk >> 32);
760 ix->ei_block = ext2fs_cpu_to_le32(extent->e_lblk);
761 ix->ei_unused = 0;
762 }
763 update_path(handle);
764 return 0;
765}
766
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500767/*
768 * allocate a new block, move half the current node to it, and update parent
769 *
770 * handle will be left pointing at original record.
771 */
Theodore Ts'o8895f432008-06-07 11:53:56 -0400772static errcode_t extent_node_split(ext2_extent_handle_t handle)
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500773{
774 errcode_t retval = 0;
775 blk_t new_node_pblk;
776 blk64_t new_node_start;
777 blk64_t orig_lblk;
778 blk64_t goal_blk = 0;
779 int orig_height;
780 char *block_buf = NULL;
781 struct ext2fs_extent extent;
782 struct extent_path *path, *newpath = 0;
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500783 struct ext3_extent_header *eh, *neweh;
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500784 int tocopy;
785 int new_root = 0;
786 struct ext2_extent_info info;
787
788 /* basic sanity */
789 EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
790
791 if (!(handle->fs->flags & EXT2_FLAG_RW))
792 return EXT2_ET_RO_FILSYS;
793
794 if (!handle->path)
795 return EXT2_ET_NO_CURRENT_NODE;
796
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400797#ifdef DEBUG
798 printf("splitting node at level %d\n", handle->level);
799#endif
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500800 retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent);
801 if (retval)
802 goto done;
803
804 retval = ext2fs_extent_get_info(handle, &info);
805 if (retval)
806 goto done;
807
808 /* save the position we were originally splitting... */
809 orig_height = info.max_depth - info.curr_level;
810 orig_lblk = extent.e_lblk;
811
812 /* Is there room in the parent for a new entry? */
813 if (handle->level &&
814 (handle->path[handle->level - 1].entries >=
815 handle->path[handle->level - 1].max_entries)) {
816
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400817#ifdef DEBUG
818 printf("parent level %d full; splitting it too\n",
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500819 handle->level - 1);
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400820#endif
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500821 /* split the parent */
822 retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP, &extent);
823 if (retval)
824 goto done;
825 goal_blk = extent.e_pblk;
826
Theodore Ts'o8895f432008-06-07 11:53:56 -0400827 retval = extent_node_split(handle);
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500828 if (retval)
829 goto done;
830
831 /* get handle back to our original split position */
832 retval = extent_goto(handle, orig_height, orig_lblk);
833 if (retval)
834 goto done;
835 }
836
837 /* At this point, parent should have room for this split */
838 path = handle->path + handle->level;
839 if (!path->curr)
840 return EXT2_ET_NO_CURRENT_NODE;
841
842 /* extent header of the current node we'll split */
843 eh = (struct ext3_extent_header *)path->buf;
844
845 /* splitting root level means moving them all out */
846 if (handle->level == 0) {
847 new_root = 1;
848 tocopy = ext2fs_le16_to_cpu(eh->eh_entries);
849 retval = ext2fs_get_mem(((handle->max_depth+2) *
850 sizeof(struct extent_path)),
851 &newpath);
852 if (retval)
853 goto done;
854 memset(newpath, 0,
855 ((handle->max_depth+2) * sizeof(struct extent_path)));
856 } else {
857 tocopy = ext2fs_le16_to_cpu(eh->eh_entries) / 2;
858 }
859
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400860#ifdef DEBUG
861 printf("will copy out %d of %d entries at level %d\n",
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500862 tocopy, ext2fs_le16_to_cpu(eh->eh_entries),
863 handle->level);
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400864#endif
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500865
866 if (!tocopy) {
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400867#ifdef DEBUG
868 printf("Nothing to copy to new block!\n");
869#endif
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500870 retval = EXT2_ET_CANT_SPLIT_EXTENT;
871 goto done;
872 }
873
874 /* first we need a new block, or can do nothing. */
875 block_buf = malloc(handle->fs->blocksize);
876 if (!block_buf) {
877 retval = ENOMEM;
878 goto done;
879 }
880
881 if (!goal_blk) {
882 dgrp_t group = ext2fs_group_of_ino(handle->fs, handle->ino);
883 __u8 log_flex = handle->fs->super->s_log_groups_per_flex;
884
885 if (log_flex)
886 group = group & ~((1 << (log_flex)) - 1);
887 goal_blk = (group * handle->fs->super->s_blocks_per_group) +
888 handle->fs->super->s_first_data_block;
889 }
890 retval = ext2fs_alloc_block(handle->fs, (blk_t) goal_blk, block_buf,
891 &new_node_pblk);
892 if (retval)
893 goto done;
894
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400895#ifdef DEBUG
896 printf("will copy to new node at block %lu\n", new_node_pblk);
897#endif
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500898
899 /* Copy data into new block buffer */
900 /* First the header for the new block... */
901 neweh = (struct ext3_extent_header *) block_buf;
902 memcpy(neweh, eh, sizeof(struct ext3_extent_header));
903 neweh->eh_entries = ext2fs_cpu_to_le16(tocopy);
904 neweh->eh_max = ext2fs_cpu_to_le16((handle->fs->blocksize -
905 sizeof(struct ext3_extent_header)) /
906 sizeof(struct ext3_extent));
907
908 /* then the entries for the new block... */
909 memcpy(EXT_FIRST_INDEX(neweh),
910 EXT_FIRST_INDEX(eh) +
911 (ext2fs_le16_to_cpu(eh->eh_entries) - tocopy),
912 sizeof(struct ext3_extent_idx) * tocopy);
913
914 new_node_start = ext2fs_le32_to_cpu(EXT_FIRST_INDEX(neweh)->ei_block);
915
916 /* ...and write the new node block out to disk. */
917 retval = io_channel_write_blk(handle->fs->io, new_node_pblk, 1, block_buf);
918
919 if (retval)
920 goto done;
921
922 /* OK! we've created the new node; now adjust the tree */
923
924 /* current path now has fewer active entries, we copied some out */
925 if (handle->level == 0) {
926 memcpy(newpath, path,
927 sizeof(struct extent_path) * (handle->max_depth+1));
928 handle->path = newpath;
929 newpath = path;
930 path = handle->path;
931 path->entries = 1;
932 path->left = path->max_entries - 1;
933 handle->max_depth++;
934 eh->eh_depth = ext2fs_cpu_to_le16(handle->max_depth);
935 } else {
936 path->entries -= tocopy;
937 path->left -= tocopy;
938 }
939
940 eh->eh_entries = ext2fs_cpu_to_le16(path->entries);
941 /* this writes out the node, incl. the modified header */
942 retval = update_path(handle);
943 if (retval)
944 goto done;
945
946 /* now go up and insert/replace index for new node we created */
947 if (new_root) {
948 retval = ext2fs_extent_get(handle, EXT2_EXTENT_FIRST_SIB, &extent);
949 if (retval)
950 goto done;
951
952 extent.e_lblk = new_node_start;
953 extent.e_pblk = new_node_pblk;
954 extent.e_len = handle->path[0].end_blk - extent.e_lblk;
955 retval = ext2fs_extent_replace(handle, 0, &extent);
956 if (retval)
957 goto done;
958 } else {
959 __u32 new_node_length;
960
961 retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP, &extent);
962 /* will insert after this one; it's length is shorter now */
963 new_node_length = new_node_start - extent.e_lblk;
964 extent.e_len -= new_node_length;
965 retval = ext2fs_extent_replace(handle, 0, &extent);
966 if (retval)
967 goto done;
968
969 /* now set up the new extent and insert it */
970 extent.e_lblk = new_node_start;
971 extent.e_pblk = new_node_pblk;
972 extent.e_len = new_node_length;
973 retval = ext2fs_extent_insert(handle, EXT2_EXTENT_INSERT_AFTER, &extent);
974 if (retval)
975 goto done;
976 }
977
978 /* get handle back to our original position */
979 retval = extent_goto(handle, orig_height, orig_lblk);
980 if (retval)
981 goto done;
982
983 /* new node hooked in, so update inode block count (do this here?) */
984 handle->inode->i_blocks += handle->fs->blocksize / 512;
985 retval = ext2fs_write_inode_full(handle->fs, handle->ino,
986 handle->inode, EXT2_INODE_SIZE(handle->fs->super));
987 if (retval)
988 goto done;
989
990done:
991 if (newpath)
992 ext2fs_free_mem(&newpath);
993 if (block_buf)
994 free(block_buf);
995
996 return retval;
997}
998
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400999errcode_t ext2fs_extent_insert(ext2_extent_handle_t handle, int flags,
1000 struct ext2fs_extent *extent)
1001{
1002 struct extent_path *path;
1003 struct ext3_extent_idx *ix;
1004 struct ext3_extent_header *eh;
1005 errcode_t retval;
1006
1007 EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
1008
1009 if (!(handle->fs->flags & EXT2_FLAG_RW))
1010 return EXT2_ET_RO_FILSYS;
1011
1012 if (!handle->path)
1013 return EXT2_ET_NO_CURRENT_NODE;
1014
1015 path = handle->path + handle->level;
1016
Eric Sandeen01229db2008-05-20 10:15:27 -05001017 if (path->entries >= path->max_entries) {
1018 if (flags & EXT2_EXTENT_INSERT_NOSPLIT) {
1019 return EXT2_ET_CANT_INSERT_EXTENT;
1020 } else {
Theodore Ts'o5a299b52008-07-13 14:19:14 -04001021#ifdef DEBUG
1022 printf("node full (level %d) - splitting\n",
Theodore Ts'o8895f432008-06-07 11:53:56 -04001023 handle->level);
Theodore Ts'o5a299b52008-07-13 14:19:14 -04001024#endif
Theodore Ts'o8895f432008-06-07 11:53:56 -04001025 retval = extent_node_split(handle);
Eric Sandeen01229db2008-05-20 10:15:27 -05001026 if (retval)
1027 goto errout;
1028 path = handle->path + handle->level;
1029 }
1030 }
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001031
1032 eh = (struct ext3_extent_header *) path->buf;
1033 if (path->curr) {
1034 ix = path->curr;
1035 if (flags & EXT2_EXTENT_INSERT_AFTER) {
1036 ix++;
1037 path->left--;
1038 }
1039 } else
1040 ix = EXT_FIRST_INDEX(eh);
1041
1042 path->curr = ix;
1043
Eric Sandeen5e057d52008-04-07 22:00:58 -05001044 if (path->left >= 0)
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001045 memmove(ix + 1, ix,
Theodore Ts'o252b7362008-03-14 10:42:50 -04001046 (path->left+1) * sizeof(struct ext3_extent_idx));
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001047 path->left++;
1048 path->entries++;
1049
1050 eh = (struct ext3_extent_header *) path->buf;
1051 eh->eh_entries = ext2fs_cpu_to_le16(path->entries);
1052
1053 retval = ext2fs_extent_replace(handle, 0, extent);
1054 if (retval)
1055 goto errout;
1056
1057 retval = update_path(handle);
1058 if (retval)
1059 goto errout;
1060
1061 return 0;
1062
1063errout:
1064 ext2fs_extent_delete(handle, 0);
1065 return retval;
1066}
1067
Eric Sandeenf4e99632008-05-20 10:17:46 -05001068/*
1069 * Sets the physical block for a logical file block in the extent tree.
1070 *
1071 * May: map unmapped, unmap mapped, or remap mapped blocks.
1072 *
1073 * Mapping an unmapped block adds a single-block extent.
1074 *
1075 * Unmapping first or last block modifies extent in-place
1076 * - But may need to fix parent's starts too in first-block case
1077 *
1078 * Mapping any unmapped block requires adding a (single-block) extent
1079 * and inserting into proper point in tree.
1080 *
1081 * Modifying (unmapping or remapping) a block in the middle
1082 * of an extent requires splitting the extent.
1083 * - Remapping case requires new single-block extent.
1084 *
1085 * Remapping first or last block adds an extent.
1086 *
1087 * We really need extent adding to be smart about merging.
1088 */
1089
1090errcode_t ext2fs_extent_set_bmap(ext2_extent_handle_t handle,
1091 blk64_t logical, blk64_t physical, int flags)
1092{
1093 errcode_t ec, retval = 0;
1094 int mapped = 1; /* logical is mapped? */
1095 int orig_height;
1096 int extent_uninit = 0;
1097 int new_uninit = 0;
1098 int max_len = EXT_INIT_MAX_LEN;
1099 blk64_t orig_lblk;
1100 struct extent_path *path;
1101 struct ext2fs_extent extent;
1102 struct ext2fs_extent newextent;
1103 struct ext2_extent_info info;
1104
1105 EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
1106
1107 if (!(handle->fs->flags & EXT2_FLAG_RW))
1108 return EXT2_ET_RO_FILSYS;
1109
1110 if (!handle->path)
1111 return EXT2_ET_NO_CURRENT_NODE;
1112
1113 path = handle->path + handle->level;
1114
1115 if (flags & EXT2_EXTENT_SET_BMAP_UNINIT) {
1116 new_uninit = 1;
1117 max_len = EXT_UNINIT_MAX_LEN;
1118 }
1119
1120 /* if (re)mapping, set up new extent to insert */
1121 if (physical) {
1122 newextent.e_len = 1;
1123 newextent.e_pblk = physical;
1124 newextent.e_lblk = logical;
1125 newextent.e_flags = EXT2_EXTENT_FLAGS_LEAF;
1126 if (new_uninit)
1127 newextent.e_flags |= EXT2_EXTENT_FLAGS_UNINIT;
1128 }
1129
1130 /* special case if the extent tree is completely empty */
1131 if ((handle->max_depth == 0) && (path->entries == 0)) {
1132 retval = ext2fs_extent_insert(handle, 0, &newextent);
Theodore Ts'o8895f432008-06-07 11:53:56 -04001133 return retval;
Eric Sandeenf4e99632008-05-20 10:17:46 -05001134 }
1135
1136 /* save our original location in the extent tree */
1137 if ((retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT,
1138 &extent))) {
1139 if (retval != EXT2_ET_NO_CURRENT_NODE)
1140 return retval;
1141 memset(&extent, 0, sizeof(extent));
1142 }
1143 if ((retval = ext2fs_extent_get_info(handle, &info)))
1144 return retval;
1145 orig_height = info.max_depth - info.curr_level;
1146 orig_lblk = extent.e_lblk;
1147
1148 /* go to the logical spot we want to (re/un)map */
1149 retval = ext2fs_extent_goto(handle, logical);
1150 if (retval) {
1151 if (retval == EXT2_ET_EXTENT_NOT_FOUND) {
1152 retval = 0;
1153 mapped = 0;
1154 if (!physical) {
Theodore Ts'o5a299b52008-07-13 14:19:14 -04001155#ifdef DEBUG
1156 printf("block %llu already unmapped\n",
Theodore Ts'o8895f432008-06-07 11:53:56 -04001157 logical);
Theodore Ts'o5a299b52008-07-13 14:19:14 -04001158#endif
Eric Sandeenf4e99632008-05-20 10:17:46 -05001159 goto done;
1160 }
1161 } else
1162 goto done;
1163 }
1164
1165 /*
1166 * This may be the extent *before* the requested logical,
1167 * if it's currently unmapped.
1168 */
1169 retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent);
1170 if (retval)
1171 goto done;
1172 if (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT)
1173 extent_uninit = 1;
1174
1175 /* check if already pointing to the requested physical */
1176 if (mapped && (new_uninit == extent_uninit) &&
1177 (extent.e_pblk + (logical - extent.e_lblk) == physical)) {
Theodore Ts'o5a299b52008-07-13 14:19:14 -04001178#ifdef DEBUG
1179 printf("physical block (at %llu) unchanged\n", logical);
1180#endif
Eric Sandeenf4e99632008-05-20 10:17:46 -05001181 goto done;
1182 }
1183
1184 if (!mapped) {
Theodore Ts'o5a299b52008-07-13 14:19:14 -04001185#ifdef DEBUG
1186 printf("mapping unmapped logical block %llu\n", logical);
1187#endif
Eric Sandeenf4e99632008-05-20 10:17:46 -05001188 if ((logical == extent.e_lblk + extent.e_len) &&
1189 (physical == extent.e_pblk + extent.e_len) &&
1190 (new_uninit == extent_uninit) &&
Theodore Ts'o8895f432008-06-07 11:53:56 -04001191 ((int) extent.e_len < max_len-1)) {
Eric Sandeenf4e99632008-05-20 10:17:46 -05001192 extent.e_len++;
1193 retval = ext2fs_extent_replace(handle, 0, &extent);
1194 } else
1195 retval = ext2fs_extent_insert(handle,
1196 EXT2_EXTENT_INSERT_AFTER, &newextent);
1197 if (retval)
1198 goto done;
1199 retval = ext2fs_extent_fix_parents(handle);
1200 if (retval)
1201 goto done;
1202 } else if ((logical == extent.e_lblk) && (extent.e_len == 1)) {
Theodore Ts'o5a299b52008-07-13 14:19:14 -04001203#ifdef DEBUG
1204 printf("(re/un)mapping only block in extent\n");
1205#endif
Eric Sandeenf4e99632008-05-20 10:17:46 -05001206 if (physical) {
1207 extent.e_pblk = physical;
1208 retval = ext2fs_extent_replace(handle, 0, &extent);
1209 } else {
1210 retval = ext2fs_extent_delete(handle, 0);
1211 if (retval)
1212 goto done;
1213 ec = ext2fs_extent_fix_parents(handle);
1214 if (ec != EXT2_ET_NO_CURRENT_NODE)
1215 retval = ec;
1216 }
1217
1218 if (retval)
1219 goto done;
1220 } else if (logical == extent.e_lblk + extent.e_len - 1) {
Theodore Ts'o5a299b52008-07-13 14:19:14 -04001221#ifdef DEBUG
1222 printf("(re/un)mapping last block in extent\n");
1223#endif
Eric Sandeenf4e99632008-05-20 10:17:46 -05001224 extent.e_len--;
1225 retval = ext2fs_extent_replace(handle, 0, &extent);
1226 if (retval)
1227 goto done;
1228 if (physical) {
1229 retval = ext2fs_extent_insert(handle,
1230 EXT2_EXTENT_INSERT_AFTER, &newextent);
1231 if (retval)
1232 goto done;
1233 }
1234 } else if (logical == extent.e_lblk) {
Theodore Ts'o5a299b52008-07-13 14:19:14 -04001235#ifdef DEBUG
1236 printf("(re/un)mapping first block in extent\n");
1237#endif
Eric Sandeenf4e99632008-05-20 10:17:46 -05001238 extent.e_pblk++;
1239 extent.e_lblk++;
1240 extent.e_len--;
1241 retval = ext2fs_extent_replace(handle, 0, &extent);
1242 if (retval)
1243 goto done;
1244 if (physical) {
1245 /* insert new extent ahead of current */
1246 retval = ext2fs_extent_insert(handle,
1247 0, &newextent);
1248 if (retval)
1249 goto done;
1250 } else {
1251 retval = ext2fs_extent_fix_parents(handle);
1252 if (retval)
1253 goto done;
1254 }
1255 } else {
1256 __u32 orig_length;
1257
Theodore Ts'o5a299b52008-07-13 14:19:14 -04001258#ifdef DEBUG
1259 printf("(re/un)mapping in middle of extent\n");
1260#endif
Eric Sandeenf4e99632008-05-20 10:17:46 -05001261 /* need to split this extent; later */
1262
1263 orig_length = extent.e_len;
1264
1265 /* shorten pre-split extent */
1266 extent.e_len = (logical - extent.e_lblk);
1267 retval = ext2fs_extent_replace(handle, 0, &extent);
1268 if (retval)
1269 goto done;
1270 /* insert our new extent, if any */
1271 if (physical) {
1272 /* insert new extent after current */
1273 retval = ext2fs_extent_insert(handle,
1274 EXT2_EXTENT_INSERT_AFTER, &newextent);
1275 if (retval)
1276 goto done;
1277 }
1278 /* add post-split extent */
1279 extent.e_pblk += extent.e_len + 1;
1280 extent.e_lblk += extent.e_len + 1;
1281 extent.e_len = orig_length - extent.e_len - 1;
1282 retval = ext2fs_extent_insert(handle,
1283 EXT2_EXTENT_INSERT_AFTER, &extent);
1284 if (retval)
1285 goto done;
1286 }
1287
1288done:
1289 /* get handle back to its position */
1290 if (orig_height > handle->max_depth)
1291 orig_height = handle->max_depth; /* In case we shortened the tree */
1292 extent_goto(handle, orig_height, orig_lblk);
1293 return retval;
1294}
1295
Eric Sandeenc802ad92008-05-20 16:13:41 -05001296errcode_t ext2fs_extent_delete(ext2_extent_handle_t handle, int flags)
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001297{
1298 struct extent_path *path;
1299 char *cp;
1300 struct ext3_extent_header *eh;
Theodore Ts'o8895f432008-06-07 11:53:56 -04001301 errcode_t retval = 0;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001302
1303 EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
1304
1305 if (!(handle->fs->flags & EXT2_FLAG_RW))
1306 return EXT2_ET_RO_FILSYS;
1307
1308 if (!handle->path)
1309 return EXT2_ET_NO_CURRENT_NODE;
1310
1311 path = handle->path + handle->level;
1312 if (!path->curr)
1313 return EXT2_ET_NO_CURRENT_NODE;
1314
1315 cp = path->curr;
1316
1317 if (path->left) {
1318 memmove(cp, cp + sizeof(struct ext3_extent_idx),
1319 path->left * sizeof(struct ext3_extent_idx));
1320 path->left--;
Theodore Ts'o83d9be12008-03-13 23:55:40 -04001321 } else {
1322 struct ext3_extent_idx *ix = path->curr;
1323 ix--;
1324 path->curr = ix;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001325 }
Eric Sandeenc802ad92008-05-20 16:13:41 -05001326 if (--path->entries == 0)
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001327 path->curr = 0;
1328
Eric Sandeenc802ad92008-05-20 16:13:41 -05001329 /* if non-root node has no entries left, remove it & parent ptr to it */
1330 if (path->entries == 0 && handle->level) {
1331 if (!(flags & EXT2_EXTENT_DELETE_KEEP_EMPTY)) {
1332 struct ext2fs_extent extent;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001333
Eric Sandeenc802ad92008-05-20 16:13:41 -05001334 retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP,
1335 &extent);
1336 if (retval)
1337 return retval;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001338
Eric Sandeenc802ad92008-05-20 16:13:41 -05001339 retval = ext2fs_extent_delete(handle, flags);
1340 handle->inode->i_blocks -= handle->fs->blocksize / 512;
1341 retval = ext2fs_write_inode_full(handle->fs,
1342 handle->ino, handle->inode,
1343 EXT2_INODE_SIZE(handle->fs->super));
1344 ext2fs_block_alloc_stats(handle->fs, extent.e_pblk, -1);
1345 }
1346 } else {
1347 eh = (struct ext3_extent_header *) path->buf;
1348 eh->eh_entries = ext2fs_cpu_to_le16(path->entries);
1349 if ((path->entries == 0) && (handle->level == 0))
1350 eh->eh_depth = handle->max_depth = 0;
1351 retval = update_path(handle);
1352 }
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001353 return retval;
1354}
1355
1356errcode_t ext2fs_extent_get_info(ext2_extent_handle_t handle,
1357 struct ext2_extent_info *info)
1358{
1359 struct extent_path *path;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001360
1361 EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
1362
1363 memset(info, 0, sizeof(struct ext2_extent_info));
1364
1365 path = handle->path + handle->level;
1366 if (path) {
1367 if (path->curr)
1368 info->curr_entry = ((char *) path->curr - path->buf) /
1369 sizeof(struct ext3_extent_idx);
1370 else
1371 info->curr_entry = 0;
1372 info->num_entries = path->entries;
1373 info->max_entries = path->max_entries;
1374 info->bytes_avail = (path->max_entries - path->entries) *
1375 sizeof(struct ext3_extent);
1376 }
1377
1378 info->curr_level = handle->level;
1379 info->max_depth = handle->max_depth;
1380 info->max_lblk = ((__u64) 1 << 32) - 1;
1381 info->max_pblk = ((__u64) 1 << 48) - 1;
1382 info->max_len = (1UL << 15);
1383 info->max_uninit_len = (1UL << 15) - 1;
1384
1385 return 0;
1386}
1387
1388#ifdef DEBUG
1389
Theodore Ts'o92e94af2008-06-23 14:10:43 -04001390#include "ss/ss.h"
1391
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001392#include "debugfs.h"
1393
1394/*
1395 * Hook in new commands into debugfs
1396 */
Theodore Ts'o2d328bb2008-03-17 23:17:13 -04001397const char *debug_prog_name = "tst_extents";
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001398extern ss_request_table extent_cmds;
1399ss_request_table *extra_cmds = &extent_cmds;
1400
1401ext2_ino_t current_ino = 0;
1402ext2_extent_handle_t current_handle;
1403
Eric Sandeenf4e99632008-05-20 10:17:46 -05001404int common_extent_args_process(int argc, char *argv[], int min_argc,
1405 int max_argc, const char *cmd,
1406 const char *usage, int flags)
1407{
1408 if (common_args_process(argc, argv, min_argc, max_argc, cmd,
1409 usage, flags))
1410 return 1;
1411
1412 if (!current_handle) {
1413 com_err(cmd, 0, "Extent handle not open");
1414 return 1;
1415 }
1416 return 0;
1417}
1418
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001419void do_inode(int argc, char *argv[])
1420{
1421 ext2_ino_t inode;
1422 int i;
1423 struct ext3_extent_header *eh;
1424 errcode_t retval;
1425
1426 if (check_fs_open(argv[0]))
1427 return;
1428
1429 if (argc == 1) {
1430 if (current_ino)
1431 printf("Current inode is %d\n", current_ino);
1432 else
1433 printf("No current inode\n");
1434 return;
1435 }
1436
1437 if (common_inode_args_process(argc, argv, &inode, 0)) {
1438 return;
1439 }
1440
1441 current_ino = 0;
1442
1443 retval = ext2fs_extent_open(current_fs, inode, &current_handle);
1444 if (retval) {
1445 com_err(argv[1], retval, "while opening extent handle");
1446 return;
1447 }
1448
1449 current_ino = inode;
1450
1451 printf("Loaded inode %d\n", current_ino);
1452
1453 return;
1454}
1455
1456void generic_goto_node(char *cmd_name, int op)
1457{
1458 struct ext2fs_extent extent;
1459 errcode_t retval;
1460
1461 if (check_fs_open(cmd_name))
1462 return;
1463
1464 if (!current_handle) {
1465 com_err(cmd_name, 0, "Extent handle not open");
1466 return;
1467 }
1468
1469 retval = ext2fs_extent_get(current_handle, op, &extent);
1470 if (retval) {
1471 com_err(cmd_name, retval, 0);
1472 return;
1473 }
1474 dbg_print_extent(0, &extent);
1475}
1476
1477void do_current_node(int argc, char *argv[])
1478{
1479 generic_goto_node(argv[0], EXT2_EXTENT_CURRENT);
1480}
1481
1482void do_root_node(int argc, char *argv[])
1483{
1484 generic_goto_node(argv[0], EXT2_EXTENT_ROOT);
1485}
1486
1487void do_last_leaf(int argc, char *argv[])
1488{
1489 generic_goto_node(argv[0], EXT2_EXTENT_LAST_LEAF);
1490}
1491
1492void do_first_sib(int argc, char *argv[])
1493{
1494 generic_goto_node(argv[0], EXT2_EXTENT_FIRST_SIB);
1495}
1496
1497void do_last_sib(int argc, char *argv[])
1498{
1499 generic_goto_node(argv[0], EXT2_EXTENT_LAST_SIB);
1500}
1501
1502void do_next_sib(int argc, char *argv[])
1503{
1504 generic_goto_node(argv[0], EXT2_EXTENT_NEXT_SIB);
1505}
1506
1507void do_prev_sib(int argc, char *argv[])
1508{
1509 generic_goto_node(argv[0], EXT2_EXTENT_PREV_SIB);
1510}
1511
1512void do_next_leaf(int argc, char *argv[])
1513{
1514 generic_goto_node(argv[0], EXT2_EXTENT_NEXT_LEAF);
1515}
1516
1517void do_prev_leaf(int argc, char *argv[])
1518{
1519 generic_goto_node(argv[0], EXT2_EXTENT_PREV_LEAF);
1520}
1521
1522void do_next(int argc, char *argv[])
1523{
1524 generic_goto_node(argv[0], EXT2_EXTENT_NEXT);
1525}
1526
1527void do_prev(int argc, char *argv[])
1528{
1529 generic_goto_node(argv[0], EXT2_EXTENT_PREV);
1530}
1531
1532void do_up(int argc, char *argv[])
1533{
1534 generic_goto_node(argv[0], EXT2_EXTENT_UP);
1535}
1536
1537void do_down(int argc, char *argv[])
1538{
1539 generic_goto_node(argv[0], EXT2_EXTENT_DOWN);
1540}
1541
1542void do_delete_node(int argc, char *argv[])
1543{
1544 errcode_t retval;
1545 int err;
1546
Eric Sandeenf4e99632008-05-20 10:17:46 -05001547 if (common_extent_args_process(argc, argv, 1, 1, "delete_node",
1548 "", CHECK_FS_RW | CHECK_FS_BITMAPS))
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001549 return;
1550
1551 retval = ext2fs_extent_delete(current_handle, 0);
1552 if (retval) {
1553 com_err(argv[0], retval, 0);
1554 return;
1555 }
Theodore Ts'oaa8e2f12008-06-02 00:08:19 -04001556 if (current_handle->path && current_handle->path[0].curr)
1557 do_current_node(argc, argv);
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001558}
1559
1560void do_replace_node(int argc, char *argv[])
1561{
Eric Sandeenf4e99632008-05-20 10:17:46 -05001562 const char *usage = "[--uninit] <lblk> <len> <pblk>";
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001563 errcode_t retval;
1564 struct ext2fs_extent extent;
1565 int err;
1566
Eric Sandeenf4e99632008-05-20 10:17:46 -05001567 if (common_extent_args_process(argc, argv, 3, 5, "replace_node",
1568 usage, CHECK_FS_RW | CHECK_FS_BITMAPS))
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001569 return;
1570
Theodore Ts'o05a32de2008-06-01 22:56:37 -04001571 extent.e_flags = 0;
1572
1573 if (!strcmp(argv[1], "--uninit")) {
1574 argc--;
1575 argv++;
1576 extent.e_flags |= EXT2_EXTENT_FLAGS_UNINIT;
1577 }
1578
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001579 if (argc != 4) {
Eric Sandeenf4e99632008-05-20 10:17:46 -05001580 fprintf(stderr, "Usage: %s %s\n", argv[0], usage);
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001581 return;
1582 }
1583
Eric Sandeenf4e99632008-05-20 10:17:46 -05001584 extent.e_lblk = parse_ulong(argv[1], argv[0], "logical block", &err);
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001585 if (err)
1586 return;
1587
Eric Sandeenf4e99632008-05-20 10:17:46 -05001588 extent.e_len = parse_ulong(argv[2], argv[0], "logical block", &err);
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001589 if (err)
1590 return;
1591
Eric Sandeenf4e99632008-05-20 10:17:46 -05001592 extent.e_pblk = parse_ulong(argv[3], argv[0], "logical block", &err);
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001593 if (err)
1594 return;
1595
1596 retval = ext2fs_extent_replace(current_handle, 0, &extent);
1597 if (retval) {
1598 com_err(argv[0], retval, 0);
1599 return;
1600 }
1601 do_current_node(argc, argv);
1602}
1603
Eric Sandeen9fd6a962008-05-20 10:14:20 -05001604void do_split_node(int argc, char *argv[])
1605{
1606 errcode_t retval;
1607 struct ext2fs_extent extent;
1608 int err;
Eric Sandeen9fd6a962008-05-20 10:14:20 -05001609
Eric Sandeenf4e99632008-05-20 10:17:46 -05001610 if (common_extent_args_process(argc, argv, 1, 1, "split_node",
1611 "", CHECK_FS_RW | CHECK_FS_BITMAPS))
Eric Sandeen9fd6a962008-05-20 10:14:20 -05001612 return;
1613
Theodore Ts'o8895f432008-06-07 11:53:56 -04001614 retval = extent_node_split(current_handle);
Eric Sandeen9fd6a962008-05-20 10:14:20 -05001615 if (retval) {
1616 com_err(argv[0], retval, 0);
1617 return;
1618 }
1619 do_current_node(argc, argv);
1620}
1621
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001622void do_insert_node(int argc, char *argv[])
1623{
Eric Sandeenf4e99632008-05-20 10:17:46 -05001624 const char *usage = "[--after] [--uninit] <lblk> <len> <pblk>";
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001625 errcode_t retval;
1626 struct ext2fs_extent extent;
1627 char *cmd;
1628 int err;
1629 int flags = 0;
1630
Eric Sandeenf4e99632008-05-20 10:17:46 -05001631 if (common_extent_args_process(argc, argv, 3, 6, "insert_node",
1632 usage, CHECK_FS_RW | CHECK_FS_BITMAPS))
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001633 return;
1634
1635 cmd = argv[0];
1636
Theodore Ts'o05a32de2008-06-01 22:56:37 -04001637 extent.e_flags = 0;
1638
1639 while (argc > 2) {
1640 if (!strcmp(argv[1], "--after")) {
1641 argc--;
1642 argv++;
1643 flags |= EXT2_EXTENT_INSERT_AFTER;
1644 continue;
1645 }
1646 if (!strcmp(argv[1], "--uninit")) {
1647 argc--;
1648 argv++;
1649 extent.e_flags |= EXT2_EXTENT_FLAGS_UNINIT;
1650 continue;
1651 }
1652 break;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001653 }
1654
1655 if (argc != 4) {
Eric Sandeenf4e99632008-05-20 10:17:46 -05001656 fprintf(stderr, "usage: %s %s\n", cmd, usage);
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001657 return;
1658 }
1659
1660 extent.e_lblk = parse_ulong(argv[1], cmd,
1661 "logical block", &err);
1662 if (err)
1663 return;
1664
1665 extent.e_len = parse_ulong(argv[2], cmd,
1666 "length", &err);
1667 if (err)
1668 return;
1669
1670 extent.e_pblk = parse_ulong(argv[3], cmd,
1671 "pysical block", &err);
1672 if (err)
1673 return;
1674
1675 retval = ext2fs_extent_insert(current_handle, flags, &extent);
1676 if (retval) {
1677 com_err(cmd, retval, 0);
1678 return;
1679 }
1680 do_current_node(argc, argv);
1681}
1682
Eric Sandeenf4e99632008-05-20 10:17:46 -05001683void do_set_bmap(int argc, char **argv)
1684{
1685 const char *usage = "[--uninit] <lblk> <pblk>";
1686 errcode_t retval;
1687 blk_t logical;
1688 blk_t physical;
1689 char *cmd = argv[0];
1690 int flags = 0;
1691 int err;
1692
1693 if (common_extent_args_process(argc, argv, 3, 5, "set_bmap",
1694 usage, CHECK_FS_RW | CHECK_FS_BITMAPS))
1695 return;
1696
1697 if (argc > 2 && !strcmp(argv[1], "--uninit")) {
1698 argc--;
1699 argv++;
1700 flags |= EXT2_EXTENT_SET_BMAP_UNINIT;
1701 }
1702
1703 if (argc != 3) {
1704 fprintf(stderr, "Usage: %s %s\n", cmd, usage);
1705 return;
1706 }
1707
1708 logical = parse_ulong(argv[1], cmd,
1709 "logical block", &err);
1710 if (err)
1711 return;
1712
1713 physical = parse_ulong(argv[2], cmd,
1714 "physical block", &err);
1715 if (err)
1716 return;
1717
1718 retval = ext2fs_extent_set_bmap(current_handle, logical,
1719 (blk64_t) physical, flags);
1720 if (retval) {
1721 com_err(cmd, retval, 0);
1722 return;
1723 }
1724 if (current_handle->path && current_handle->path[0].curr)
1725 do_current_node(argc, argv);
1726}
1727
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001728void do_print_all(int argc, char **argv)
1729{
Eric Sandeenf4e99632008-05-20 10:17:46 -05001730 const char *usage = "[--leaf-only|--reverse|--reverse-leaf]";
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001731 struct ext2fs_extent extent;
1732 errcode_t retval;
1733 errcode_t end_err = EXT2_ET_EXTENT_NO_NEXT;
1734 int op = EXT2_EXTENT_NEXT;
1735 int first_op = EXT2_EXTENT_ROOT;
1736
1737
Eric Sandeenf4e99632008-05-20 10:17:46 -05001738 if (common_extent_args_process(argc, argv, 1, 2, "print_all",
1739 usage, 0))
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001740 return;
1741
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001742 if (argc == 2) {
1743 if (!strcmp(argv[1], "--leaf-only"))
1744 op = EXT2_EXTENT_NEXT_LEAF;
1745 else if (!strcmp(argv[1], "--reverse")) {
1746 op = EXT2_EXTENT_PREV;
1747 first_op = EXT2_EXTENT_LAST_LEAF;
1748 end_err = EXT2_ET_EXTENT_NO_PREV;
1749 } else if (!strcmp(argv[1], "--reverse-leaf")) {
1750 op = EXT2_EXTENT_PREV_LEAF;
1751 first_op = EXT2_EXTENT_LAST_LEAF;
1752 end_err = EXT2_ET_EXTENT_NO_PREV;
Eric Sandeenf4e99632008-05-20 10:17:46 -05001753 } else {
1754 fprintf(stderr, "Usage: %s %s\n", argv[0], usage);
1755 return;
1756 }
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001757 }
1758
1759 retval = ext2fs_extent_get(current_handle, first_op, &extent);
1760 if (retval) {
1761 com_err(argv[0], retval, 0);
1762 return;
1763 }
1764 dbg_print_extent(0, &extent);
1765
1766 while (1) {
1767 retval = ext2fs_extent_get(current_handle, op, &extent);
1768 if (retval == end_err)
1769 break;
1770
1771 if (retval) {
1772 com_err(argv[0], retval, 0);
1773 return;
1774 }
1775 dbg_print_extent(0, &extent);
1776 }
1777}
1778
1779void do_info(int argc, char **argv)
1780{
Eric Sandeen108e62e2008-04-07 22:00:57 -05001781 struct ext2fs_extent extent;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001782 struct ext2_extent_info info;
1783 errcode_t retval;
1784
Eric Sandeenf4e99632008-05-20 10:17:46 -05001785 if (common_extent_args_process(argc, argv, 1, 1, "info", "", 0))
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001786 return;
1787
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001788 retval = ext2fs_extent_get_info(current_handle, &info);
1789 if (retval) {
1790 com_err(argv[0], retval, 0);
1791 return;
1792 }
1793
Eric Sandeen108e62e2008-04-07 22:00:57 -05001794 retval = ext2fs_extent_get(current_handle,
1795 EXT2_EXTENT_CURRENT, &extent);
1796 if (retval) {
1797 com_err(argv[0], retval, 0);
1798 return;
1799 }
1800
1801 dbg_print_extent(0, &extent);
1802
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001803 printf("Current handle location: %d/%d (max: %d, bytes %d), level %d/%d\n",
1804 info.curr_entry, info.num_entries, info.max_entries,
1805 info.bytes_avail, info.curr_level, info.max_depth);
1806 printf("\tmax lblk: %llu, max pblk: %llu\n", info.max_lblk,
1807 info.max_pblk);
1808 printf("\tmax_len: %u, max_uninit_len: %u\n", info.max_len,
1809 info.max_uninit_len);
1810}
1811
1812void do_goto_block(int argc, char **argv)
1813{
1814 struct ext2fs_extent extent;
1815 errcode_t retval;
1816 int op = EXT2_EXTENT_NEXT_LEAF;
1817 blk_t blk;
Eric Sandeen9817a2b2008-05-12 18:13:49 -05001818 int level = 0;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001819
Eric Sandeenf4e99632008-05-20 10:17:46 -05001820 if (common_extent_args_process(argc, argv, 2, 3, "goto_block",
1821 "block [level]", 0))
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001822 return;
1823
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001824 if (strtoblk(argv[0], argv[1], &blk))
1825 return;
1826
Eric Sandeen9817a2b2008-05-12 18:13:49 -05001827 if (argc == 3)
1828 if (strtoblk(argv[0], argv[2], &level))
1829 return;
1830
1831 retval = extent_goto(current_handle, level, (blk64_t) blk);
1832
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001833 if (retval) {
Eric Sandeen9817a2b2008-05-12 18:13:49 -05001834 com_err(argv[0], retval, "while trying to go to block %lu, level %d",
1835 blk, level);
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001836 return;
1837 }
1838
1839 generic_goto_node(argv[0], EXT2_EXTENT_CURRENT);
1840}
1841#endif
1842