blob: fc6c95b8ec36710c704660449bfe4ef44303b434 [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%
Theodore Ts'o543547a2010-05-17 21:31:56 -04007 * This file may be redistributed under the terms of the GNU Library
8 * General Public License, version 2.
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04009 * %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;
JP Abgralle0ed7402014-03-19 19:08:39 -070056 struct ext2_inode inodebuf;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -040057 int type;
58 int level;
59 int max_depth;
60 struct extent_path *path;
61};
62
63struct ext2_extent_path {
64 errcode_t magic;
65 int leaf_height;
66 blk64_t lblk;
67};
68
69/*
70 * Useful Debugging stuff
71 */
72
73#ifdef DEBUG
74static void dbg_show_header(struct ext3_extent_header *eh)
75{
76 printf("header: magic=%x entries=%u max=%u depth=%u generation=%u\n",
Aneesh Kumar K.Vac24e622008-07-18 17:35:40 +053077 ext2fs_le16_to_cpu(eh->eh_magic),
78 ext2fs_le16_to_cpu(eh->eh_entries),
79 ext2fs_le16_to_cpu(eh->eh_max),
80 ext2fs_le16_to_cpu(eh->eh_depth),
81 ext2fs_le32_to_cpu(eh->eh_generation));
Theodore Ts'o3eb07f62007-10-14 23:04:58 -040082}
83
84static void dbg_show_index(struct ext3_extent_idx *ix)
85{
86 printf("index: block=%u leaf=%u leaf_hi=%u unused=%u\n",
Aneesh Kumar K.Vac24e622008-07-18 17:35:40 +053087 ext2fs_le32_to_cpu(ix->ei_block),
88 ext2fs_le32_to_cpu(ix->ei_leaf),
89 ext2fs_le16_to_cpu(ix->ei_leaf_hi),
90 ext2fs_le16_to_cpu(ix->ei_unused));
Theodore Ts'o3eb07f62007-10-14 23:04:58 -040091}
92
93static void dbg_show_extent(struct ext3_extent *ex)
94{
95 printf("extent: block=%u-%u len=%u start=%u start_hi=%u\n",
Aneesh Kumar K.Vac24e622008-07-18 17:35:40 +053096 ext2fs_le32_to_cpu(ex->ee_block),
97 ext2fs_le32_to_cpu(ex->ee_block) +
98 ext2fs_le16_to_cpu(ex->ee_len) - 1,
99 ext2fs_le16_to_cpu(ex->ee_len),
100 ext2fs_le32_to_cpu(ex->ee_start),
101 ext2fs_le16_to_cpu(ex->ee_start_hi));
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400102}
103
104static void dbg_print_extent(char *desc, struct ext2fs_extent *extent)
105{
106 if (desc)
107 printf("%s: ", desc);
Theodore Ts'o64cb68b2009-07-19 23:32:09 -0400108 printf("extent: lblk %llu--%llu, len %u, pblk %llu, flags: ",
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400109 extent->e_lblk, extent->e_lblk + extent->e_len - 1,
110 extent->e_len, extent->e_pblk);
111 if (extent->e_flags & EXT2_EXTENT_FLAGS_LEAF)
112 fputs("LEAF ", stdout);
113 if (extent->e_flags & EXT2_EXTENT_FLAGS_UNINIT)
114 fputs("UNINIT ", stdout);
115 if (extent->e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT)
116 fputs("2ND_VISIT ", stdout);
117 if (!extent->e_flags)
118 fputs("(none)", stdout);
119 fputc('\n', stdout);
120
121}
122
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400123#else
124#define dbg_show_header(eh) do { } while (0)
125#define dbg_show_index(ix) do { } while (0)
126#define dbg_show_extent(ex) do { } while (0)
127#define dbg_print_extent(desc, ex) do { } while (0)
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400128#endif
129
130/*
131 * Verify the extent header as being sane
132 */
133errcode_t ext2fs_extent_header_verify(void *ptr, int size)
134{
135 int eh_max, entry_size;
136 struct ext3_extent_header *eh = ptr;
137
138 dbg_show_header(eh);
139 if (ext2fs_le16_to_cpu(eh->eh_magic) != EXT3_EXT_MAGIC)
140 return EXT2_ET_EXTENT_HEADER_BAD;
141 if (ext2fs_le16_to_cpu(eh->eh_entries) > ext2fs_le16_to_cpu(eh->eh_max))
142 return EXT2_ET_EXTENT_HEADER_BAD;
143 if (eh->eh_depth == 0)
144 entry_size = sizeof(struct ext3_extent);
145 else
146 entry_size = sizeof(struct ext3_extent_idx);
147
148 eh_max = (size - sizeof(*eh)) / entry_size;
149 /* Allow two extent-sized items at the end of the block, for
150 * ext4_extent_tail with checksum in the future. */
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400151 if ((ext2fs_le16_to_cpu(eh->eh_max) > eh_max) ||
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400152 (ext2fs_le16_to_cpu(eh->eh_max) < (eh_max - 2)))
153 return EXT2_ET_EXTENT_HEADER_BAD;
154
155 return 0;
156}
157
158
159/*
160 * Begin functions to handle an inode's extent information
161 */
JP Abgralle0ed7402014-03-19 19:08:39 -0700162void ext2fs_extent_free(ext2_extent_handle_t handle)
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400163{
164 int i;
165
166 if (!handle)
167 return;
168
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400169 if (handle->path) {
Theodore Ts'o7b59b4a2009-06-15 01:34:43 -0400170 for (i=1; i <= handle->max_depth; i++) {
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400171 if (handle->path[i].buf)
172 ext2fs_free_mem(&handle->path[i].buf);
173 }
Valerie Clementd46d67a2008-02-21 16:39:23 +0100174 ext2fs_free_mem(&handle->path);
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400175 }
176 ext2fs_free_mem(&handle);
177}
178
JP Abgralle0ed7402014-03-19 19:08:39 -0700179errcode_t ext2fs_extent_open(ext2_filsys fs, ext2_ino_t ino,
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400180 ext2_extent_handle_t *ret_handle)
181{
number965284b239a2009-05-19 13:34:12 -0700182 return ext2fs_extent_open2(fs, ino, NULL, ret_handle);
183}
184
JP Abgralle0ed7402014-03-19 19:08:39 -0700185errcode_t ext2fs_extent_open2(ext2_filsys fs, ext2_ino_t ino,
number965284b239a2009-05-19 13:34:12 -0700186 struct ext2_inode *inode,
187 ext2_extent_handle_t *ret_handle)
188{
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400189 struct ext2_extent_handle *handle;
190 errcode_t retval;
Theodore Ts'oaa8e2f12008-06-02 00:08:19 -0400191 int i;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400192 struct ext3_extent_header *eh;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400193
194 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
195
number965284b239a2009-05-19 13:34:12 -0700196 if (!inode)
197 if ((ino == 0) || (ino > fs->super->s_inodes_count))
198 return EXT2_ET_BAD_INODE_NUM;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400199
200 retval = ext2fs_get_mem(sizeof(struct ext2_extent_handle), &handle);
201 if (retval)
202 return retval;
203 memset(handle, 0, sizeof(struct ext2_extent_handle));
204
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400205 handle->ino = ino;
206 handle->fs = fs;
207
number965284b239a2009-05-19 13:34:12 -0700208 if (inode) {
JP Abgralle0ed7402014-03-19 19:08:39 -0700209 handle->inode = inode;
210 } else {
211 handle->inode = &handle->inodebuf;
number965284b239a2009-05-19 13:34:12 -0700212 retval = ext2fs_read_inode(fs, ino, handle->inode);
213 if (retval)
214 goto errout;
215 }
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400216
Theodore Ts'oaa8e2f12008-06-02 00:08:19 -0400217 eh = (struct ext3_extent_header *) &handle->inode->i_block[0];
218
219 for (i=0; i < EXT2_N_BLOCKS; i++)
220 if (handle->inode->i_block[i])
221 break;
222 if (i >= EXT2_N_BLOCKS) {
223 eh->eh_magic = ext2fs_cpu_to_le16(EXT3_EXT_MAGIC);
224 eh->eh_depth = 0;
225 eh->eh_entries = 0;
226 i = (sizeof(handle->inode->i_block) - sizeof(*eh)) /
227 sizeof(struct ext3_extent);
228 eh->eh_max = ext2fs_cpu_to_le16(i);
229 handle->inode->i_flags |= EXT4_EXTENTS_FL;
230 }
231
Manish Katiyarf93d9f62008-08-16 02:26:43 +0530232 if (!(handle->inode->i_flags & EXT4_EXTENTS_FL)) {
233 retval = EXT2_ET_INODE_NOT_EXTENT;
234 goto errout;
235 }
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400236
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400237 retval = ext2fs_extent_header_verify(eh, sizeof(handle->inode->i_block));
238 if (retval)
Manish Katiyarf93d9f62008-08-16 02:26:43 +0530239 goto errout;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400240
241 handle->max_depth = ext2fs_le16_to_cpu(eh->eh_depth);
242 handle->type = ext2fs_le16_to_cpu(eh->eh_magic);
243
244 retval = ext2fs_get_mem(((handle->max_depth+1) *
245 sizeof(struct extent_path)),
246 &handle->path);
247 memset(handle->path, 0,
248 (handle->max_depth+1) * sizeof(struct extent_path));
249 handle->path[0].buf = (char *) handle->inode->i_block;
250
251 handle->path[0].left = handle->path[0].entries =
252 ext2fs_le16_to_cpu(eh->eh_entries);
253 handle->path[0].max_entries = ext2fs_le16_to_cpu(eh->eh_max);
254 handle->path[0].curr = 0;
255 handle->path[0].end_blk =
JP Abgralle0ed7402014-03-19 19:08:39 -0700256 (EXT2_I_SIZE(handle->inode) + fs->blocksize - 1) >>
257 EXT2_BLOCK_SIZE_BITS(fs->super);
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400258 handle->path[0].visit_num = 1;
259 handle->level = 0;
260 handle->magic = EXT2_ET_MAGIC_EXTENT_HANDLE;
261
262 *ret_handle = handle;
263 return 0;
264
265errout:
266 ext2fs_extent_free(handle);
267 return retval;
268}
269
270/*
271 * This function is responsible for (optionally) moving through the
272 * extent tree and then returning the current extent
273 */
274errcode_t ext2fs_extent_get(ext2_extent_handle_t handle,
275 int flags, struct ext2fs_extent *extent)
276{
277 struct extent_path *path, *newpath;
278 struct ext3_extent_header *eh;
279 struct ext3_extent_idx *ix = 0;
280 struct ext3_extent *ex;
281 errcode_t retval;
JP Abgralle0ed7402014-03-19 19:08:39 -0700282 blk64_t blk;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400283 blk64_t end_blk;
284 int orig_op, op;
285
286 EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
287
288 if (!handle->path)
289 return EXT2_ET_NO_CURRENT_NODE;
290
291 orig_op = op = flags & EXT2_EXTENT_MOVE_MASK;
292
293retry:
294 path = handle->path + handle->level;
295 if ((orig_op == EXT2_EXTENT_NEXT) ||
296 (orig_op == EXT2_EXTENT_NEXT_LEAF)) {
297 if (handle->level < handle->max_depth) {
298 /* interior node */
299 if (path->visit_num == 0) {
300 path->visit_num++;
301 op = EXT2_EXTENT_DOWN;
302 } else if (path->left > 0)
303 op = EXT2_EXTENT_NEXT_SIB;
304 else if (handle->level > 0)
305 op = EXT2_EXTENT_UP;
306 else
307 return EXT2_ET_EXTENT_NO_NEXT;
308 } else {
309 /* leaf node */
310 if (path->left > 0)
311 op = EXT2_EXTENT_NEXT_SIB;
312 else if (handle->level > 0)
313 op = EXT2_EXTENT_UP;
314 else
315 return EXT2_ET_EXTENT_NO_NEXT;
316 }
317 if (op != EXT2_EXTENT_NEXT_SIB) {
Theodore Ts'o64cb68b2009-07-19 23:32:09 -0400318#ifdef DEBUG_GET_EXTENT
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400319 printf("<<<< OP = %s\n",
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400320 (op == EXT2_EXTENT_DOWN) ? "down" :
321 ((op == EXT2_EXTENT_UP) ? "up" : "unknown"));
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400322#endif
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400323 }
324 }
325
326 if ((orig_op == EXT2_EXTENT_PREV) ||
327 (orig_op == EXT2_EXTENT_PREV_LEAF)) {
328 if (handle->level < handle->max_depth) {
329 /* interior node */
330 if (path->visit_num > 0 ) {
331 /* path->visit_num = 0; */
332 op = EXT2_EXTENT_DOWN_AND_LAST;
333 } else if (path->left < path->entries-1)
334 op = EXT2_EXTENT_PREV_SIB;
335 else if (handle->level > 0)
336 op = EXT2_EXTENT_UP;
337 else
338 return EXT2_ET_EXTENT_NO_PREV;
339 } else {
340 /* leaf node */
341 if (path->left < path->entries-1)
342 op = EXT2_EXTENT_PREV_SIB;
343 else if (handle->level > 0)
344 op = EXT2_EXTENT_UP;
345 else
346 return EXT2_ET_EXTENT_NO_PREV;
347 }
348 if (op != EXT2_EXTENT_PREV_SIB) {
Theodore Ts'o64cb68b2009-07-19 23:32:09 -0400349#ifdef DEBUG_GET_EXTENT
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400350 printf("<<<< OP = %s\n",
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400351 (op == EXT2_EXTENT_DOWN_AND_LAST) ? "down/last" :
352 ((op == EXT2_EXTENT_UP) ? "up" : "unknown"));
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400353#endif
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400354 }
355 }
356
357 if (orig_op == EXT2_EXTENT_LAST_LEAF) {
358 if ((handle->level < handle->max_depth) &&
359 (path->left == 0))
360 op = EXT2_EXTENT_DOWN;
361 else
362 op = EXT2_EXTENT_LAST_SIB;
Theodore Ts'o64cb68b2009-07-19 23:32:09 -0400363#ifdef DEBUG_GET_EXTENT
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400364 printf("<<<< OP = %s\n",
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400365 (op == EXT2_EXTENT_DOWN) ? "down" : "last_sib");
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400366#endif
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400367 }
368
369 switch (op) {
370 case EXT2_EXTENT_CURRENT:
371 ix = path->curr;
372 break;
373 case EXT2_EXTENT_ROOT:
374 handle->level = 0;
375 path = handle->path + handle->level;
JP Abgralle0ed7402014-03-19 19:08:39 -0700376 /* fallthrough */
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400377 case EXT2_EXTENT_FIRST_SIB:
378 path->left = path->entries;
379 path->curr = 0;
JP Abgralle0ed7402014-03-19 19:08:39 -0700380 /* fallthrough */
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400381 case EXT2_EXTENT_NEXT_SIB:
382 if (path->left <= 0)
383 return EXT2_ET_EXTENT_NO_NEXT;
384 if (path->curr) {
385 ix = path->curr;
386 ix++;
387 } else {
388 eh = (struct ext3_extent_header *) path->buf;
389 ix = EXT_FIRST_INDEX(eh);
390 }
391 path->left--;
392 path->curr = ix;
393 path->visit_num = 0;
394 break;
395 case EXT2_EXTENT_PREV_SIB:
396 if (!path->curr ||
397 path->left+1 >= path->entries)
398 return EXT2_ET_EXTENT_NO_PREV;
399 ix = path->curr;
400 ix--;
401 path->curr = ix;
402 path->left++;
403 if (handle->level < handle->max_depth)
404 path->visit_num = 1;
405 break;
406 case EXT2_EXTENT_LAST_SIB:
407 eh = (struct ext3_extent_header *) path->buf;
408 path->curr = EXT_LAST_EXTENT(eh);
409 ix = path->curr;
410 path->left = 0;
411 path->visit_num = 0;
412 break;
413 case EXT2_EXTENT_UP:
414 if (handle->level <= 0)
415 return EXT2_ET_EXTENT_NO_UP;
416 handle->level--;
417 path--;
418 ix = path->curr;
419 if ((orig_op == EXT2_EXTENT_PREV) ||
420 (orig_op == EXT2_EXTENT_PREV_LEAF))
421 path->visit_num = 0;
422 break;
423 case EXT2_EXTENT_DOWN:
424 case EXT2_EXTENT_DOWN_AND_LAST:
425 if (!path->curr ||(handle->level >= handle->max_depth))
426 return EXT2_ET_EXTENT_NO_DOWN;
427
428 ix = path->curr;
429 newpath = path + 1;
430 if (!newpath->buf) {
431 retval = ext2fs_get_mem(handle->fs->blocksize,
432 &newpath->buf);
433 if (retval)
434 return retval;
435 }
436 blk = ext2fs_le32_to_cpu(ix->ei_leaf) +
Valerie Clemente6bbc002008-02-21 16:39:23 +0100437 ((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32);
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400438 if ((handle->fs->flags & EXT2_FLAG_IMAGE_FILE) &&
439 (handle->fs->io != handle->fs->image_io))
440 memset(newpath->buf, 0, handle->fs->blocksize);
441 else {
JP Abgralle0ed7402014-03-19 19:08:39 -0700442 retval = io_channel_read_blk64(handle->fs->io,
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400443 blk, 1, newpath->buf);
444 if (retval)
445 return retval;
446 }
447 handle->level++;
448
449 eh = (struct ext3_extent_header *) newpath->buf;
450
451 retval = ext2fs_extent_header_verify(eh, handle->fs->blocksize);
Theodore Ts'o7518c172008-12-25 22:42:38 -0500452 if (retval) {
453 handle->level--;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400454 return retval;
Theodore Ts'o7518c172008-12-25 22:42:38 -0500455 }
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400456
457 newpath->left = newpath->entries =
458 ext2fs_le16_to_cpu(eh->eh_entries);
459 newpath->max_entries = ext2fs_le16_to_cpu(eh->eh_max);
460
461 if (path->left > 0) {
462 ix++;
463 newpath->end_blk = ext2fs_le32_to_cpu(ix->ei_block);
464 } else
465 newpath->end_blk = path->end_blk;
466
467 path = newpath;
468 if (op == EXT2_EXTENT_DOWN) {
469 ix = EXT_FIRST_INDEX((struct ext3_extent_header *) eh);
470 path->curr = ix;
471 path->left = path->entries - 1;
472 path->visit_num = 0;
473 } else {
474 ix = EXT_LAST_INDEX((struct ext3_extent_header *) eh);
475 path->curr = ix;
476 path->left = 0;
477 if (handle->level < handle->max_depth)
478 path->visit_num = 1;
479 }
Theodore Ts'o64cb68b2009-07-19 23:32:09 -0400480#ifdef DEBUG_GET_EXTENT
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400481 printf("Down to level %d/%d, end_blk=%llu\n",
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400482 handle->level, handle->max_depth,
483 path->end_blk);
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400484#endif
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400485 break;
486 default:
487 return EXT2_ET_OP_NOT_SUPPORTED;
488 }
489
490 if (!ix)
491 return EXT2_ET_NO_CURRENT_NODE;
492
493 extent->e_flags = 0;
Theodore Ts'o64cb68b2009-07-19 23:32:09 -0400494#ifdef DEBUG_GET_EXTENT
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400495 printf("(Left %d)\n", path->left);
496#endif
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400497
498 if (handle->level == handle->max_depth) {
499 ex = (struct ext3_extent *) ix;
500
501 extent->e_pblk = ext2fs_le32_to_cpu(ex->ee_start) +
502 ((__u64) ext2fs_le16_to_cpu(ex->ee_start_hi) << 32);
503 extent->e_lblk = ext2fs_le32_to_cpu(ex->ee_block);
504 extent->e_len = ext2fs_le16_to_cpu(ex->ee_len);
Eric Sandeen85b59c42008-03-19 15:20:18 -0500505 extent->e_flags |= EXT2_EXTENT_FLAGS_LEAF;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400506 if (extent->e_len > EXT_INIT_MAX_LEN) {
507 extent->e_len -= EXT_INIT_MAX_LEN;
Eric Sandeen85b59c42008-03-19 15:20:18 -0500508 extent->e_flags |= EXT2_EXTENT_FLAGS_UNINIT;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400509 }
510 } else {
511 extent->e_pblk = ext2fs_le32_to_cpu(ix->ei_leaf) +
512 ((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32);
513 extent->e_lblk = ext2fs_le32_to_cpu(ix->ei_block);
514 if (path->left > 0) {
515 ix++;
516 end_blk = ext2fs_le32_to_cpu(ix->ei_block);
517 } else
518 end_blk = path->end_blk;
519
520 extent->e_len = end_blk - extent->e_lblk;
521 }
522 if (path->visit_num)
523 extent->e_flags |= EXT2_EXTENT_FLAGS_SECOND_VISIT;
524
525 if (((orig_op == EXT2_EXTENT_NEXT_LEAF) ||
526 (orig_op == EXT2_EXTENT_PREV_LEAF)) &&
527 (handle->level != handle->max_depth))
528 goto retry;
529
530 if ((orig_op == EXT2_EXTENT_LAST_LEAF) &&
531 ((handle->level != handle->max_depth) ||
532 (path->left != 0)))
533 goto retry;
534
535 return 0;
536}
537
538static errcode_t update_path(ext2_extent_handle_t handle)
539{
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400540 blk64_t blk;
541 errcode_t retval;
542 struct ext3_extent_idx *ix;
543
544 if (handle->level == 0) {
Eric Sandeen125a3672009-06-17 18:49:01 -0400545 retval = ext2fs_write_inode(handle->fs, handle->ino,
546 handle->inode);
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400547 } else {
548 ix = handle->path[handle->level - 1].curr;
549 blk = ext2fs_le32_to_cpu(ix->ei_leaf) +
550 ((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32);
551
JP Abgralle0ed7402014-03-19 19:08:39 -0700552 retval = io_channel_write_blk64(handle->fs->io,
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400553 blk, 1, handle->path[handle->level].buf);
554 }
555 return retval;
556}
557
Theodore Ts'o2d328bb2008-03-17 23:17:13 -0400558#if 0
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400559errcode_t ext2fs_extent_save_path(ext2_extent_handle_t handle,
560 ext2_extent_path_t *ret_path)
561{
562 ext2_extent_path_t save_path;
563 struct ext2fs_extent extent;
564 struct ext2_extent_info info;
565 errcode_t retval;
566
567 retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent);
568 if (retval)
569 return retval;
570
571 retval = ext2fs_extent_get_info(handle, &info);
572 if (retval)
573 return retval;
574
575 retval = ext2fs_get_mem(sizeof(struct ext2_extent_path), &save_path);
576 if (retval)
577 return retval;
578 memset(save_path, 0, sizeof(struct ext2_extent_path));
579
580 save_path->magic = EXT2_ET_MAGIC_EXTENT_PATH;
581 save_path->leaf_height = info.max_depth - info.curr_level - 1;
582 save_path->lblk = extent.e_lblk;
583
584 *ret_path = save_path;
585 return 0;
586}
587
588errcode_t ext2fs_extent_free_path(ext2_extent_path_t path)
589{
590 EXT2_CHECK_MAGIC(path, EXT2_ET_MAGIC_EXTENT_PATH);
591
592 ext2fs_free_mem(&path);
593 return 0;
594}
Theodore Ts'o2d328bb2008-03-17 23:17:13 -0400595#endif
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400596
Eric Sandeenc21ed772008-04-07 22:00:59 -0500597/*
598 * Go to the node at leaf_level which contains logical block blk.
599 *
Eric Sandeen9817a2b2008-05-12 18:13:49 -0500600 * leaf_level is height from the leaf node level, i.e.
601 * leaf_level 0 is at leaf node, leaf_level 1 is 1 above etc.
602 *
Eric Sandeenc21ed772008-04-07 22:00:59 -0500603 * If "blk" has no mapping (hole) then handle is left at last
604 * extent before blk.
605 */
JP Abgralle0ed7402014-03-19 19:08:39 -0700606errcode_t ext2fs_extent_goto2(ext2_extent_handle_t handle,
607 int leaf_level, blk64_t blk)
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400608{
Theodore Ts'o2d328bb2008-03-17 23:17:13 -0400609 struct ext2fs_extent extent;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400610 errcode_t retval;
611
612 retval = ext2fs_extent_get(handle, EXT2_EXTENT_ROOT, &extent);
Theodore Ts'o9033faf2008-08-27 18:37:11 -0400613 if (retval) {
614 if (retval == EXT2_ET_EXTENT_NO_NEXT)
615 retval = EXT2_ET_EXTENT_NOT_FOUND;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400616 return retval;
Theodore Ts'o9033faf2008-08-27 18:37:11 -0400617 }
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400618
Eric Sandeen9817a2b2008-05-12 18:13:49 -0500619 if (leaf_level > handle->max_depth) {
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400620#ifdef DEBUG
621 printf("leaf level %d greater than tree depth %d\n",
Eric Sandeen9817a2b2008-05-12 18:13:49 -0500622 leaf_level, handle->max_depth);
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400623#endif
Eric Sandeen9817a2b2008-05-12 18:13:49 -0500624 return EXT2_ET_OP_NOT_SUPPORTED;
625 }
626
Theodore Ts'o64cb68b2009-07-19 23:32:09 -0400627#ifdef DEBUG
628 printf("goto extent ino %u, level %d, %llu\n", handle->ino,
629 leaf_level, blk);
630#endif
631
632#ifdef DEBUG_GOTO_EXTENTS
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400633 dbg_print_extent("root", &extent);
Theodore Ts'o64cb68b2009-07-19 23:32:09 -0400634#endif
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400635 while (1) {
Eric Sandeen9817a2b2008-05-12 18:13:49 -0500636 if (handle->max_depth - handle->level == leaf_level) {
Eric Sandeenc21ed772008-04-07 22:00:59 -0500637 /* block is in this &extent */
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400638 if ((blk >= extent.e_lblk) &&
639 (blk < extent.e_lblk + extent.e_len))
640 return 0;
Eric Sandeenc21ed772008-04-07 22:00:59 -0500641 if (blk < extent.e_lblk) {
642 retval = ext2fs_extent_get(handle,
643 EXT2_EXTENT_PREV_SIB,
644 &extent);
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400645 return EXT2_ET_EXTENT_NOT_FOUND;
Eric Sandeenc21ed772008-04-07 22:00:59 -0500646 }
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400647 retval = ext2fs_extent_get(handle,
648 EXT2_EXTENT_NEXT_SIB,
649 &extent);
650 if (retval == EXT2_ET_EXTENT_NO_NEXT)
651 return EXT2_ET_EXTENT_NOT_FOUND;
652 if (retval)
653 return retval;
654 continue;
655 }
656
657 retval = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_SIB,
658 &extent);
659 if (retval == EXT2_ET_EXTENT_NO_NEXT)
660 goto go_down;
661 if (retval)
662 return retval;
663
Theodore Ts'o64cb68b2009-07-19 23:32:09 -0400664#ifdef DEBUG_GOTO_EXTENTS
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400665 dbg_print_extent("next", &extent);
Theodore Ts'o64cb68b2009-07-19 23:32:09 -0400666#endif
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400667 if (blk == extent.e_lblk)
668 goto go_down;
669 if (blk > extent.e_lblk)
670 continue;
671
672 retval = ext2fs_extent_get(handle, EXT2_EXTENT_PREV_SIB,
673 &extent);
674 if (retval)
675 return retval;
676
Theodore Ts'o64cb68b2009-07-19 23:32:09 -0400677#ifdef DEBUG_GOTO_EXTENTS
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400678 dbg_print_extent("prev", &extent);
Theodore Ts'o64cb68b2009-07-19 23:32:09 -0400679#endif
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400680
681 go_down:
682 retval = ext2fs_extent_get(handle, EXT2_EXTENT_DOWN,
683 &extent);
684 if (retval)
685 return retval;
686
Theodore Ts'o64cb68b2009-07-19 23:32:09 -0400687#ifdef DEBUG_GOTO_EXTENTS
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400688 dbg_print_extent("down", &extent);
Theodore Ts'o64cb68b2009-07-19 23:32:09 -0400689#endif
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400690 }
691}
692
693errcode_t ext2fs_extent_goto(ext2_extent_handle_t handle,
694 blk64_t blk)
695{
JP Abgralle0ed7402014-03-19 19:08:39 -0700696 return ext2fs_extent_goto2(handle, 0, blk);
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400697}
698
Eric Sandeenf4e99632008-05-20 10:17:46 -0500699/*
700 * Traverse back up to root fixing parents of current node as needed.
701 *
702 * If we changed start of first entry in a node, fix parent index start
703 * and so on.
704 *
705 * Safe to call for any position in node; if not at the first entry,
706 * will simply return.
707 */
JP Abgralle0ed7402014-03-19 19:08:39 -0700708errcode_t ext2fs_extent_fix_parents(ext2_extent_handle_t handle)
Eric Sandeenf4e99632008-05-20 10:17:46 -0500709{
710 int retval = 0;
JP Abgralle0ed7402014-03-19 19:08:39 -0700711 int orig_height;
Eric Sandeenf4e99632008-05-20 10:17:46 -0500712 blk64_t start;
713 struct extent_path *path;
714 struct ext2fs_extent extent;
JP Abgralle0ed7402014-03-19 19:08:39 -0700715 struct ext2_extent_info info;
Eric Sandeenf4e99632008-05-20 10:17:46 -0500716
717 EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
718
719 if (!(handle->fs->flags & EXT2_FLAG_RW))
720 return EXT2_ET_RO_FILSYS;
721
722 if (!handle->path)
723 return EXT2_ET_NO_CURRENT_NODE;
724
725 path = handle->path + handle->level;
726 if (!path->curr)
727 return EXT2_ET_NO_CURRENT_NODE;
728
729 retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent);
730 if (retval)
731 goto done;
732
733 /* modified node's start block */
734 start = extent.e_lblk;
735
JP Abgralle0ed7402014-03-19 19:08:39 -0700736 if ((retval = ext2fs_extent_get_info(handle, &info)))
737 return retval;
738 orig_height = info.max_depth - info.curr_level;
739
Eric Sandeenf4e99632008-05-20 10:17:46 -0500740 /* traverse up until index not first, or startblk matches, or top */
741 while (handle->level > 0 &&
742 (path->left == path->entries - 1)) {
743 retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP, &extent);
744 if (retval)
745 goto done;
746 if (extent.e_lblk == start)
747 break;
748 path = handle->path + handle->level;
749 extent.e_len += (extent.e_lblk - start);
750 extent.e_lblk = start;
751 retval = ext2fs_extent_replace(handle, 0, &extent);
752 if (retval)
753 goto done;
754 update_path(handle);
755 }
756
757 /* put handle back to where we started */
JP Abgralle0ed7402014-03-19 19:08:39 -0700758 retval = ext2fs_extent_goto2(handle, orig_height, start);
Eric Sandeenf4e99632008-05-20 10:17:46 -0500759done:
760 return retval;
761}
762
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400763errcode_t ext2fs_extent_replace(ext2_extent_handle_t handle,
Theodore Ts'o2d328bb2008-03-17 23:17:13 -0400764 int flags EXT2FS_ATTR((unused)),
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400765 struct ext2fs_extent *extent)
766{
767 struct extent_path *path;
768 struct ext3_extent_idx *ix;
769 struct ext3_extent *ex;
770
771 EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
772
773 if (!(handle->fs->flags & EXT2_FLAG_RW))
774 return EXT2_ET_RO_FILSYS;
775
776 if (!handle->path)
777 return EXT2_ET_NO_CURRENT_NODE;
778
779 path = handle->path + handle->level;
780 if (!path->curr)
781 return EXT2_ET_NO_CURRENT_NODE;
782
Theodore Ts'o64cb68b2009-07-19 23:32:09 -0400783#ifdef DEBUG
784 printf("extent replace: %u ", handle->ino);
785 dbg_print_extent(0, extent);
786#endif
787
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400788 if (handle->level == handle->max_depth) {
789 ex = path->curr;
790
791 ex->ee_block = ext2fs_cpu_to_le32(extent->e_lblk);
792 ex->ee_start = ext2fs_cpu_to_le32(extent->e_pblk & 0xFFFFFFFF);
793 ex->ee_start_hi = ext2fs_cpu_to_le16(extent->e_pblk >> 32);
Theodore Ts'o05a32de2008-06-01 22:56:37 -0400794 if (extent->e_flags & EXT2_EXTENT_FLAGS_UNINIT) {
795 if (extent->e_len > EXT_UNINIT_MAX_LEN)
796 return EXT2_ET_EXTENT_INVALID_LENGTH;
797 ex->ee_len = ext2fs_cpu_to_le16(extent->e_len +
798 EXT_INIT_MAX_LEN);
799 } else {
800 if (extent->e_len > EXT_INIT_MAX_LEN)
801 return EXT2_ET_EXTENT_INVALID_LENGTH;
802 ex->ee_len = ext2fs_cpu_to_le16(extent->e_len);
803 }
Theodore Ts'o3eb07f62007-10-14 23:04:58 -0400804 } else {
805 ix = path->curr;
806
807 ix->ei_leaf = ext2fs_cpu_to_le32(extent->e_pblk & 0xFFFFFFFF);
808 ix->ei_leaf_hi = ext2fs_cpu_to_le16(extent->e_pblk >> 32);
809 ix->ei_block = ext2fs_cpu_to_le32(extent->e_lblk);
810 ix->ei_unused = 0;
811 }
812 update_path(handle);
813 return 0;
814}
815
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500816/*
817 * allocate a new block, move half the current node to it, and update parent
818 *
819 * handle will be left pointing at original record.
820 */
JP Abgralle0ed7402014-03-19 19:08:39 -0700821errcode_t ext2fs_extent_node_split(ext2_extent_handle_t handle)
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500822{
823 errcode_t retval = 0;
JP Abgralle0ed7402014-03-19 19:08:39 -0700824 blk64_t new_node_pblk;
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500825 blk64_t new_node_start;
826 blk64_t orig_lblk;
827 blk64_t goal_blk = 0;
828 int orig_height;
829 char *block_buf = NULL;
830 struct ext2fs_extent extent;
831 struct extent_path *path, *newpath = 0;
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500832 struct ext3_extent_header *eh, *neweh;
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500833 int tocopy;
834 int new_root = 0;
835 struct ext2_extent_info info;
836
837 /* basic sanity */
838 EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
839
840 if (!(handle->fs->flags & EXT2_FLAG_RW))
841 return EXT2_ET_RO_FILSYS;
842
843 if (!handle->path)
844 return EXT2_ET_NO_CURRENT_NODE;
845
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400846#ifdef DEBUG
847 printf("splitting node at level %d\n", handle->level);
848#endif
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500849 retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent);
850 if (retval)
851 goto done;
852
853 retval = ext2fs_extent_get_info(handle, &info);
854 if (retval)
855 goto done;
856
857 /* save the position we were originally splitting... */
858 orig_height = info.max_depth - info.curr_level;
859 orig_lblk = extent.e_lblk;
860
861 /* Is there room in the parent for a new entry? */
862 if (handle->level &&
863 (handle->path[handle->level - 1].entries >=
864 handle->path[handle->level - 1].max_entries)) {
865
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400866#ifdef DEBUG
867 printf("parent level %d full; splitting it too\n",
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500868 handle->level - 1);
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400869#endif
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500870 /* split the parent */
871 retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP, &extent);
872 if (retval)
873 goto done;
874 goal_blk = extent.e_pblk;
875
JP Abgralle0ed7402014-03-19 19:08:39 -0700876 retval = ext2fs_extent_node_split(handle);
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500877 if (retval)
878 goto done;
879
880 /* get handle back to our original split position */
JP Abgralle0ed7402014-03-19 19:08:39 -0700881 retval = ext2fs_extent_goto2(handle, orig_height, orig_lblk);
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500882 if (retval)
883 goto done;
884 }
885
886 /* At this point, parent should have room for this split */
887 path = handle->path + handle->level;
888 if (!path->curr)
889 return EXT2_ET_NO_CURRENT_NODE;
890
891 /* extent header of the current node we'll split */
892 eh = (struct ext3_extent_header *)path->buf;
893
894 /* splitting root level means moving them all out */
895 if (handle->level == 0) {
896 new_root = 1;
897 tocopy = ext2fs_le16_to_cpu(eh->eh_entries);
898 retval = ext2fs_get_mem(((handle->max_depth+2) *
899 sizeof(struct extent_path)),
900 &newpath);
901 if (retval)
902 goto done;
903 memset(newpath, 0,
904 ((handle->max_depth+2) * sizeof(struct extent_path)));
905 } else {
906 tocopy = ext2fs_le16_to_cpu(eh->eh_entries) / 2;
907 }
908
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400909#ifdef DEBUG
910 printf("will copy out %d of %d entries at level %d\n",
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500911 tocopy, ext2fs_le16_to_cpu(eh->eh_entries),
912 handle->level);
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400913#endif
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500914
915 if (!tocopy) {
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400916#ifdef DEBUG
917 printf("Nothing to copy to new block!\n");
918#endif
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500919 retval = EXT2_ET_CANT_SPLIT_EXTENT;
920 goto done;
921 }
922
923 /* first we need a new block, or can do nothing. */
924 block_buf = malloc(handle->fs->blocksize);
925 if (!block_buf) {
926 retval = ENOMEM;
927 goto done;
928 }
929
930 if (!goal_blk) {
931 dgrp_t group = ext2fs_group_of_ino(handle->fs, handle->ino);
932 __u8 log_flex = handle->fs->super->s_log_groups_per_flex;
933
934 if (log_flex)
935 group = group & ~((1 << (log_flex)) - 1);
JP Abgralle0ed7402014-03-19 19:08:39 -0700936 goal_blk = ext2fs_group_first_block2(handle->fs, group);
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500937 }
JP Abgralle0ed7402014-03-19 19:08:39 -0700938 retval = ext2fs_alloc_block2(handle->fs, goal_blk, block_buf,
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500939 &new_node_pblk);
940 if (retval)
941 goto done;
942
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400943#ifdef DEBUG
Theodore Ts'o64cb68b2009-07-19 23:32:09 -0400944 printf("will copy to new node at block %lu\n",
945 (unsigned long) new_node_pblk);
Theodore Ts'o5a299b52008-07-13 14:19:14 -0400946#endif
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500947
948 /* Copy data into new block buffer */
949 /* First the header for the new block... */
950 neweh = (struct ext3_extent_header *) block_buf;
951 memcpy(neweh, eh, sizeof(struct ext3_extent_header));
952 neweh->eh_entries = ext2fs_cpu_to_le16(tocopy);
953 neweh->eh_max = ext2fs_cpu_to_le16((handle->fs->blocksize -
954 sizeof(struct ext3_extent_header)) /
955 sizeof(struct ext3_extent));
956
957 /* then the entries for the new block... */
958 memcpy(EXT_FIRST_INDEX(neweh),
959 EXT_FIRST_INDEX(eh) +
960 (ext2fs_le16_to_cpu(eh->eh_entries) - tocopy),
961 sizeof(struct ext3_extent_idx) * tocopy);
962
963 new_node_start = ext2fs_le32_to_cpu(EXT_FIRST_INDEX(neweh)->ei_block);
964
965 /* ...and write the new node block out to disk. */
JP Abgralle0ed7402014-03-19 19:08:39 -0700966 retval = io_channel_write_blk64(handle->fs->io, new_node_pblk, 1,
967 block_buf);
Eric Sandeen9fd6a962008-05-20 10:14:20 -0500968
969 if (retval)
970 goto done;
971
972 /* OK! we've created the new node; now adjust the tree */
973
974 /* current path now has fewer active entries, we copied some out */
975 if (handle->level == 0) {
976 memcpy(newpath, path,
977 sizeof(struct extent_path) * (handle->max_depth+1));
978 handle->path = newpath;
979 newpath = path;
980 path = handle->path;
981 path->entries = 1;
982 path->left = path->max_entries - 1;
983 handle->max_depth++;
984 eh->eh_depth = ext2fs_cpu_to_le16(handle->max_depth);
985 } else {
986 path->entries -= tocopy;
987 path->left -= tocopy;
988 }
989
990 eh->eh_entries = ext2fs_cpu_to_le16(path->entries);
991 /* this writes out the node, incl. the modified header */
992 retval = update_path(handle);
993 if (retval)
994 goto done;
995
996 /* now go up and insert/replace index for new node we created */
997 if (new_root) {
998 retval = ext2fs_extent_get(handle, EXT2_EXTENT_FIRST_SIB, &extent);
999 if (retval)
1000 goto done;
1001
1002 extent.e_lblk = new_node_start;
1003 extent.e_pblk = new_node_pblk;
1004 extent.e_len = handle->path[0].end_blk - extent.e_lblk;
1005 retval = ext2fs_extent_replace(handle, 0, &extent);
1006 if (retval)
1007 goto done;
1008 } else {
1009 __u32 new_node_length;
1010
1011 retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP, &extent);
1012 /* will insert after this one; it's length is shorter now */
1013 new_node_length = new_node_start - extent.e_lblk;
1014 extent.e_len -= new_node_length;
1015 retval = ext2fs_extent_replace(handle, 0, &extent);
1016 if (retval)
1017 goto done;
1018
1019 /* now set up the new extent and insert it */
1020 extent.e_lblk = new_node_start;
1021 extent.e_pblk = new_node_pblk;
1022 extent.e_len = new_node_length;
1023 retval = ext2fs_extent_insert(handle, EXT2_EXTENT_INSERT_AFTER, &extent);
1024 if (retval)
1025 goto done;
1026 }
1027
1028 /* get handle back to our original position */
JP Abgralle0ed7402014-03-19 19:08:39 -07001029 retval = ext2fs_extent_goto2(handle, orig_height, orig_lblk);
Eric Sandeen9fd6a962008-05-20 10:14:20 -05001030 if (retval)
1031 goto done;
1032
1033 /* new node hooked in, so update inode block count (do this here?) */
JP Abgralle0ed7402014-03-19 19:08:39 -07001034 handle->inode->i_blocks += (handle->fs->blocksize *
1035 EXT2FS_CLUSTER_RATIO(handle->fs)) / 512;
Eric Sandeen125a3672009-06-17 18:49:01 -04001036 retval = ext2fs_write_inode(handle->fs, handle->ino,
1037 handle->inode);
Eric Sandeen9fd6a962008-05-20 10:14:20 -05001038 if (retval)
1039 goto done;
1040
1041done:
1042 if (newpath)
1043 ext2fs_free_mem(&newpath);
Jim Meyering45e338f2009-02-23 18:07:50 +01001044 free(block_buf);
Eric Sandeen9fd6a962008-05-20 10:14:20 -05001045
1046 return retval;
1047}
1048
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001049errcode_t ext2fs_extent_insert(ext2_extent_handle_t handle, int flags,
1050 struct ext2fs_extent *extent)
1051{
1052 struct extent_path *path;
1053 struct ext3_extent_idx *ix;
1054 struct ext3_extent_header *eh;
1055 errcode_t retval;
1056
1057 EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
1058
1059 if (!(handle->fs->flags & EXT2_FLAG_RW))
1060 return EXT2_ET_RO_FILSYS;
1061
1062 if (!handle->path)
1063 return EXT2_ET_NO_CURRENT_NODE;
1064
Theodore Ts'o64cb68b2009-07-19 23:32:09 -04001065#ifdef DEBUG
1066 printf("extent insert: %u ", handle->ino);
1067 dbg_print_extent(0, extent);
1068#endif
1069
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001070 path = handle->path + handle->level;
1071
Eric Sandeen01229db2008-05-20 10:15:27 -05001072 if (path->entries >= path->max_entries) {
1073 if (flags & EXT2_EXTENT_INSERT_NOSPLIT) {
1074 return EXT2_ET_CANT_INSERT_EXTENT;
1075 } else {
Theodore Ts'o5a299b52008-07-13 14:19:14 -04001076#ifdef DEBUG
1077 printf("node full (level %d) - splitting\n",
Theodore Ts'o8895f432008-06-07 11:53:56 -04001078 handle->level);
Theodore Ts'o5a299b52008-07-13 14:19:14 -04001079#endif
JP Abgralle0ed7402014-03-19 19:08:39 -07001080 retval = ext2fs_extent_node_split(handle);
Eric Sandeen01229db2008-05-20 10:15:27 -05001081 if (retval)
Eric Sandeen53422e82009-05-20 16:36:26 -05001082 return retval;
Eric Sandeen01229db2008-05-20 10:15:27 -05001083 path = handle->path + handle->level;
1084 }
1085 }
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001086
1087 eh = (struct ext3_extent_header *) path->buf;
1088 if (path->curr) {
1089 ix = path->curr;
1090 if (flags & EXT2_EXTENT_INSERT_AFTER) {
1091 ix++;
1092 path->left--;
1093 }
1094 } else
1095 ix = EXT_FIRST_INDEX(eh);
1096
1097 path->curr = ix;
1098
Eric Sandeen5e057d52008-04-07 22:00:58 -05001099 if (path->left >= 0)
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001100 memmove(ix + 1, ix,
Theodore Ts'o252b7362008-03-14 10:42:50 -04001101 (path->left+1) * sizeof(struct ext3_extent_idx));
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001102 path->left++;
1103 path->entries++;
1104
1105 eh = (struct ext3_extent_header *) path->buf;
1106 eh->eh_entries = ext2fs_cpu_to_le16(path->entries);
1107
1108 retval = ext2fs_extent_replace(handle, 0, extent);
1109 if (retval)
1110 goto errout;
1111
1112 retval = update_path(handle);
1113 if (retval)
1114 goto errout;
1115
1116 return 0;
1117
1118errout:
1119 ext2fs_extent_delete(handle, 0);
1120 return retval;
1121}
1122
Eric Sandeenf4e99632008-05-20 10:17:46 -05001123/*
1124 * Sets the physical block for a logical file block in the extent tree.
1125 *
1126 * May: map unmapped, unmap mapped, or remap mapped blocks.
1127 *
1128 * Mapping an unmapped block adds a single-block extent.
1129 *
1130 * Unmapping first or last block modifies extent in-place
1131 * - But may need to fix parent's starts too in first-block case
1132 *
1133 * Mapping any unmapped block requires adding a (single-block) extent
1134 * and inserting into proper point in tree.
1135 *
1136 * Modifying (unmapping or remapping) a block in the middle
1137 * of an extent requires splitting the extent.
1138 * - Remapping case requires new single-block extent.
1139 *
1140 * Remapping first or last block adds an extent.
1141 *
1142 * We really need extent adding to be smart about merging.
1143 */
1144
1145errcode_t ext2fs_extent_set_bmap(ext2_extent_handle_t handle,
1146 blk64_t logical, blk64_t physical, int flags)
1147{
1148 errcode_t ec, retval = 0;
1149 int mapped = 1; /* logical is mapped? */
1150 int orig_height;
1151 int extent_uninit = 0;
Theodore Ts'o8b5ed492009-07-19 23:37:44 -04001152 int prev_uninit = 0;
1153 int next_uninit = 0;
Eric Sandeenf4e99632008-05-20 10:17:46 -05001154 int new_uninit = 0;
1155 int max_len = EXT_INIT_MAX_LEN;
Theodore Ts'o8b5ed492009-07-19 23:37:44 -04001156 int has_prev, has_next;
Eric Sandeenf4e99632008-05-20 10:17:46 -05001157 blk64_t orig_lblk;
1158 struct extent_path *path;
Theodore Ts'o8b5ed492009-07-19 23:37:44 -04001159 struct ext2fs_extent extent, next_extent, prev_extent;
Eric Sandeenf4e99632008-05-20 10:17:46 -05001160 struct ext2fs_extent newextent;
1161 struct ext2_extent_info info;
1162
1163 EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
1164
Theodore Ts'o64cb68b2009-07-19 23:32:09 -04001165#ifdef DEBUG
1166 printf("set_bmap ino %u log %lld phys %lld flags %d\n",
1167 handle->ino, logical, physical, flags);
1168#endif
1169
Eric Sandeenf4e99632008-05-20 10:17:46 -05001170 if (!(handle->fs->flags & EXT2_FLAG_RW))
1171 return EXT2_ET_RO_FILSYS;
1172
1173 if (!handle->path)
1174 return EXT2_ET_NO_CURRENT_NODE;
1175
1176 path = handle->path + handle->level;
1177
1178 if (flags & EXT2_EXTENT_SET_BMAP_UNINIT) {
1179 new_uninit = 1;
1180 max_len = EXT_UNINIT_MAX_LEN;
1181 }
1182
1183 /* if (re)mapping, set up new extent to insert */
1184 if (physical) {
1185 newextent.e_len = 1;
1186 newextent.e_pblk = physical;
1187 newextent.e_lblk = logical;
1188 newextent.e_flags = EXT2_EXTENT_FLAGS_LEAF;
1189 if (new_uninit)
1190 newextent.e_flags |= EXT2_EXTENT_FLAGS_UNINIT;
1191 }
1192
1193 /* special case if the extent tree is completely empty */
1194 if ((handle->max_depth == 0) && (path->entries == 0)) {
1195 retval = ext2fs_extent_insert(handle, 0, &newextent);
Theodore Ts'o8895f432008-06-07 11:53:56 -04001196 return retval;
Eric Sandeenf4e99632008-05-20 10:17:46 -05001197 }
1198
1199 /* save our original location in the extent tree */
1200 if ((retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT,
1201 &extent))) {
1202 if (retval != EXT2_ET_NO_CURRENT_NODE)
1203 return retval;
1204 memset(&extent, 0, sizeof(extent));
1205 }
1206 if ((retval = ext2fs_extent_get_info(handle, &info)))
1207 return retval;
1208 orig_height = info.max_depth - info.curr_level;
1209 orig_lblk = extent.e_lblk;
1210
1211 /* go to the logical spot we want to (re/un)map */
1212 retval = ext2fs_extent_goto(handle, logical);
1213 if (retval) {
1214 if (retval == EXT2_ET_EXTENT_NOT_FOUND) {
1215 retval = 0;
1216 mapped = 0;
1217 if (!physical) {
Theodore Ts'o5a299b52008-07-13 14:19:14 -04001218#ifdef DEBUG
1219 printf("block %llu already unmapped\n",
Theodore Ts'o8895f432008-06-07 11:53:56 -04001220 logical);
Theodore Ts'o5a299b52008-07-13 14:19:14 -04001221#endif
Eric Sandeenf4e99632008-05-20 10:17:46 -05001222 goto done;
1223 }
1224 } else
1225 goto done;
1226 }
1227
1228 /*
1229 * This may be the extent *before* the requested logical,
1230 * if it's currently unmapped.
Theodore Ts'o8b5ed492009-07-19 23:37:44 -04001231 *
1232 * Get the previous and next leaf extents, if they are present.
Eric Sandeenf4e99632008-05-20 10:17:46 -05001233 */
1234 retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent);
1235 if (retval)
1236 goto done;
1237 if (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT)
1238 extent_uninit = 1;
Theodore Ts'o8b5ed492009-07-19 23:37:44 -04001239 retval = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_LEAF, &next_extent);
1240 if (retval) {
1241 has_next = 0;
1242 if (retval != EXT2_ET_EXTENT_NO_NEXT)
1243 goto done;
1244 } else {
1245 dbg_print_extent("set_bmap: next_extent",
1246 &next_extent);
1247 has_next = 1;
1248 if (next_extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT)
1249 next_uninit = 1;
1250 }
1251 retval = ext2fs_extent_goto(handle, logical);
1252 if (retval && retval != EXT2_ET_EXTENT_NOT_FOUND)
1253 goto done;
1254 retval = ext2fs_extent_get(handle, EXT2_EXTENT_PREV_LEAF, &prev_extent);
1255 if (retval) {
1256 has_prev = 0;
1257 if (retval != EXT2_ET_EXTENT_NO_PREV)
1258 goto done;
1259 } else {
1260 has_prev = 1;
1261 dbg_print_extent("set_bmap: prev_extent",
1262 &prev_extent);
1263 if (prev_extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT)
1264 prev_uninit = 1;
1265 }
1266 retval = ext2fs_extent_goto(handle, logical);
1267 if (retval && retval != EXT2_ET_EXTENT_NOT_FOUND)
1268 goto done;
Eric Sandeenf4e99632008-05-20 10:17:46 -05001269
1270 /* check if already pointing to the requested physical */
1271 if (mapped && (new_uninit == extent_uninit) &&
1272 (extent.e_pblk + (logical - extent.e_lblk) == physical)) {
Theodore Ts'o5a299b52008-07-13 14:19:14 -04001273#ifdef DEBUG
1274 printf("physical block (at %llu) unchanged\n", logical);
1275#endif
Eric Sandeenf4e99632008-05-20 10:17:46 -05001276 goto done;
1277 }
1278
1279 if (!mapped) {
Theodore Ts'o5a299b52008-07-13 14:19:14 -04001280#ifdef DEBUG
1281 printf("mapping unmapped logical block %llu\n", logical);
1282#endif
Eric Sandeenf4e99632008-05-20 10:17:46 -05001283 if ((logical == extent.e_lblk + extent.e_len) &&
1284 (physical == extent.e_pblk + extent.e_len) &&
1285 (new_uninit == extent_uninit) &&
Theodore Ts'o8895f432008-06-07 11:53:56 -04001286 ((int) extent.e_len < max_len-1)) {
Eric Sandeenf4e99632008-05-20 10:17:46 -05001287 extent.e_len++;
1288 retval = ext2fs_extent_replace(handle, 0, &extent);
Theodore Ts'o8b5ed492009-07-19 23:37:44 -04001289 } else if ((logical == extent.e_lblk - 1) &&
1290 (physical == extent.e_pblk - 1) &&
1291 (new_uninit == extent_uninit) &&
1292 ((int) extent.e_len < max_len - 1)) {
1293 extent.e_len++;
1294 extent.e_lblk--;
1295 extent.e_pblk--;
1296 retval = ext2fs_extent_replace(handle, 0, &extent);
1297 } else if (has_next &&
1298 (logical == next_extent.e_lblk - 1) &&
1299 (physical == next_extent.e_pblk - 1) &&
1300 (new_uninit == next_uninit) &&
1301 ((int) next_extent.e_len < max_len - 1)) {
1302 retval = ext2fs_extent_get(handle,
1303 EXT2_EXTENT_NEXT_LEAF,
1304 &next_extent);
1305 if (retval)
1306 goto done;
1307 next_extent.e_len++;
1308 next_extent.e_lblk--;
1309 next_extent.e_pblk--;
1310 retval = ext2fs_extent_replace(handle, 0, &next_extent);
Theodore Ts'oa9ea7282008-08-22 02:32:28 -04001311 } else if (logical < extent.e_lblk)
1312 retval = ext2fs_extent_insert(handle, 0, &newextent);
1313 else
Eric Sandeenf4e99632008-05-20 10:17:46 -05001314 retval = ext2fs_extent_insert(handle,
1315 EXT2_EXTENT_INSERT_AFTER, &newextent);
1316 if (retval)
1317 goto done;
1318 retval = ext2fs_extent_fix_parents(handle);
1319 if (retval)
1320 goto done;
1321 } else if ((logical == extent.e_lblk) && (extent.e_len == 1)) {
Theodore Ts'o5a299b52008-07-13 14:19:14 -04001322#ifdef DEBUG
1323 printf("(re/un)mapping only block in extent\n");
1324#endif
Eric Sandeenf4e99632008-05-20 10:17:46 -05001325 if (physical) {
Theodore Ts'o2047e0a2008-08-22 02:39:46 -04001326 retval = ext2fs_extent_replace(handle, 0, &newextent);
Eric Sandeenf4e99632008-05-20 10:17:46 -05001327 } else {
1328 retval = ext2fs_extent_delete(handle, 0);
1329 if (retval)
1330 goto done;
1331 ec = ext2fs_extent_fix_parents(handle);
1332 if (ec != EXT2_ET_NO_CURRENT_NODE)
1333 retval = ec;
1334 }
1335
1336 if (retval)
1337 goto done;
1338 } else if (logical == extent.e_lblk + extent.e_len - 1) {
Theodore Ts'o5a299b52008-07-13 14:19:14 -04001339#ifdef DEBUG
1340 printf("(re/un)mapping last block in extent\n");
1341#endif
Eric Sandeenf4e99632008-05-20 10:17:46 -05001342 if (physical) {
Theodore Ts'o8b5ed492009-07-19 23:37:44 -04001343 if (has_next &&
1344 (logical == (next_extent.e_lblk - 1)) &&
1345 (physical == (next_extent.e_pblk - 1)) &&
1346 (new_uninit == next_uninit) &&
1347 ((int) next_extent.e_len < max_len - 1)) {
1348 retval = ext2fs_extent_get(handle,
1349 EXT2_EXTENT_NEXT_LEAF, &next_extent);
1350 if (retval)
1351 goto done;
1352 next_extent.e_len++;
1353 next_extent.e_lblk--;
1354 next_extent.e_pblk--;
1355 retval = ext2fs_extent_replace(handle, 0,
1356 &next_extent);
1357 if (retval)
1358 goto done;
JP Abgralle0ed7402014-03-19 19:08:39 -07001359 retval = ext2fs_extent_fix_parents(handle);
1360 if (retval)
1361 goto done;
Theodore Ts'o8b5ed492009-07-19 23:37:44 -04001362 } else
1363 retval = ext2fs_extent_insert(handle,
1364 EXT2_EXTENT_INSERT_AFTER, &newextent);
Eric Sandeenf4e99632008-05-20 10:17:46 -05001365 if (retval)
1366 goto done;
Eric Sandeen4bd87f22009-07-07 14:30:32 -05001367 /* Now pointing at inserted extent; move back to prev */
Theodore Ts'o8b5ed492009-07-19 23:37:44 -04001368 retval = ext2fs_extent_get(handle,
1369 EXT2_EXTENT_PREV_LEAF,
1370 &extent);
Eric Sandeen4bd87f22009-07-07 14:30:32 -05001371 if (retval)
1372 goto done;
Eric Sandeenf4e99632008-05-20 10:17:46 -05001373 }
Eric Sandeen53422e82009-05-20 16:36:26 -05001374 extent.e_len--;
1375 retval = ext2fs_extent_replace(handle, 0, &extent);
1376 if (retval)
1377 goto done;
Eric Sandeenf4e99632008-05-20 10:17:46 -05001378 } else if (logical == extent.e_lblk) {
Theodore Ts'o5a299b52008-07-13 14:19:14 -04001379#ifdef DEBUG
1380 printf("(re/un)mapping first block in extent\n");
1381#endif
Theodore Ts'o0dc29162009-07-10 00:19:28 -04001382 if (physical) {
Theodore Ts'o8b5ed492009-07-19 23:37:44 -04001383 if (has_prev &&
1384 (logical == (prev_extent.e_lblk +
1385 prev_extent.e_len)) &&
1386 (physical == (prev_extent.e_pblk +
1387 prev_extent.e_len)) &&
1388 (new_uninit == prev_uninit) &&
1389 ((int) prev_extent.e_len < max_len-1)) {
1390 retval = ext2fs_extent_get(handle,
1391 EXT2_EXTENT_PREV_LEAF, &prev_extent);
Theodore Ts'o0dc29162009-07-10 00:19:28 -04001392 if (retval)
1393 goto done;
Theodore Ts'o8b5ed492009-07-19 23:37:44 -04001394 prev_extent.e_len++;
1395 retval = ext2fs_extent_replace(handle, 0,
1396 &prev_extent);
1397 } else
Theodore Ts'o0dc29162009-07-10 00:19:28 -04001398 retval = ext2fs_extent_insert(handle,
1399 0, &newextent);
Theodore Ts'o0dc29162009-07-10 00:19:28 -04001400 if (retval)
1401 goto done;
Theodore Ts'o16e470e2009-07-19 22:43:33 -04001402 retval = ext2fs_extent_get(handle,
1403 EXT2_EXTENT_NEXT_LEAF,
1404 &extent);
1405 if (retval)
1406 goto done;
Theodore Ts'o0dc29162009-07-10 00:19:28 -04001407 }
Eric Sandeenf4e99632008-05-20 10:17:46 -05001408 extent.e_pblk++;
1409 extent.e_lblk++;
1410 extent.e_len--;
1411 retval = ext2fs_extent_replace(handle, 0, &extent);
1412 if (retval)
1413 goto done;
JP Abgralle0ed7402014-03-19 19:08:39 -07001414 retval = ext2fs_extent_fix_parents(handle);
1415 if (retval)
1416 goto done;
Eric Sandeenf4e99632008-05-20 10:17:46 -05001417 } else {
1418 __u32 orig_length;
1419
Theodore Ts'o5a299b52008-07-13 14:19:14 -04001420#ifdef DEBUG
1421 printf("(re/un)mapping in middle of extent\n");
1422#endif
Eric Sandeenf4e99632008-05-20 10:17:46 -05001423 /* need to split this extent; later */
1424
1425 orig_length = extent.e_len;
1426
1427 /* shorten pre-split extent */
1428 extent.e_len = (logical - extent.e_lblk);
1429 retval = ext2fs_extent_replace(handle, 0, &extent);
1430 if (retval)
1431 goto done;
1432 /* insert our new extent, if any */
1433 if (physical) {
1434 /* insert new extent after current */
1435 retval = ext2fs_extent_insert(handle,
1436 EXT2_EXTENT_INSERT_AFTER, &newextent);
1437 if (retval)
1438 goto done;
1439 }
1440 /* add post-split extent */
1441 extent.e_pblk += extent.e_len + 1;
1442 extent.e_lblk += extent.e_len + 1;
1443 extent.e_len = orig_length - extent.e_len - 1;
1444 retval = ext2fs_extent_insert(handle,
1445 EXT2_EXTENT_INSERT_AFTER, &extent);
1446 if (retval)
1447 goto done;
1448 }
1449
1450done:
1451 /* get handle back to its position */
1452 if (orig_height > handle->max_depth)
1453 orig_height = handle->max_depth; /* In case we shortened the tree */
JP Abgralle0ed7402014-03-19 19:08:39 -07001454 ext2fs_extent_goto2(handle, orig_height, orig_lblk);
Eric Sandeenf4e99632008-05-20 10:17:46 -05001455 return retval;
1456}
1457
Eric Sandeenc802ad92008-05-20 16:13:41 -05001458errcode_t ext2fs_extent_delete(ext2_extent_handle_t handle, int flags)
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001459{
1460 struct extent_path *path;
1461 char *cp;
1462 struct ext3_extent_header *eh;
Theodore Ts'o8895f432008-06-07 11:53:56 -04001463 errcode_t retval = 0;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001464
1465 EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
1466
1467 if (!(handle->fs->flags & EXT2_FLAG_RW))
1468 return EXT2_ET_RO_FILSYS;
1469
1470 if (!handle->path)
1471 return EXT2_ET_NO_CURRENT_NODE;
1472
Theodore Ts'o64cb68b2009-07-19 23:32:09 -04001473#ifdef DEBUG
1474 {
1475 struct ext2fs_extent extent;
1476
1477 retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT,
1478 &extent);
1479 if (retval == 0) {
1480 printf("extent delete %u ", handle->ino);
1481 dbg_print_extent(0, &extent);
1482 }
1483 }
1484#endif
1485
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001486 path = handle->path + handle->level;
1487 if (!path->curr)
1488 return EXT2_ET_NO_CURRENT_NODE;
1489
1490 cp = path->curr;
1491
1492 if (path->left) {
1493 memmove(cp, cp + sizeof(struct ext3_extent_idx),
1494 path->left * sizeof(struct ext3_extent_idx));
1495 path->left--;
Theodore Ts'o83d9be12008-03-13 23:55:40 -04001496 } else {
1497 struct ext3_extent_idx *ix = path->curr;
1498 ix--;
1499 path->curr = ix;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001500 }
Eric Sandeenc802ad92008-05-20 16:13:41 -05001501 if (--path->entries == 0)
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001502 path->curr = 0;
1503
Eric Sandeenc802ad92008-05-20 16:13:41 -05001504 /* if non-root node has no entries left, remove it & parent ptr to it */
1505 if (path->entries == 0 && handle->level) {
1506 if (!(flags & EXT2_EXTENT_DELETE_KEEP_EMPTY)) {
1507 struct ext2fs_extent extent;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001508
Eric Sandeenc802ad92008-05-20 16:13:41 -05001509 retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP,
1510 &extent);
1511 if (retval)
1512 return retval;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001513
Eric Sandeenc802ad92008-05-20 16:13:41 -05001514 retval = ext2fs_extent_delete(handle, flags);
JP Abgralle0ed7402014-03-19 19:08:39 -07001515 handle->inode->i_blocks -=
1516 (handle->fs->blocksize *
1517 EXT2FS_CLUSTER_RATIO(handle->fs)) / 512;
Eric Sandeen125a3672009-06-17 18:49:01 -04001518 retval = ext2fs_write_inode(handle->fs, handle->ino,
1519 handle->inode);
JP Abgralle0ed7402014-03-19 19:08:39 -07001520 ext2fs_block_alloc_stats2(handle->fs,
1521 extent.e_pblk, -1);
Eric Sandeenc802ad92008-05-20 16:13:41 -05001522 }
1523 } else {
1524 eh = (struct ext3_extent_header *) path->buf;
1525 eh->eh_entries = ext2fs_cpu_to_le16(path->entries);
1526 if ((path->entries == 0) && (handle->level == 0))
1527 eh->eh_depth = handle->max_depth = 0;
1528 retval = update_path(handle);
1529 }
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001530 return retval;
1531}
1532
1533errcode_t ext2fs_extent_get_info(ext2_extent_handle_t handle,
1534 struct ext2_extent_info *info)
1535{
1536 struct extent_path *path;
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001537
1538 EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
1539
1540 memset(info, 0, sizeof(struct ext2_extent_info));
1541
1542 path = handle->path + handle->level;
1543 if (path) {
1544 if (path->curr)
1545 info->curr_entry = ((char *) path->curr - path->buf) /
1546 sizeof(struct ext3_extent_idx);
1547 else
1548 info->curr_entry = 0;
1549 info->num_entries = path->entries;
1550 info->max_entries = path->max_entries;
1551 info->bytes_avail = (path->max_entries - path->entries) *
1552 sizeof(struct ext3_extent);
1553 }
1554
1555 info->curr_level = handle->level;
1556 info->max_depth = handle->max_depth;
1557 info->max_lblk = ((__u64) 1 << 32) - 1;
1558 info->max_pblk = ((__u64) 1 << 48) - 1;
1559 info->max_len = (1UL << 15);
1560 info->max_uninit_len = (1UL << 15) - 1;
1561
1562 return 0;
1563}
1564
1565#ifdef DEBUG
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001566/*
JP Abgralle0ed7402014-03-19 19:08:39 -07001567 * Override debugfs's prompt
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001568 */
Theodore Ts'o2d328bb2008-03-17 23:17:13 -04001569const char *debug_prog_name = "tst_extents";
JP Abgrall65f0aab2014-03-06 13:50:20 -08001570
Theodore Ts'o3eb07f62007-10-14 23:04:58 -04001571#endif
1572