2007-10-15 07:04:58 +04:00
|
|
|
/*
|
|
|
|
* extent.c --- routines to implement extents support
|
|
|
|
*
|
|
|
|
* Copyright (C) 2007 Theodore Ts'o.
|
|
|
|
*
|
|
|
|
* %Begin-Header%
|
2010-05-18 05:31:56 +04:00
|
|
|
* This file may be redistributed under the terms of the GNU Library
|
|
|
|
* General Public License, version 2.
|
2007-10-15 07:04:58 +04:00
|
|
|
* %End-Header%
|
|
|
|
*/
|
|
|
|
|
2011-09-19 01:34:37 +04:00
|
|
|
#include "config.h"
|
2007-10-15 07:04:58 +04:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#if HAVE_UNISTD_H
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
#if HAVE_ERRNO_H
|
|
|
|
#include <errno.h>
|
|
|
|
#endif
|
|
|
|
#if HAVE_SYS_STAT_H
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#endif
|
|
|
|
#if HAVE_SYS_TYPES_H
|
|
|
|
#include <sys/types.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "ext2_fs.h"
|
|
|
|
#include "ext2fsP.h"
|
|
|
|
#include "e2image.h"
|
|
|
|
|
2014-05-02 03:15:05 +04:00
|
|
|
#undef DEBUG
|
|
|
|
|
2007-10-15 07:04:58 +04:00
|
|
|
/*
|
|
|
|
* Definitions to be dropped in lib/ext2fs/ext2fs.h
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Private definitions
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct extent_path {
|
|
|
|
char *buf;
|
|
|
|
int entries;
|
|
|
|
int max_entries;
|
|
|
|
int left;
|
|
|
|
int visit_num;
|
|
|
|
int flags;
|
|
|
|
blk64_t end_blk;
|
|
|
|
void *curr;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct ext2_extent_handle {
|
|
|
|
errcode_t magic;
|
|
|
|
ext2_filsys fs;
|
|
|
|
ext2_ino_t ino;
|
|
|
|
struct ext2_inode *inode;
|
2010-07-22 17:27:45 +04:00
|
|
|
struct ext2_inode inodebuf;
|
2007-10-15 07:04:58 +04:00
|
|
|
int type;
|
|
|
|
int level;
|
|
|
|
int max_depth;
|
|
|
|
struct extent_path *path;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ext2_extent_path {
|
|
|
|
errcode_t magic;
|
|
|
|
int leaf_height;
|
|
|
|
blk64_t lblk;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Useful Debugging stuff
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
static void dbg_show_header(struct ext3_extent_header *eh)
|
|
|
|
{
|
|
|
|
printf("header: magic=%x entries=%u max=%u depth=%u generation=%u\n",
|
2008-07-18 16:05:40 +04:00
|
|
|
ext2fs_le16_to_cpu(eh->eh_magic),
|
|
|
|
ext2fs_le16_to_cpu(eh->eh_entries),
|
|
|
|
ext2fs_le16_to_cpu(eh->eh_max),
|
|
|
|
ext2fs_le16_to_cpu(eh->eh_depth),
|
|
|
|
ext2fs_le32_to_cpu(eh->eh_generation));
|
2007-10-15 07:04:58 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void dbg_show_index(struct ext3_extent_idx *ix)
|
|
|
|
{
|
|
|
|
printf("index: block=%u leaf=%u leaf_hi=%u unused=%u\n",
|
2008-07-18 16:05:40 +04:00
|
|
|
ext2fs_le32_to_cpu(ix->ei_block),
|
|
|
|
ext2fs_le32_to_cpu(ix->ei_leaf),
|
|
|
|
ext2fs_le16_to_cpu(ix->ei_leaf_hi),
|
|
|
|
ext2fs_le16_to_cpu(ix->ei_unused));
|
2007-10-15 07:04:58 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void dbg_show_extent(struct ext3_extent *ex)
|
|
|
|
{
|
|
|
|
printf("extent: block=%u-%u len=%u start=%u start_hi=%u\n",
|
2008-07-18 16:05:40 +04:00
|
|
|
ext2fs_le32_to_cpu(ex->ee_block),
|
|
|
|
ext2fs_le32_to_cpu(ex->ee_block) +
|
|
|
|
ext2fs_le16_to_cpu(ex->ee_len) - 1,
|
|
|
|
ext2fs_le16_to_cpu(ex->ee_len),
|
|
|
|
ext2fs_le32_to_cpu(ex->ee_start),
|
|
|
|
ext2fs_le16_to_cpu(ex->ee_start_hi));
|
2007-10-15 07:04:58 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void dbg_print_extent(char *desc, struct ext2fs_extent *extent)
|
|
|
|
{
|
|
|
|
if (desc)
|
|
|
|
printf("%s: ", desc);
|
2009-07-20 07:32:09 +04:00
|
|
|
printf("extent: lblk %llu--%llu, len %u, pblk %llu, flags: ",
|
2007-10-15 07:04:58 +04:00
|
|
|
extent->e_lblk, extent->e_lblk + extent->e_len - 1,
|
|
|
|
extent->e_len, extent->e_pblk);
|
|
|
|
if (extent->e_flags & EXT2_EXTENT_FLAGS_LEAF)
|
|
|
|
fputs("LEAF ", stdout);
|
|
|
|
if (extent->e_flags & EXT2_EXTENT_FLAGS_UNINIT)
|
|
|
|
fputs("UNINIT ", stdout);
|
|
|
|
if (extent->e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT)
|
|
|
|
fputs("2ND_VISIT ", stdout);
|
|
|
|
if (!extent->e_flags)
|
|
|
|
fputs("(none)", stdout);
|
|
|
|
fputc('\n', stdout);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-05-02 03:15:05 +04:00
|
|
|
static void dump_path(const char *tag, struct ext2_extent_handle *handle,
|
|
|
|
struct extent_path *path)
|
|
|
|
{
|
|
|
|
struct extent_path *ppp = path;
|
|
|
|
printf("%s: level=%d\n", tag, handle->level);
|
|
|
|
|
|
|
|
do {
|
|
|
|
printf("%s: path=%ld buf=%p entries=%d max_entries=%d left=%d "
|
|
|
|
"visit_num=%d flags=0x%x end_blk=%llu curr=%p(%ld)\n",
|
|
|
|
tag, (ppp - handle->path), ppp->buf, ppp->entries,
|
|
|
|
ppp->max_entries, ppp->left, ppp->visit_num, ppp->flags,
|
|
|
|
ppp->end_blk, ppp->curr, ppp->curr - (void *)ppp->buf);
|
|
|
|
printf(" ");
|
|
|
|
dbg_show_header((struct ext3_extent_header *)ppp->buf);
|
|
|
|
if (ppp->curr) {
|
|
|
|
printf(" ");
|
|
|
|
dbg_show_index(ppp->curr);
|
|
|
|
printf(" ");
|
|
|
|
dbg_show_extent(ppp->curr);
|
|
|
|
}
|
|
|
|
ppp--;
|
|
|
|
} while (ppp >= handle->path);
|
|
|
|
fflush(stdout);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-10-15 07:04:58 +04:00
|
|
|
#else
|
|
|
|
#define dbg_show_header(eh) do { } while (0)
|
|
|
|
#define dbg_show_index(ix) do { } while (0)
|
|
|
|
#define dbg_show_extent(ex) do { } while (0)
|
|
|
|
#define dbg_print_extent(desc, ex) do { } while (0)
|
2014-05-02 03:15:05 +04:00
|
|
|
#define dump_path(tag, handle, path) do { } while (0)
|
2007-10-15 07:04:58 +04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Verify the extent header as being sane
|
|
|
|
*/
|
|
|
|
errcode_t ext2fs_extent_header_verify(void *ptr, int size)
|
|
|
|
{
|
|
|
|
int eh_max, entry_size;
|
|
|
|
struct ext3_extent_header *eh = ptr;
|
|
|
|
|
|
|
|
dbg_show_header(eh);
|
|
|
|
if (ext2fs_le16_to_cpu(eh->eh_magic) != EXT3_EXT_MAGIC)
|
|
|
|
return EXT2_ET_EXTENT_HEADER_BAD;
|
|
|
|
if (ext2fs_le16_to_cpu(eh->eh_entries) > ext2fs_le16_to_cpu(eh->eh_max))
|
|
|
|
return EXT2_ET_EXTENT_HEADER_BAD;
|
|
|
|
if (eh->eh_depth == 0)
|
|
|
|
entry_size = sizeof(struct ext3_extent);
|
|
|
|
else
|
|
|
|
entry_size = sizeof(struct ext3_extent_idx);
|
|
|
|
|
|
|
|
eh_max = (size - sizeof(*eh)) / entry_size;
|
|
|
|
/* Allow two extent-sized items at the end of the block, for
|
|
|
|
* ext4_extent_tail with checksum in the future. */
|
2008-08-28 07:07:54 +04:00
|
|
|
if ((ext2fs_le16_to_cpu(eh->eh_max) > eh_max) ||
|
2007-10-15 07:04:58 +04:00
|
|
|
(ext2fs_le16_to_cpu(eh->eh_max) < (eh_max - 2)))
|
|
|
|
return EXT2_ET_EXTENT_HEADER_BAD;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Begin functions to handle an inode's extent information
|
|
|
|
*/
|
2013-12-17 03:56:36 +04:00
|
|
|
void ext2fs_extent_free(ext2_extent_handle_t handle)
|
2007-10-15 07:04:58 +04:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!handle)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (handle->path) {
|
2009-06-15 09:34:43 +04:00
|
|
|
for (i=1; i <= handle->max_depth; i++) {
|
2007-10-15 07:04:58 +04:00
|
|
|
if (handle->path[i].buf)
|
|
|
|
ext2fs_free_mem(&handle->path[i].buf);
|
|
|
|
}
|
2008-02-21 18:39:23 +03:00
|
|
|
ext2fs_free_mem(&handle->path);
|
2007-10-15 07:04:58 +04:00
|
|
|
}
|
|
|
|
ext2fs_free_mem(&handle);
|
|
|
|
}
|
|
|
|
|
2013-12-17 03:56:36 +04:00
|
|
|
errcode_t ext2fs_extent_open(ext2_filsys fs, ext2_ino_t ino,
|
2007-10-15 07:04:58 +04:00
|
|
|
ext2_extent_handle_t *ret_handle)
|
2009-05-20 00:34:12 +04:00
|
|
|
{
|
|
|
|
return ext2fs_extent_open2(fs, ino, NULL, ret_handle);
|
|
|
|
}
|
|
|
|
|
2013-12-17 03:56:36 +04:00
|
|
|
errcode_t ext2fs_extent_open2(ext2_filsys fs, ext2_ino_t ino,
|
2009-05-20 00:34:12 +04:00
|
|
|
struct ext2_inode *inode,
|
|
|
|
ext2_extent_handle_t *ret_handle)
|
2007-10-15 07:04:58 +04:00
|
|
|
{
|
|
|
|
struct ext2_extent_handle *handle;
|
|
|
|
errcode_t retval;
|
2008-06-02 08:08:19 +04:00
|
|
|
int i;
|
2007-10-15 07:04:58 +04:00
|
|
|
struct ext3_extent_header *eh;
|
|
|
|
|
|
|
|
EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
|
|
|
|
|
2009-05-20 00:34:12 +04:00
|
|
|
if (!inode)
|
|
|
|
if ((ino == 0) || (ino > fs->super->s_inodes_count))
|
|
|
|
return EXT2_ET_BAD_INODE_NUM;
|
2007-10-15 07:04:58 +04:00
|
|
|
|
|
|
|
retval = ext2fs_get_mem(sizeof(struct ext2_extent_handle), &handle);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
memset(handle, 0, sizeof(struct ext2_extent_handle));
|
|
|
|
|
|
|
|
handle->ino = ino;
|
|
|
|
handle->fs = fs;
|
|
|
|
|
2009-05-20 00:34:12 +04:00
|
|
|
if (inode) {
|
2010-07-22 17:27:45 +04:00
|
|
|
handle->inode = inode;
|
|
|
|
} else {
|
|
|
|
handle->inode = &handle->inodebuf;
|
2009-05-20 00:34:12 +04:00
|
|
|
retval = ext2fs_read_inode(fs, ino, handle->inode);
|
|
|
|
if (retval)
|
|
|
|
goto errout;
|
|
|
|
}
|
2007-10-15 07:04:58 +04:00
|
|
|
|
2008-06-02 08:08:19 +04:00
|
|
|
eh = (struct ext3_extent_header *) &handle->inode->i_block[0];
|
|
|
|
|
|
|
|
for (i=0; i < EXT2_N_BLOCKS; i++)
|
|
|
|
if (handle->inode->i_block[i])
|
|
|
|
break;
|
|
|
|
if (i >= EXT2_N_BLOCKS) {
|
|
|
|
eh->eh_magic = ext2fs_cpu_to_le16(EXT3_EXT_MAGIC);
|
|
|
|
eh->eh_depth = 0;
|
|
|
|
eh->eh_entries = 0;
|
|
|
|
i = (sizeof(handle->inode->i_block) - sizeof(*eh)) /
|
|
|
|
sizeof(struct ext3_extent);
|
|
|
|
eh->eh_max = ext2fs_cpu_to_le16(i);
|
|
|
|
handle->inode->i_flags |= EXT4_EXTENTS_FL;
|
|
|
|
}
|
|
|
|
|
2008-08-16 00:56:43 +04:00
|
|
|
if (!(handle->inode->i_flags & EXT4_EXTENTS_FL)) {
|
|
|
|
retval = EXT2_ET_INODE_NOT_EXTENT;
|
|
|
|
goto errout;
|
|
|
|
}
|
2007-10-15 07:04:58 +04:00
|
|
|
|
|
|
|
retval = ext2fs_extent_header_verify(eh, sizeof(handle->inode->i_block));
|
|
|
|
if (retval)
|
2008-08-16 00:56:43 +04:00
|
|
|
goto errout;
|
2007-10-15 07:04:58 +04:00
|
|
|
|
|
|
|
handle->max_depth = ext2fs_le16_to_cpu(eh->eh_depth);
|
|
|
|
handle->type = ext2fs_le16_to_cpu(eh->eh_magic);
|
|
|
|
|
|
|
|
retval = ext2fs_get_mem(((handle->max_depth+1) *
|
|
|
|
sizeof(struct extent_path)),
|
|
|
|
&handle->path);
|
|
|
|
memset(handle->path, 0,
|
|
|
|
(handle->max_depth+1) * sizeof(struct extent_path));
|
|
|
|
handle->path[0].buf = (char *) handle->inode->i_block;
|
|
|
|
|
|
|
|
handle->path[0].left = handle->path[0].entries =
|
|
|
|
ext2fs_le16_to_cpu(eh->eh_entries);
|
|
|
|
handle->path[0].max_entries = ext2fs_le16_to_cpu(eh->eh_max);
|
|
|
|
handle->path[0].curr = 0;
|
|
|
|
handle->path[0].end_blk =
|
2011-07-09 03:54:57 +04:00
|
|
|
(EXT2_I_SIZE(handle->inode) + fs->blocksize - 1) >>
|
|
|
|
EXT2_BLOCK_SIZE_BITS(fs->super);
|
2007-10-15 07:04:58 +04:00
|
|
|
handle->path[0].visit_num = 1;
|
|
|
|
handle->level = 0;
|
|
|
|
handle->magic = EXT2_ET_MAGIC_EXTENT_HANDLE;
|
|
|
|
|
|
|
|
*ret_handle = handle;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
errout:
|
|
|
|
ext2fs_extent_free(handle);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function is responsible for (optionally) moving through the
|
|
|
|
* extent tree and then returning the current extent
|
|
|
|
*/
|
|
|
|
errcode_t ext2fs_extent_get(ext2_extent_handle_t handle,
|
|
|
|
int flags, struct ext2fs_extent *extent)
|
|
|
|
{
|
|
|
|
struct extent_path *path, *newpath;
|
|
|
|
struct ext3_extent_header *eh;
|
|
|
|
struct ext3_extent_idx *ix = 0;
|
|
|
|
struct ext3_extent *ex;
|
|
|
|
errcode_t retval;
|
2010-06-13 19:00:00 +04:00
|
|
|
blk64_t blk;
|
2007-10-15 07:04:58 +04:00
|
|
|
blk64_t end_blk;
|
|
|
|
int orig_op, op;
|
|
|
|
|
|
|
|
EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
|
|
|
|
|
|
|
|
if (!handle->path)
|
|
|
|
return EXT2_ET_NO_CURRENT_NODE;
|
|
|
|
|
|
|
|
orig_op = op = flags & EXT2_EXTENT_MOVE_MASK;
|
|
|
|
|
|
|
|
retry:
|
|
|
|
path = handle->path + handle->level;
|
|
|
|
if ((orig_op == EXT2_EXTENT_NEXT) ||
|
|
|
|
(orig_op == EXT2_EXTENT_NEXT_LEAF)) {
|
|
|
|
if (handle->level < handle->max_depth) {
|
|
|
|
/* interior node */
|
|
|
|
if (path->visit_num == 0) {
|
|
|
|
path->visit_num++;
|
|
|
|
op = EXT2_EXTENT_DOWN;
|
|
|
|
} else if (path->left > 0)
|
|
|
|
op = EXT2_EXTENT_NEXT_SIB;
|
|
|
|
else if (handle->level > 0)
|
|
|
|
op = EXT2_EXTENT_UP;
|
|
|
|
else
|
|
|
|
return EXT2_ET_EXTENT_NO_NEXT;
|
|
|
|
} else {
|
|
|
|
/* leaf node */
|
|
|
|
if (path->left > 0)
|
|
|
|
op = EXT2_EXTENT_NEXT_SIB;
|
|
|
|
else if (handle->level > 0)
|
|
|
|
op = EXT2_EXTENT_UP;
|
|
|
|
else
|
|
|
|
return EXT2_ET_EXTENT_NO_NEXT;
|
|
|
|
}
|
|
|
|
if (op != EXT2_EXTENT_NEXT_SIB) {
|
2009-07-20 07:32:09 +04:00
|
|
|
#ifdef DEBUG_GET_EXTENT
|
2008-07-13 22:19:14 +04:00
|
|
|
printf("<<<< OP = %s\n",
|
2007-10-15 07:04:58 +04:00
|
|
|
(op == EXT2_EXTENT_DOWN) ? "down" :
|
|
|
|
((op == EXT2_EXTENT_UP) ? "up" : "unknown"));
|
2008-07-13 22:19:14 +04:00
|
|
|
#endif
|
2007-10-15 07:04:58 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((orig_op == EXT2_EXTENT_PREV) ||
|
|
|
|
(orig_op == EXT2_EXTENT_PREV_LEAF)) {
|
|
|
|
if (handle->level < handle->max_depth) {
|
|
|
|
/* interior node */
|
|
|
|
if (path->visit_num > 0 ) {
|
|
|
|
/* path->visit_num = 0; */
|
|
|
|
op = EXT2_EXTENT_DOWN_AND_LAST;
|
|
|
|
} else if (path->left < path->entries-1)
|
|
|
|
op = EXT2_EXTENT_PREV_SIB;
|
|
|
|
else if (handle->level > 0)
|
|
|
|
op = EXT2_EXTENT_UP;
|
|
|
|
else
|
|
|
|
return EXT2_ET_EXTENT_NO_PREV;
|
|
|
|
} else {
|
|
|
|
/* leaf node */
|
|
|
|
if (path->left < path->entries-1)
|
|
|
|
op = EXT2_EXTENT_PREV_SIB;
|
|
|
|
else if (handle->level > 0)
|
|
|
|
op = EXT2_EXTENT_UP;
|
|
|
|
else
|
|
|
|
return EXT2_ET_EXTENT_NO_PREV;
|
|
|
|
}
|
|
|
|
if (op != EXT2_EXTENT_PREV_SIB) {
|
2009-07-20 07:32:09 +04:00
|
|
|
#ifdef DEBUG_GET_EXTENT
|
2008-07-13 22:19:14 +04:00
|
|
|
printf("<<<< OP = %s\n",
|
2007-10-15 07:04:58 +04:00
|
|
|
(op == EXT2_EXTENT_DOWN_AND_LAST) ? "down/last" :
|
|
|
|
((op == EXT2_EXTENT_UP) ? "up" : "unknown"));
|
2008-07-13 22:19:14 +04:00
|
|
|
#endif
|
2007-10-15 07:04:58 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (orig_op == EXT2_EXTENT_LAST_LEAF) {
|
|
|
|
if ((handle->level < handle->max_depth) &&
|
|
|
|
(path->left == 0))
|
|
|
|
op = EXT2_EXTENT_DOWN;
|
|
|
|
else
|
|
|
|
op = EXT2_EXTENT_LAST_SIB;
|
2009-07-20 07:32:09 +04:00
|
|
|
#ifdef DEBUG_GET_EXTENT
|
2008-07-13 22:19:14 +04:00
|
|
|
printf("<<<< OP = %s\n",
|
2007-10-15 07:04:58 +04:00
|
|
|
(op == EXT2_EXTENT_DOWN) ? "down" : "last_sib");
|
2008-07-13 22:19:14 +04:00
|
|
|
#endif
|
2007-10-15 07:04:58 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (op) {
|
|
|
|
case EXT2_EXTENT_CURRENT:
|
|
|
|
ix = path->curr;
|
|
|
|
break;
|
|
|
|
case EXT2_EXTENT_ROOT:
|
|
|
|
handle->level = 0;
|
|
|
|
path = handle->path + handle->level;
|
2011-09-17 00:49:23 +04:00
|
|
|
/* fallthrough */
|
2007-10-15 07:04:58 +04:00
|
|
|
case EXT2_EXTENT_FIRST_SIB:
|
|
|
|
path->left = path->entries;
|
|
|
|
path->curr = 0;
|
2011-09-17 00:49:23 +04:00
|
|
|
/* fallthrough */
|
2007-10-15 07:04:58 +04:00
|
|
|
case EXT2_EXTENT_NEXT_SIB:
|
|
|
|
if (path->left <= 0)
|
|
|
|
return EXT2_ET_EXTENT_NO_NEXT;
|
|
|
|
if (path->curr) {
|
|
|
|
ix = path->curr;
|
|
|
|
ix++;
|
|
|
|
} else {
|
|
|
|
eh = (struct ext3_extent_header *) path->buf;
|
|
|
|
ix = EXT_FIRST_INDEX(eh);
|
|
|
|
}
|
|
|
|
path->left--;
|
|
|
|
path->curr = ix;
|
|
|
|
path->visit_num = 0;
|
|
|
|
break;
|
|
|
|
case EXT2_EXTENT_PREV_SIB:
|
|
|
|
if (!path->curr ||
|
|
|
|
path->left+1 >= path->entries)
|
|
|
|
return EXT2_ET_EXTENT_NO_PREV;
|
|
|
|
ix = path->curr;
|
|
|
|
ix--;
|
|
|
|
path->curr = ix;
|
|
|
|
path->left++;
|
|
|
|
if (handle->level < handle->max_depth)
|
|
|
|
path->visit_num = 1;
|
|
|
|
break;
|
|
|
|
case EXT2_EXTENT_LAST_SIB:
|
|
|
|
eh = (struct ext3_extent_header *) path->buf;
|
|
|
|
path->curr = EXT_LAST_EXTENT(eh);
|
|
|
|
ix = path->curr;
|
|
|
|
path->left = 0;
|
|
|
|
path->visit_num = 0;
|
|
|
|
break;
|
|
|
|
case EXT2_EXTENT_UP:
|
|
|
|
if (handle->level <= 0)
|
|
|
|
return EXT2_ET_EXTENT_NO_UP;
|
|
|
|
handle->level--;
|
|
|
|
path--;
|
|
|
|
ix = path->curr;
|
|
|
|
if ((orig_op == EXT2_EXTENT_PREV) ||
|
|
|
|
(orig_op == EXT2_EXTENT_PREV_LEAF))
|
|
|
|
path->visit_num = 0;
|
|
|
|
break;
|
|
|
|
case EXT2_EXTENT_DOWN:
|
|
|
|
case EXT2_EXTENT_DOWN_AND_LAST:
|
|
|
|
if (!path->curr ||(handle->level >= handle->max_depth))
|
|
|
|
return EXT2_ET_EXTENT_NO_DOWN;
|
|
|
|
|
|
|
|
ix = path->curr;
|
|
|
|
newpath = path + 1;
|
|
|
|
if (!newpath->buf) {
|
|
|
|
retval = ext2fs_get_mem(handle->fs->blocksize,
|
|
|
|
&newpath->buf);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
blk = ext2fs_le32_to_cpu(ix->ei_leaf) +
|
2008-02-21 18:39:23 +03:00
|
|
|
((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32);
|
2007-10-15 07:04:58 +04:00
|
|
|
if ((handle->fs->flags & EXT2_FLAG_IMAGE_FILE) &&
|
|
|
|
(handle->fs->io != handle->fs->image_io))
|
|
|
|
memset(newpath->buf, 0, handle->fs->blocksize);
|
|
|
|
else {
|
2009-09-08 05:14:24 +04:00
|
|
|
retval = io_channel_read_blk64(handle->fs->io,
|
2007-10-15 07:04:58 +04:00
|
|
|
blk, 1, newpath->buf);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
handle->level++;
|
|
|
|
|
|
|
|
eh = (struct ext3_extent_header *) newpath->buf;
|
|
|
|
|
|
|
|
retval = ext2fs_extent_header_verify(eh, handle->fs->blocksize);
|
2008-12-26 06:42:38 +03:00
|
|
|
if (retval) {
|
|
|
|
handle->level--;
|
2007-10-15 07:04:58 +04:00
|
|
|
return retval;
|
2008-12-26 06:42:38 +03:00
|
|
|
}
|
2007-10-15 07:04:58 +04:00
|
|
|
|
|
|
|
newpath->left = newpath->entries =
|
|
|
|
ext2fs_le16_to_cpu(eh->eh_entries);
|
|
|
|
newpath->max_entries = ext2fs_le16_to_cpu(eh->eh_max);
|
|
|
|
|
|
|
|
if (path->left > 0) {
|
|
|
|
ix++;
|
|
|
|
newpath->end_blk = ext2fs_le32_to_cpu(ix->ei_block);
|
|
|
|
} else
|
|
|
|
newpath->end_blk = path->end_blk;
|
|
|
|
|
|
|
|
path = newpath;
|
|
|
|
if (op == EXT2_EXTENT_DOWN) {
|
|
|
|
ix = EXT_FIRST_INDEX((struct ext3_extent_header *) eh);
|
|
|
|
path->curr = ix;
|
|
|
|
path->left = path->entries - 1;
|
|
|
|
path->visit_num = 0;
|
|
|
|
} else {
|
|
|
|
ix = EXT_LAST_INDEX((struct ext3_extent_header *) eh);
|
|
|
|
path->curr = ix;
|
|
|
|
path->left = 0;
|
|
|
|
if (handle->level < handle->max_depth)
|
|
|
|
path->visit_num = 1;
|
|
|
|
}
|
2009-07-20 07:32:09 +04:00
|
|
|
#ifdef DEBUG_GET_EXTENT
|
2008-07-13 22:19:14 +04:00
|
|
|
printf("Down to level %d/%d, end_blk=%llu\n",
|
2007-10-15 07:04:58 +04:00
|
|
|
handle->level, handle->max_depth,
|
|
|
|
path->end_blk);
|
2008-07-13 22:19:14 +04:00
|
|
|
#endif
|
2007-10-15 07:04:58 +04:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return EXT2_ET_OP_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ix)
|
|
|
|
return EXT2_ET_NO_CURRENT_NODE;
|
|
|
|
|
|
|
|
extent->e_flags = 0;
|
2009-07-20 07:32:09 +04:00
|
|
|
#ifdef DEBUG_GET_EXTENT
|
2008-07-13 22:19:14 +04:00
|
|
|
printf("(Left %d)\n", path->left);
|
|
|
|
#endif
|
2007-10-15 07:04:58 +04:00
|
|
|
|
|
|
|
if (handle->level == handle->max_depth) {
|
|
|
|
ex = (struct ext3_extent *) ix;
|
|
|
|
|
|
|
|
extent->e_pblk = ext2fs_le32_to_cpu(ex->ee_start) +
|
|
|
|
((__u64) ext2fs_le16_to_cpu(ex->ee_start_hi) << 32);
|
|
|
|
extent->e_lblk = ext2fs_le32_to_cpu(ex->ee_block);
|
|
|
|
extent->e_len = ext2fs_le16_to_cpu(ex->ee_len);
|
2008-03-19 23:20:18 +03:00
|
|
|
extent->e_flags |= EXT2_EXTENT_FLAGS_LEAF;
|
2007-10-15 07:04:58 +04:00
|
|
|
if (extent->e_len > EXT_INIT_MAX_LEN) {
|
|
|
|
extent->e_len -= EXT_INIT_MAX_LEN;
|
2008-03-19 23:20:18 +03:00
|
|
|
extent->e_flags |= EXT2_EXTENT_FLAGS_UNINIT;
|
2007-10-15 07:04:58 +04:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
extent->e_pblk = ext2fs_le32_to_cpu(ix->ei_leaf) +
|
|
|
|
((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32);
|
|
|
|
extent->e_lblk = ext2fs_le32_to_cpu(ix->ei_block);
|
|
|
|
if (path->left > 0) {
|
|
|
|
ix++;
|
|
|
|
end_blk = ext2fs_le32_to_cpu(ix->ei_block);
|
|
|
|
} else
|
|
|
|
end_blk = path->end_blk;
|
|
|
|
|
|
|
|
extent->e_len = end_blk - extent->e_lblk;
|
|
|
|
}
|
|
|
|
if (path->visit_num)
|
|
|
|
extent->e_flags |= EXT2_EXTENT_FLAGS_SECOND_VISIT;
|
|
|
|
|
|
|
|
if (((orig_op == EXT2_EXTENT_NEXT_LEAF) ||
|
|
|
|
(orig_op == EXT2_EXTENT_PREV_LEAF)) &&
|
|
|
|
(handle->level != handle->max_depth))
|
|
|
|
goto retry;
|
|
|
|
|
|
|
|
if ((orig_op == EXT2_EXTENT_LAST_LEAF) &&
|
|
|
|
((handle->level != handle->max_depth) ||
|
|
|
|
(path->left != 0)))
|
|
|
|
goto retry;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static errcode_t update_path(ext2_extent_handle_t handle)
|
|
|
|
{
|
|
|
|
blk64_t blk;
|
|
|
|
errcode_t retval;
|
|
|
|
struct ext3_extent_idx *ix;
|
|
|
|
|
|
|
|
if (handle->level == 0) {
|
2009-06-18 02:49:01 +04:00
|
|
|
retval = ext2fs_write_inode(handle->fs, handle->ino,
|
|
|
|
handle->inode);
|
2007-10-15 07:04:58 +04:00
|
|
|
} else {
|
|
|
|
ix = handle->path[handle->level - 1].curr;
|
|
|
|
blk = ext2fs_le32_to_cpu(ix->ei_leaf) +
|
|
|
|
((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32);
|
|
|
|
|
2009-09-08 05:14:24 +04:00
|
|
|
retval = io_channel_write_blk64(handle->fs->io,
|
2007-10-15 07:04:58 +04:00
|
|
|
blk, 1, handle->path[handle->level].buf);
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2008-03-18 06:17:13 +03:00
|
|
|
#if 0
|
2007-10-15 07:04:58 +04:00
|
|
|
errcode_t ext2fs_extent_save_path(ext2_extent_handle_t handle,
|
|
|
|
ext2_extent_path_t *ret_path)
|
|
|
|
{
|
|
|
|
ext2_extent_path_t save_path;
|
|
|
|
struct ext2fs_extent extent;
|
|
|
|
struct ext2_extent_info info;
|
|
|
|
errcode_t retval;
|
|
|
|
|
|
|
|
retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
retval = ext2fs_extent_get_info(handle, &info);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
retval = ext2fs_get_mem(sizeof(struct ext2_extent_path), &save_path);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
memset(save_path, 0, sizeof(struct ext2_extent_path));
|
|
|
|
|
|
|
|
save_path->magic = EXT2_ET_MAGIC_EXTENT_PATH;
|
|
|
|
save_path->leaf_height = info.max_depth - info.curr_level - 1;
|
|
|
|
save_path->lblk = extent.e_lblk;
|
|
|
|
|
|
|
|
*ret_path = save_path;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
errcode_t ext2fs_extent_free_path(ext2_extent_path_t path)
|
|
|
|
{
|
|
|
|
EXT2_CHECK_MAGIC(path, EXT2_ET_MAGIC_EXTENT_PATH);
|
|
|
|
|
|
|
|
ext2fs_free_mem(&path);
|
|
|
|
return 0;
|
|
|
|
}
|
2008-03-18 06:17:13 +03:00
|
|
|
#endif
|
2007-10-15 07:04:58 +04:00
|
|
|
|
2008-04-08 07:00:59 +04:00
|
|
|
/*
|
|
|
|
* Go to the node at leaf_level which contains logical block blk.
|
|
|
|
*
|
2008-05-13 03:13:49 +04:00
|
|
|
* leaf_level is height from the leaf node level, i.e.
|
|
|
|
* leaf_level 0 is at leaf node, leaf_level 1 is 1 above etc.
|
|
|
|
*
|
2008-04-08 07:00:59 +04:00
|
|
|
* If "blk" has no mapping (hole) then handle is left at last
|
|
|
|
* extent before blk.
|
|
|
|
*/
|
2012-12-23 03:50:33 +04:00
|
|
|
errcode_t ext2fs_extent_goto2(ext2_extent_handle_t handle,
|
|
|
|
int leaf_level, blk64_t blk)
|
2007-10-15 07:04:58 +04:00
|
|
|
{
|
2008-03-18 06:17:13 +03:00
|
|
|
struct ext2fs_extent extent;
|
2007-10-15 07:04:58 +04:00
|
|
|
errcode_t retval;
|
|
|
|
|
|
|
|
retval = ext2fs_extent_get(handle, EXT2_EXTENT_ROOT, &extent);
|
2008-08-28 02:37:11 +04:00
|
|
|
if (retval) {
|
|
|
|
if (retval == EXT2_ET_EXTENT_NO_NEXT)
|
|
|
|
retval = EXT2_ET_EXTENT_NOT_FOUND;
|
2007-10-15 07:04:58 +04:00
|
|
|
return retval;
|
2008-08-28 02:37:11 +04:00
|
|
|
}
|
2007-10-15 07:04:58 +04:00
|
|
|
|
2008-05-13 03:13:49 +04:00
|
|
|
if (leaf_level > handle->max_depth) {
|
2008-07-13 22:19:14 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf("leaf level %d greater than tree depth %d\n",
|
2008-05-13 03:13:49 +04:00
|
|
|
leaf_level, handle->max_depth);
|
2008-07-13 22:19:14 +04:00
|
|
|
#endif
|
2008-05-13 03:13:49 +04:00
|
|
|
return EXT2_ET_OP_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
|
2009-07-20 07:32:09 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf("goto extent ino %u, level %d, %llu\n", handle->ino,
|
|
|
|
leaf_level, blk);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef DEBUG_GOTO_EXTENTS
|
2007-10-15 07:04:58 +04:00
|
|
|
dbg_print_extent("root", &extent);
|
2009-07-20 07:32:09 +04:00
|
|
|
#endif
|
2007-10-15 07:04:58 +04:00
|
|
|
while (1) {
|
2008-05-13 03:13:49 +04:00
|
|
|
if (handle->max_depth - handle->level == leaf_level) {
|
2008-04-08 07:00:59 +04:00
|
|
|
/* block is in this &extent */
|
2007-10-15 07:04:58 +04:00
|
|
|
if ((blk >= extent.e_lblk) &&
|
|
|
|
(blk < extent.e_lblk + extent.e_len))
|
|
|
|
return 0;
|
2008-04-08 07:00:59 +04:00
|
|
|
if (blk < extent.e_lblk) {
|
|
|
|
retval = ext2fs_extent_get(handle,
|
|
|
|
EXT2_EXTENT_PREV_SIB,
|
|
|
|
&extent);
|
2007-10-15 07:04:58 +04:00
|
|
|
return EXT2_ET_EXTENT_NOT_FOUND;
|
2008-04-08 07:00:59 +04:00
|
|
|
}
|
2007-10-15 07:04:58 +04:00
|
|
|
retval = ext2fs_extent_get(handle,
|
|
|
|
EXT2_EXTENT_NEXT_SIB,
|
|
|
|
&extent);
|
|
|
|
if (retval == EXT2_ET_EXTENT_NO_NEXT)
|
|
|
|
return EXT2_ET_EXTENT_NOT_FOUND;
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
retval = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_SIB,
|
|
|
|
&extent);
|
|
|
|
if (retval == EXT2_ET_EXTENT_NO_NEXT)
|
|
|
|
goto go_down;
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
|
2009-07-20 07:32:09 +04:00
|
|
|
#ifdef DEBUG_GOTO_EXTENTS
|
2007-10-15 07:04:58 +04:00
|
|
|
dbg_print_extent("next", &extent);
|
2009-07-20 07:32:09 +04:00
|
|
|
#endif
|
2007-10-15 07:04:58 +04:00
|
|
|
if (blk == extent.e_lblk)
|
|
|
|
goto go_down;
|
|
|
|
if (blk > extent.e_lblk)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
retval = ext2fs_extent_get(handle, EXT2_EXTENT_PREV_SIB,
|
|
|
|
&extent);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
|
2009-07-20 07:32:09 +04:00
|
|
|
#ifdef DEBUG_GOTO_EXTENTS
|
2007-10-15 07:04:58 +04:00
|
|
|
dbg_print_extent("prev", &extent);
|
2009-07-20 07:32:09 +04:00
|
|
|
#endif
|
2007-10-15 07:04:58 +04:00
|
|
|
|
|
|
|
go_down:
|
|
|
|
retval = ext2fs_extent_get(handle, EXT2_EXTENT_DOWN,
|
|
|
|
&extent);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
|
2009-07-20 07:32:09 +04:00
|
|
|
#ifdef DEBUG_GOTO_EXTENTS
|
2007-10-15 07:04:58 +04:00
|
|
|
dbg_print_extent("down", &extent);
|
2009-07-20 07:32:09 +04:00
|
|
|
#endif
|
2007-10-15 07:04:58 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
errcode_t ext2fs_extent_goto(ext2_extent_handle_t handle,
|
|
|
|
blk64_t blk)
|
|
|
|
{
|
2012-12-23 03:50:33 +04:00
|
|
|
return ext2fs_extent_goto2(handle, 0, blk);
|
2007-10-15 07:04:58 +04:00
|
|
|
}
|
|
|
|
|
2008-05-20 19:17:46 +04:00
|
|
|
/*
|
|
|
|
* Traverse back up to root fixing parents of current node as needed.
|
|
|
|
*
|
|
|
|
* If we changed start of first entry in a node, fix parent index start
|
|
|
|
* and so on.
|
|
|
|
*
|
|
|
|
* Safe to call for any position in node; if not at the first entry,
|
|
|
|
* will simply return.
|
|
|
|
*/
|
2012-12-20 23:05:01 +04:00
|
|
|
errcode_t ext2fs_extent_fix_parents(ext2_extent_handle_t handle)
|
2008-05-20 19:17:46 +04:00
|
|
|
{
|
|
|
|
int retval = 0;
|
2012-12-21 03:27:33 +04:00
|
|
|
int orig_height;
|
2008-05-20 19:17:46 +04:00
|
|
|
blk64_t start;
|
|
|
|
struct extent_path *path;
|
|
|
|
struct ext2fs_extent extent;
|
2012-12-21 03:27:33 +04:00
|
|
|
struct ext2_extent_info info;
|
2008-05-20 19:17:46 +04:00
|
|
|
|
|
|
|
EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
|
|
|
|
|
|
|
|
if (!(handle->fs->flags & EXT2_FLAG_RW))
|
|
|
|
return EXT2_ET_RO_FILSYS;
|
|
|
|
|
|
|
|
if (!handle->path)
|
|
|
|
return EXT2_ET_NO_CURRENT_NODE;
|
|
|
|
|
|
|
|
path = handle->path + handle->level;
|
|
|
|
if (!path->curr)
|
|
|
|
return EXT2_ET_NO_CURRENT_NODE;
|
|
|
|
|
|
|
|
retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
/* modified node's start block */
|
|
|
|
start = extent.e_lblk;
|
|
|
|
|
2012-12-21 03:27:33 +04:00
|
|
|
if ((retval = ext2fs_extent_get_info(handle, &info)))
|
|
|
|
return retval;
|
|
|
|
orig_height = info.max_depth - info.curr_level;
|
|
|
|
|
2008-05-20 19:17:46 +04:00
|
|
|
/* traverse up until index not first, or startblk matches, or top */
|
|
|
|
while (handle->level > 0 &&
|
|
|
|
(path->left == path->entries - 1)) {
|
|
|
|
retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP, &extent);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
if (extent.e_lblk == start)
|
|
|
|
break;
|
|
|
|
path = handle->path + handle->level;
|
|
|
|
extent.e_len += (extent.e_lblk - start);
|
|
|
|
extent.e_lblk = start;
|
|
|
|
retval = ext2fs_extent_replace(handle, 0, &extent);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
update_path(handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* put handle back to where we started */
|
2012-12-23 03:50:33 +04:00
|
|
|
retval = ext2fs_extent_goto2(handle, orig_height, start);
|
2008-05-20 19:17:46 +04:00
|
|
|
done:
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2008-08-28 07:07:54 +04:00
|
|
|
errcode_t ext2fs_extent_replace(ext2_extent_handle_t handle,
|
2008-03-18 06:17:13 +03:00
|
|
|
int flags EXT2FS_ATTR((unused)),
|
2007-10-15 07:04:58 +04:00
|
|
|
struct ext2fs_extent *extent)
|
|
|
|
{
|
|
|
|
struct extent_path *path;
|
|
|
|
struct ext3_extent_idx *ix;
|
|
|
|
struct ext3_extent *ex;
|
|
|
|
|
|
|
|
EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
|
|
|
|
|
|
|
|
if (!(handle->fs->flags & EXT2_FLAG_RW))
|
|
|
|
return EXT2_ET_RO_FILSYS;
|
|
|
|
|
|
|
|
if (!handle->path)
|
|
|
|
return EXT2_ET_NO_CURRENT_NODE;
|
|
|
|
|
|
|
|
path = handle->path + handle->level;
|
|
|
|
if (!path->curr)
|
|
|
|
return EXT2_ET_NO_CURRENT_NODE;
|
|
|
|
|
2009-07-20 07:32:09 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf("extent replace: %u ", handle->ino);
|
|
|
|
dbg_print_extent(0, extent);
|
|
|
|
#endif
|
|
|
|
|
2007-10-15 07:04:58 +04:00
|
|
|
if (handle->level == handle->max_depth) {
|
|
|
|
ex = path->curr;
|
|
|
|
|
|
|
|
ex->ee_block = ext2fs_cpu_to_le32(extent->e_lblk);
|
|
|
|
ex->ee_start = ext2fs_cpu_to_le32(extent->e_pblk & 0xFFFFFFFF);
|
|
|
|
ex->ee_start_hi = ext2fs_cpu_to_le16(extent->e_pblk >> 32);
|
2008-06-02 06:56:37 +04:00
|
|
|
if (extent->e_flags & EXT2_EXTENT_FLAGS_UNINIT) {
|
|
|
|
if (extent->e_len > EXT_UNINIT_MAX_LEN)
|
|
|
|
return EXT2_ET_EXTENT_INVALID_LENGTH;
|
|
|
|
ex->ee_len = ext2fs_cpu_to_le16(extent->e_len +
|
|
|
|
EXT_INIT_MAX_LEN);
|
|
|
|
} else {
|
|
|
|
if (extent->e_len > EXT_INIT_MAX_LEN)
|
|
|
|
return EXT2_ET_EXTENT_INVALID_LENGTH;
|
|
|
|
ex->ee_len = ext2fs_cpu_to_le16(extent->e_len);
|
|
|
|
}
|
2007-10-15 07:04:58 +04:00
|
|
|
} else {
|
|
|
|
ix = path->curr;
|
|
|
|
|
|
|
|
ix->ei_leaf = ext2fs_cpu_to_le32(extent->e_pblk & 0xFFFFFFFF);
|
|
|
|
ix->ei_leaf_hi = ext2fs_cpu_to_le16(extent->e_pblk >> 32);
|
|
|
|
ix->ei_block = ext2fs_cpu_to_le32(extent->e_lblk);
|
|
|
|
ix->ei_unused = 0;
|
|
|
|
}
|
|
|
|
update_path(handle);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-05-02 03:15:05 +04:00
|
|
|
static int splitting_at_eof(struct ext2_extent_handle *handle,
|
|
|
|
struct extent_path *path)
|
|
|
|
{
|
|
|
|
struct extent_path *ppp = path;
|
|
|
|
dump_path(__func__, handle, path);
|
|
|
|
|
|
|
|
if (handle->level == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (ppp->left)
|
|
|
|
return 0;
|
|
|
|
ppp--;
|
|
|
|
} while (ppp >= handle->path);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2008-05-20 19:14:20 +04:00
|
|
|
/*
|
|
|
|
* allocate a new block, move half the current node to it, and update parent
|
|
|
|
*
|
|
|
|
* handle will be left pointing at original record.
|
|
|
|
*/
|
2014-05-02 03:15:05 +04:00
|
|
|
static errcode_t extent_node_split(ext2_extent_handle_t handle,
|
|
|
|
int expand_allowed)
|
2008-05-20 19:14:20 +04:00
|
|
|
{
|
|
|
|
errcode_t retval = 0;
|
2010-06-13 19:00:00 +04:00
|
|
|
blk64_t new_node_pblk;
|
2008-05-20 19:14:20 +04:00
|
|
|
blk64_t new_node_start;
|
|
|
|
blk64_t orig_lblk;
|
|
|
|
blk64_t goal_blk = 0;
|
|
|
|
int orig_height;
|
|
|
|
char *block_buf = NULL;
|
|
|
|
struct ext2fs_extent extent;
|
|
|
|
struct extent_path *path, *newpath = 0;
|
|
|
|
struct ext3_extent_header *eh, *neweh;
|
|
|
|
int tocopy;
|
|
|
|
int new_root = 0;
|
|
|
|
struct ext2_extent_info info;
|
2014-05-02 03:15:05 +04:00
|
|
|
int no_balance;
|
2008-05-20 19:14:20 +04:00
|
|
|
|
|
|
|
/* basic sanity */
|
|
|
|
EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
|
|
|
|
|
|
|
|
if (!(handle->fs->flags & EXT2_FLAG_RW))
|
|
|
|
return EXT2_ET_RO_FILSYS;
|
|
|
|
|
|
|
|
if (!handle->path)
|
|
|
|
return EXT2_ET_NO_CURRENT_NODE;
|
|
|
|
|
2008-07-13 22:19:14 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf("splitting node at level %d\n", handle->level);
|
|
|
|
#endif
|
2008-05-20 19:14:20 +04:00
|
|
|
retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
retval = ext2fs_extent_get_info(handle, &info);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
/* save the position we were originally splitting... */
|
|
|
|
orig_height = info.max_depth - info.curr_level;
|
|
|
|
orig_lblk = extent.e_lblk;
|
|
|
|
|
|
|
|
/* Is there room in the parent for a new entry? */
|
|
|
|
if (handle->level &&
|
|
|
|
(handle->path[handle->level - 1].entries >=
|
|
|
|
handle->path[handle->level - 1].max_entries)) {
|
|
|
|
|
2008-07-13 22:19:14 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf("parent level %d full; splitting it too\n",
|
2008-05-20 19:14:20 +04:00
|
|
|
handle->level - 1);
|
2008-07-13 22:19:14 +04:00
|
|
|
#endif
|
2008-05-20 19:14:20 +04:00
|
|
|
/* split the parent */
|
|
|
|
retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP, &extent);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
goal_blk = extent.e_pblk;
|
|
|
|
|
2014-05-02 03:15:05 +04:00
|
|
|
retval = extent_node_split(handle, expand_allowed);
|
2008-05-20 19:14:20 +04:00
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
/* get handle back to our original split position */
|
2012-12-23 03:50:33 +04:00
|
|
|
retval = ext2fs_extent_goto2(handle, orig_height, orig_lblk);
|
2008-05-20 19:14:20 +04:00
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* At this point, parent should have room for this split */
|
|
|
|
path = handle->path + handle->level;
|
|
|
|
if (!path->curr)
|
|
|
|
return EXT2_ET_NO_CURRENT_NODE;
|
|
|
|
|
2014-05-02 03:15:05 +04:00
|
|
|
/*
|
|
|
|
* Normally, we try to split a full node in half. This doesn't turn
|
|
|
|
* out so well if we're tacking extents on the end of the file because
|
|
|
|
* then we're stuck with a tree of half-full extent blocks. This of
|
|
|
|
* course doesn't apply to the root level.
|
|
|
|
*/
|
|
|
|
no_balance = expand_allowed ? splitting_at_eof(handle, path) : 0;
|
|
|
|
|
2008-05-20 19:14:20 +04:00
|
|
|
/* extent header of the current node we'll split */
|
|
|
|
eh = (struct ext3_extent_header *)path->buf;
|
|
|
|
|
|
|
|
/* splitting root level means moving them all out */
|
|
|
|
if (handle->level == 0) {
|
|
|
|
new_root = 1;
|
|
|
|
tocopy = ext2fs_le16_to_cpu(eh->eh_entries);
|
|
|
|
retval = ext2fs_get_mem(((handle->max_depth+2) *
|
|
|
|
sizeof(struct extent_path)),
|
|
|
|
&newpath);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
memset(newpath, 0,
|
|
|
|
((handle->max_depth+2) * sizeof(struct extent_path)));
|
|
|
|
} else {
|
2014-05-02 03:15:05 +04:00
|
|
|
if (no_balance)
|
|
|
|
tocopy = 1;
|
|
|
|
else
|
|
|
|
tocopy = ext2fs_le16_to_cpu(eh->eh_entries) / 2;
|
2008-05-20 19:14:20 +04:00
|
|
|
}
|
|
|
|
|
2008-07-13 22:19:14 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf("will copy out %d of %d entries at level %d\n",
|
2008-05-20 19:14:20 +04:00
|
|
|
tocopy, ext2fs_le16_to_cpu(eh->eh_entries),
|
|
|
|
handle->level);
|
2008-07-13 22:19:14 +04:00
|
|
|
#endif
|
2008-05-20 19:14:20 +04:00
|
|
|
|
2014-05-02 03:15:05 +04:00
|
|
|
if (!tocopy && !no_balance) {
|
2008-07-13 22:19:14 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf("Nothing to copy to new block!\n");
|
|
|
|
#endif
|
2008-05-20 19:14:20 +04:00
|
|
|
retval = EXT2_ET_CANT_SPLIT_EXTENT;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* first we need a new block, or can do nothing. */
|
|
|
|
block_buf = malloc(handle->fs->blocksize);
|
|
|
|
if (!block_buf) {
|
|
|
|
retval = ENOMEM;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!goal_blk) {
|
|
|
|
dgrp_t group = ext2fs_group_of_ino(handle->fs, handle->ino);
|
|
|
|
__u8 log_flex = handle->fs->super->s_log_groups_per_flex;
|
|
|
|
|
|
|
|
if (log_flex)
|
|
|
|
group = group & ~((1 << (log_flex)) - 1);
|
2013-01-03 22:42:38 +04:00
|
|
|
goal_blk = ext2fs_group_first_block2(handle->fs, group);
|
2008-05-20 19:14:20 +04:00
|
|
|
}
|
2010-06-13 19:00:00 +04:00
|
|
|
retval = ext2fs_alloc_block2(handle->fs, goal_blk, block_buf,
|
2008-05-20 19:14:20 +04:00
|
|
|
&new_node_pblk);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
|
2008-07-13 22:19:14 +04:00
|
|
|
#ifdef DEBUG
|
2009-07-20 07:32:09 +04:00
|
|
|
printf("will copy to new node at block %lu\n",
|
|
|
|
(unsigned long) new_node_pblk);
|
2008-07-13 22:19:14 +04:00
|
|
|
#endif
|
2008-05-20 19:14:20 +04:00
|
|
|
|
|
|
|
/* Copy data into new block buffer */
|
|
|
|
/* First the header for the new block... */
|
|
|
|
neweh = (struct ext3_extent_header *) block_buf;
|
|
|
|
memcpy(neweh, eh, sizeof(struct ext3_extent_header));
|
|
|
|
neweh->eh_entries = ext2fs_cpu_to_le16(tocopy);
|
|
|
|
neweh->eh_max = ext2fs_cpu_to_le16((handle->fs->blocksize -
|
|
|
|
sizeof(struct ext3_extent_header)) /
|
|
|
|
sizeof(struct ext3_extent));
|
|
|
|
|
|
|
|
/* then the entries for the new block... */
|
|
|
|
memcpy(EXT_FIRST_INDEX(neweh),
|
|
|
|
EXT_FIRST_INDEX(eh) +
|
|
|
|
(ext2fs_le16_to_cpu(eh->eh_entries) - tocopy),
|
|
|
|
sizeof(struct ext3_extent_idx) * tocopy);
|
|
|
|
|
|
|
|
new_node_start = ext2fs_le32_to_cpu(EXT_FIRST_INDEX(neweh)->ei_block);
|
|
|
|
|
|
|
|
/* ...and write the new node block out to disk. */
|
2009-09-08 05:14:24 +04:00
|
|
|
retval = io_channel_write_blk64(handle->fs->io, new_node_pblk, 1,
|
|
|
|
block_buf);
|
2008-05-20 19:14:20 +04:00
|
|
|
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
/* OK! we've created the new node; now adjust the tree */
|
|
|
|
|
|
|
|
/* current path now has fewer active entries, we copied some out */
|
|
|
|
if (handle->level == 0) {
|
|
|
|
memcpy(newpath, path,
|
|
|
|
sizeof(struct extent_path) * (handle->max_depth+1));
|
|
|
|
handle->path = newpath;
|
|
|
|
newpath = path;
|
|
|
|
path = handle->path;
|
|
|
|
path->entries = 1;
|
|
|
|
path->left = path->max_entries - 1;
|
|
|
|
handle->max_depth++;
|
|
|
|
eh->eh_depth = ext2fs_cpu_to_le16(handle->max_depth);
|
|
|
|
} else {
|
|
|
|
path->entries -= tocopy;
|
|
|
|
path->left -= tocopy;
|
|
|
|
}
|
|
|
|
|
|
|
|
eh->eh_entries = ext2fs_cpu_to_le16(path->entries);
|
|
|
|
/* this writes out the node, incl. the modified header */
|
|
|
|
retval = update_path(handle);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
/* now go up and insert/replace index for new node we created */
|
|
|
|
if (new_root) {
|
|
|
|
retval = ext2fs_extent_get(handle, EXT2_EXTENT_FIRST_SIB, &extent);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
extent.e_lblk = new_node_start;
|
|
|
|
extent.e_pblk = new_node_pblk;
|
|
|
|
extent.e_len = handle->path[0].end_blk - extent.e_lblk;
|
|
|
|
retval = ext2fs_extent_replace(handle, 0, &extent);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
} else {
|
|
|
|
__u32 new_node_length;
|
|
|
|
|
|
|
|
retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP, &extent);
|
|
|
|
/* will insert after this one; it's length is shorter now */
|
|
|
|
new_node_length = new_node_start - extent.e_lblk;
|
|
|
|
extent.e_len -= new_node_length;
|
|
|
|
retval = ext2fs_extent_replace(handle, 0, &extent);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
/* now set up the new extent and insert it */
|
|
|
|
extent.e_lblk = new_node_start;
|
|
|
|
extent.e_pblk = new_node_pblk;
|
|
|
|
extent.e_len = new_node_length;
|
|
|
|
retval = ext2fs_extent_insert(handle, EXT2_EXTENT_INSERT_AFTER, &extent);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get handle back to our original position */
|
2012-12-23 03:50:33 +04:00
|
|
|
retval = ext2fs_extent_goto2(handle, orig_height, orig_lblk);
|
2008-05-20 19:14:20 +04:00
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
/* new node hooked in, so update inode block count (do this here?) */
|
2014-05-02 03:15:05 +04:00
|
|
|
ext2fs_iblk_add_blocks(handle->fs, handle->inode, 1);
|
2009-06-18 02:49:01 +04:00
|
|
|
retval = ext2fs_write_inode(handle->fs, handle->ino,
|
|
|
|
handle->inode);
|
2008-05-20 19:14:20 +04:00
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
done:
|
|
|
|
if (newpath)
|
|
|
|
ext2fs_free_mem(&newpath);
|
2009-02-23 20:07:50 +03:00
|
|
|
free(block_buf);
|
2008-05-20 19:14:20 +04:00
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2014-05-02 03:15:05 +04:00
|
|
|
errcode_t ext2fs_extent_node_split(ext2_extent_handle_t handle)
|
|
|
|
{
|
|
|
|
return extent_node_split(handle, 0);
|
|
|
|
}
|
|
|
|
|
2007-10-15 07:04:58 +04:00
|
|
|
errcode_t ext2fs_extent_insert(ext2_extent_handle_t handle, int flags,
|
|
|
|
struct ext2fs_extent *extent)
|
|
|
|
{
|
|
|
|
struct extent_path *path;
|
|
|
|
struct ext3_extent_idx *ix;
|
|
|
|
struct ext3_extent_header *eh;
|
|
|
|
errcode_t retval;
|
|
|
|
|
|
|
|
EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
|
|
|
|
|
|
|
|
if (!(handle->fs->flags & EXT2_FLAG_RW))
|
|
|
|
return EXT2_ET_RO_FILSYS;
|
|
|
|
|
|
|
|
if (!handle->path)
|
|
|
|
return EXT2_ET_NO_CURRENT_NODE;
|
|
|
|
|
2009-07-20 07:32:09 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf("extent insert: %u ", handle->ino);
|
|
|
|
dbg_print_extent(0, extent);
|
|
|
|
#endif
|
|
|
|
|
2007-10-15 07:04:58 +04:00
|
|
|
path = handle->path + handle->level;
|
|
|
|
|
2008-05-20 19:15:27 +04:00
|
|
|
if (path->entries >= path->max_entries) {
|
|
|
|
if (flags & EXT2_EXTENT_INSERT_NOSPLIT) {
|
|
|
|
return EXT2_ET_CANT_INSERT_EXTENT;
|
|
|
|
} else {
|
2008-07-13 22:19:14 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf("node full (level %d) - splitting\n",
|
2008-06-07 19:53:56 +04:00
|
|
|
handle->level);
|
2008-07-13 22:19:14 +04:00
|
|
|
#endif
|
2014-05-02 03:15:05 +04:00
|
|
|
retval = extent_node_split(handle, 1);
|
2008-05-20 19:15:27 +04:00
|
|
|
if (retval)
|
resize2fs: fix ENOSPC corruption case
http://people.redhat.com/esandeen/livecd-creator-imagefile.bz2
contains an image (for now) which, when resized to 578639, corrupts
the filesystem.
This is a bit crazy, I guess, because the fs currently has only
1 free block, but still, we should be graceful about the failure.
Perhaps it would make sense to check the requested valuea against
the minimum value resize2fs would compute for "-P" and fail (at
least without a force).
But in any case, this exposed 2 bugs when moving that one block
required an extent split, which is what hit the ENOSPC.
For starters, ext2fs_extent_set_bmap() in the "(re/un)mapping last
block in extent" case was replacing the old extent before the
new one was created; when the new extent creation failed, it
left us in an inconsistent state. Simply changing the order of
the two should fix this problem.
Next, ext2fs_extent_insert was calling ext2fs_extent_delete()
on *any* error, including one caused by failure to allocate a new
block to split the node to hold that extent ... the handle was left
unchanged, and we deleted the -original- extent.
As a quick fix for this, just don't do the delete if we fail the split,
though this may need to be smarter. I don't think we have terribly
consistent behavior about where a handle is left on various errors.
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2009-05-21 01:36:26 +04:00
|
|
|
return retval;
|
2008-05-20 19:15:27 +04:00
|
|
|
path = handle->path + handle->level;
|
|
|
|
}
|
|
|
|
}
|
2007-10-15 07:04:58 +04:00
|
|
|
|
|
|
|
eh = (struct ext3_extent_header *) path->buf;
|
|
|
|
if (path->curr) {
|
|
|
|
ix = path->curr;
|
|
|
|
if (flags & EXT2_EXTENT_INSERT_AFTER) {
|
|
|
|
ix++;
|
|
|
|
path->left--;
|
|
|
|
}
|
libext2fs: fix off-by-one bug in ext2fs_extent_insert()
When inserting the first extent into an empty inode, the
ext2fs_extent_insert() leaves path->left set to 1 instead of 0. Since
path->curr is pointing at the last (only) extent in the file,
path->left should be 0.
This is mostly harmless, and gets corrected fairly quickly if the
calling applicaton jumps to a different part of the extent tree ---
for example, by calling ext2fs_extent_goto(), or calling
ext2fs_extent_get with the flags argument set to EXT2_EXTENT_ROOT.
Which is why we hadn't noticed this problem until now.
However, if you insert four extents using ext2fs_extent_insert, the
fourth insert will end up copying too many bytes in the i_block[]
array, since path->left is one larger than it should be. This results
in the inode fields i_generation, i_file_acl, and i_size_high getting
zeroed out.
This problem can be replicated as follows:
% cp /dev/null /tmp/foo.img
% mke2fs -F -t ext4 /tmp/foo.img 100
% debugfs -w /tmp/foo.img
debugfs: write /dev/null foo
debugfs: set_inode_field foo i_size_hi 1
debugfs: stat foo
<----- note that the inode's size is 4294967296
debugfs: extent_open foo
debugfs (extent ino 12): insert --after 0 1 100
debugfs (extent ino 12): insert --after 1 1 101
debugfs (extent ino 12): insert --after 2 1 102
debugfs (extent ino 12): insert --after 3 1 103
debugfs (extent ino 12): extent_close
debugfs: stat foo
<----- note that the inode's size is now 0
debugfs: quit
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
2014-01-16 08:29:21 +04:00
|
|
|
} else {
|
2007-10-15 07:04:58 +04:00
|
|
|
ix = EXT_FIRST_INDEX(eh);
|
libext2fs: fix off-by-one bug in ext2fs_extent_insert()
When inserting the first extent into an empty inode, the
ext2fs_extent_insert() leaves path->left set to 1 instead of 0. Since
path->curr is pointing at the last (only) extent in the file,
path->left should be 0.
This is mostly harmless, and gets corrected fairly quickly if the
calling applicaton jumps to a different part of the extent tree ---
for example, by calling ext2fs_extent_goto(), or calling
ext2fs_extent_get with the flags argument set to EXT2_EXTENT_ROOT.
Which is why we hadn't noticed this problem until now.
However, if you insert four extents using ext2fs_extent_insert, the
fourth insert will end up copying too many bytes in the i_block[]
array, since path->left is one larger than it should be. This results
in the inode fields i_generation, i_file_acl, and i_size_high getting
zeroed out.
This problem can be replicated as follows:
% cp /dev/null /tmp/foo.img
% mke2fs -F -t ext4 /tmp/foo.img 100
% debugfs -w /tmp/foo.img
debugfs: write /dev/null foo
debugfs: set_inode_field foo i_size_hi 1
debugfs: stat foo
<----- note that the inode's size is 4294967296
debugfs: extent_open foo
debugfs (extent ino 12): insert --after 0 1 100
debugfs (extent ino 12): insert --after 1 1 101
debugfs (extent ino 12): insert --after 2 1 102
debugfs (extent ino 12): insert --after 3 1 103
debugfs (extent ino 12): extent_close
debugfs: stat foo
<----- note that the inode's size is now 0
debugfs: quit
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
2014-01-16 08:29:21 +04:00
|
|
|
path->left = -1;
|
|
|
|
}
|
2007-10-15 07:04:58 +04:00
|
|
|
|
|
|
|
path->curr = ix;
|
|
|
|
|
2008-04-08 07:00:58 +04:00
|
|
|
if (path->left >= 0)
|
2007-10-15 07:04:58 +04:00
|
|
|
memmove(ix + 1, ix,
|
2008-03-14 17:42:50 +03:00
|
|
|
(path->left+1) * sizeof(struct ext3_extent_idx));
|
2007-10-15 07:04:58 +04:00
|
|
|
path->left++;
|
|
|
|
path->entries++;
|
|
|
|
|
|
|
|
eh = (struct ext3_extent_header *) path->buf;
|
|
|
|
eh->eh_entries = ext2fs_cpu_to_le16(path->entries);
|
|
|
|
|
|
|
|
retval = ext2fs_extent_replace(handle, 0, extent);
|
|
|
|
if (retval)
|
|
|
|
goto errout;
|
|
|
|
|
|
|
|
retval = update_path(handle);
|
|
|
|
if (retval)
|
|
|
|
goto errout;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
errout:
|
|
|
|
ext2fs_extent_delete(handle, 0);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2008-05-20 19:17:46 +04:00
|
|
|
/*
|
|
|
|
* Sets the physical block for a logical file block in the extent tree.
|
|
|
|
*
|
|
|
|
* May: map unmapped, unmap mapped, or remap mapped blocks.
|
|
|
|
*
|
|
|
|
* Mapping an unmapped block adds a single-block extent.
|
|
|
|
*
|
|
|
|
* Unmapping first or last block modifies extent in-place
|
|
|
|
* - But may need to fix parent's starts too in first-block case
|
|
|
|
*
|
|
|
|
* Mapping any unmapped block requires adding a (single-block) extent
|
|
|
|
* and inserting into proper point in tree.
|
|
|
|
*
|
|
|
|
* Modifying (unmapping or remapping) a block in the middle
|
|
|
|
* of an extent requires splitting the extent.
|
|
|
|
* - Remapping case requires new single-block extent.
|
|
|
|
*
|
|
|
|
* Remapping first or last block adds an extent.
|
|
|
|
*
|
|
|
|
* We really need extent adding to be smart about merging.
|
|
|
|
*/
|
|
|
|
|
|
|
|
errcode_t ext2fs_extent_set_bmap(ext2_extent_handle_t handle,
|
|
|
|
blk64_t logical, blk64_t physical, int flags)
|
|
|
|
{
|
|
|
|
errcode_t ec, retval = 0;
|
|
|
|
int mapped = 1; /* logical is mapped? */
|
|
|
|
int orig_height;
|
|
|
|
int extent_uninit = 0;
|
2009-07-20 07:37:44 +04:00
|
|
|
int prev_uninit = 0;
|
|
|
|
int next_uninit = 0;
|
2008-05-20 19:17:46 +04:00
|
|
|
int new_uninit = 0;
|
|
|
|
int max_len = EXT_INIT_MAX_LEN;
|
2009-07-20 07:37:44 +04:00
|
|
|
int has_prev, has_next;
|
2008-05-20 19:17:46 +04:00
|
|
|
blk64_t orig_lblk;
|
|
|
|
struct extent_path *path;
|
2009-07-20 07:37:44 +04:00
|
|
|
struct ext2fs_extent extent, next_extent, prev_extent;
|
2008-05-20 19:17:46 +04:00
|
|
|
struct ext2fs_extent newextent;
|
|
|
|
struct ext2_extent_info info;
|
|
|
|
|
|
|
|
EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
|
|
|
|
|
2009-07-20 07:32:09 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf("set_bmap ino %u log %lld phys %lld flags %d\n",
|
|
|
|
handle->ino, logical, physical, flags);
|
|
|
|
#endif
|
|
|
|
|
2008-05-20 19:17:46 +04:00
|
|
|
if (!(handle->fs->flags & EXT2_FLAG_RW))
|
|
|
|
return EXT2_ET_RO_FILSYS;
|
|
|
|
|
|
|
|
if (!handle->path)
|
|
|
|
return EXT2_ET_NO_CURRENT_NODE;
|
|
|
|
|
|
|
|
path = handle->path + handle->level;
|
|
|
|
|
|
|
|
if (flags & EXT2_EXTENT_SET_BMAP_UNINIT) {
|
|
|
|
new_uninit = 1;
|
|
|
|
max_len = EXT_UNINIT_MAX_LEN;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if (re)mapping, set up new extent to insert */
|
|
|
|
if (physical) {
|
|
|
|
newextent.e_len = 1;
|
|
|
|
newextent.e_pblk = physical;
|
|
|
|
newextent.e_lblk = logical;
|
|
|
|
newextent.e_flags = EXT2_EXTENT_FLAGS_LEAF;
|
|
|
|
if (new_uninit)
|
|
|
|
newextent.e_flags |= EXT2_EXTENT_FLAGS_UNINIT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* special case if the extent tree is completely empty */
|
|
|
|
if ((handle->max_depth == 0) && (path->entries == 0)) {
|
|
|
|
retval = ext2fs_extent_insert(handle, 0, &newextent);
|
2008-06-07 19:53:56 +04:00
|
|
|
return retval;
|
2008-05-20 19:17:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* save our original location in the extent tree */
|
|
|
|
if ((retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT,
|
|
|
|
&extent))) {
|
|
|
|
if (retval != EXT2_ET_NO_CURRENT_NODE)
|
|
|
|
return retval;
|
|
|
|
memset(&extent, 0, sizeof(extent));
|
|
|
|
}
|
|
|
|
if ((retval = ext2fs_extent_get_info(handle, &info)))
|
|
|
|
return retval;
|
|
|
|
orig_height = info.max_depth - info.curr_level;
|
|
|
|
orig_lblk = extent.e_lblk;
|
|
|
|
|
|
|
|
/* go to the logical spot we want to (re/un)map */
|
|
|
|
retval = ext2fs_extent_goto(handle, logical);
|
|
|
|
if (retval) {
|
|
|
|
if (retval == EXT2_ET_EXTENT_NOT_FOUND) {
|
|
|
|
retval = 0;
|
|
|
|
mapped = 0;
|
|
|
|
if (!physical) {
|
2008-07-13 22:19:14 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf("block %llu already unmapped\n",
|
2008-06-07 19:53:56 +04:00
|
|
|
logical);
|
2008-07-13 22:19:14 +04:00
|
|
|
#endif
|
2008-05-20 19:17:46 +04:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This may be the extent *before* the requested logical,
|
|
|
|
* if it's currently unmapped.
|
2009-07-20 07:37:44 +04:00
|
|
|
*
|
|
|
|
* Get the previous and next leaf extents, if they are present.
|
2008-05-20 19:17:46 +04:00
|
|
|
*/
|
|
|
|
retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
if (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT)
|
|
|
|
extent_uninit = 1;
|
2009-07-20 07:37:44 +04:00
|
|
|
retval = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_LEAF, &next_extent);
|
|
|
|
if (retval) {
|
|
|
|
has_next = 0;
|
|
|
|
if (retval != EXT2_ET_EXTENT_NO_NEXT)
|
|
|
|
goto done;
|
|
|
|
} else {
|
|
|
|
dbg_print_extent("set_bmap: next_extent",
|
|
|
|
&next_extent);
|
|
|
|
has_next = 1;
|
|
|
|
if (next_extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT)
|
|
|
|
next_uninit = 1;
|
|
|
|
}
|
|
|
|
retval = ext2fs_extent_goto(handle, logical);
|
|
|
|
if (retval && retval != EXT2_ET_EXTENT_NOT_FOUND)
|
|
|
|
goto done;
|
|
|
|
retval = ext2fs_extent_get(handle, EXT2_EXTENT_PREV_LEAF, &prev_extent);
|
|
|
|
if (retval) {
|
|
|
|
has_prev = 0;
|
|
|
|
if (retval != EXT2_ET_EXTENT_NO_PREV)
|
|
|
|
goto done;
|
|
|
|
} else {
|
|
|
|
has_prev = 1;
|
|
|
|
dbg_print_extent("set_bmap: prev_extent",
|
|
|
|
&prev_extent);
|
|
|
|
if (prev_extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT)
|
|
|
|
prev_uninit = 1;
|
|
|
|
}
|
|
|
|
retval = ext2fs_extent_goto(handle, logical);
|
|
|
|
if (retval && retval != EXT2_ET_EXTENT_NOT_FOUND)
|
|
|
|
goto done;
|
2008-05-20 19:17:46 +04:00
|
|
|
|
|
|
|
/* check if already pointing to the requested physical */
|
|
|
|
if (mapped && (new_uninit == extent_uninit) &&
|
|
|
|
(extent.e_pblk + (logical - extent.e_lblk) == physical)) {
|
2008-07-13 22:19:14 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf("physical block (at %llu) unchanged\n", logical);
|
|
|
|
#endif
|
2008-05-20 19:17:46 +04:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mapped) {
|
2008-07-13 22:19:14 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf("mapping unmapped logical block %llu\n", logical);
|
|
|
|
#endif
|
2008-05-20 19:17:46 +04:00
|
|
|
if ((logical == extent.e_lblk + extent.e_len) &&
|
|
|
|
(physical == extent.e_pblk + extent.e_len) &&
|
|
|
|
(new_uninit == extent_uninit) &&
|
2008-06-07 19:53:56 +04:00
|
|
|
((int) extent.e_len < max_len-1)) {
|
2008-05-20 19:17:46 +04:00
|
|
|
extent.e_len++;
|
|
|
|
retval = ext2fs_extent_replace(handle, 0, &extent);
|
2009-07-20 07:37:44 +04:00
|
|
|
} else if ((logical == extent.e_lblk - 1) &&
|
|
|
|
(physical == extent.e_pblk - 1) &&
|
|
|
|
(new_uninit == extent_uninit) &&
|
|
|
|
((int) extent.e_len < max_len - 1)) {
|
|
|
|
extent.e_len++;
|
|
|
|
extent.e_lblk--;
|
|
|
|
extent.e_pblk--;
|
|
|
|
retval = ext2fs_extent_replace(handle, 0, &extent);
|
|
|
|
} else if (has_next &&
|
|
|
|
(logical == next_extent.e_lblk - 1) &&
|
|
|
|
(physical == next_extent.e_pblk - 1) &&
|
|
|
|
(new_uninit == next_uninit) &&
|
|
|
|
((int) next_extent.e_len < max_len - 1)) {
|
|
|
|
retval = ext2fs_extent_get(handle,
|
|
|
|
EXT2_EXTENT_NEXT_LEAF,
|
|
|
|
&next_extent);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
next_extent.e_len++;
|
|
|
|
next_extent.e_lblk--;
|
|
|
|
next_extent.e_pblk--;
|
|
|
|
retval = ext2fs_extent_replace(handle, 0, &next_extent);
|
2008-08-22 10:32:28 +04:00
|
|
|
} else if (logical < extent.e_lblk)
|
|
|
|
retval = ext2fs_extent_insert(handle, 0, &newextent);
|
|
|
|
else
|
2008-05-20 19:17:46 +04:00
|
|
|
retval = ext2fs_extent_insert(handle,
|
|
|
|
EXT2_EXTENT_INSERT_AFTER, &newextent);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
retval = ext2fs_extent_fix_parents(handle);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
} else if ((logical == extent.e_lblk) && (extent.e_len == 1)) {
|
2008-07-13 22:19:14 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf("(re/un)mapping only block in extent\n");
|
|
|
|
#endif
|
2008-05-20 19:17:46 +04:00
|
|
|
if (physical) {
|
2008-08-22 10:39:46 +04:00
|
|
|
retval = ext2fs_extent_replace(handle, 0, &newextent);
|
2008-05-20 19:17:46 +04:00
|
|
|
} else {
|
|
|
|
retval = ext2fs_extent_delete(handle, 0);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
ec = ext2fs_extent_fix_parents(handle);
|
|
|
|
if (ec != EXT2_ET_NO_CURRENT_NODE)
|
|
|
|
retval = ec;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
} else if (logical == extent.e_lblk + extent.e_len - 1) {
|
2008-07-13 22:19:14 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf("(re/un)mapping last block in extent\n");
|
|
|
|
#endif
|
2008-05-20 19:17:46 +04:00
|
|
|
if (physical) {
|
2009-07-20 07:37:44 +04:00
|
|
|
if (has_next &&
|
|
|
|
(logical == (next_extent.e_lblk - 1)) &&
|
|
|
|
(physical == (next_extent.e_pblk - 1)) &&
|
|
|
|
(new_uninit == next_uninit) &&
|
|
|
|
((int) next_extent.e_len < max_len - 1)) {
|
|
|
|
retval = ext2fs_extent_get(handle,
|
|
|
|
EXT2_EXTENT_NEXT_LEAF, &next_extent);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
next_extent.e_len++;
|
|
|
|
next_extent.e_lblk--;
|
|
|
|
next_extent.e_pblk--;
|
|
|
|
retval = ext2fs_extent_replace(handle, 0,
|
|
|
|
&next_extent);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
resize2fs: fix interior extent node corruption
If we have an extent tree like this (from debuge2fs's "ex" command):
Level Entries Logical Physical Length Flags
...
2/ 2 60/ 63 13096 - 13117 650024 - 650045 22
2/ 2 61/ 63 13134 - 13142 650062 - 650070 9
2/ 2 62/ 63 13193 - 13194 650121 - 650122 2
2/ 2 63/ 63 13227 - 13227 650155 - 650155 1 A)
1/ 2 4/ 14 13228 - 17108 655367 3881 B)
2/ 2 1/117 13228 - 13251 650156 - 650179 24 C)
2/ 2 2/117 13275 - 13287 650203 - 650215 13
2/ 2 3/117 13348 - 13353 650276 - 650281 6
...
and we resize the fs in such a way that all of those blocks must
be moved down, we do them one at a time. Eventually we move 1-block
extent A) to a lower block, and then follow it with the other
blocks in the next logical offsets from extent C) in the next
interior node B).
The userspace extent code tries to merge, so when it finds that
logical 13228 can be merged with logical 13227 into a single extent,
it does. And so on, all through extent C), up to block 13250 (why
not 13251? [1]), and eventually move the node block as well.
So we end up with this when all the blocks are moved post-resize:
Level Entries Logical Physical Length Flags
...
2/ 2 120/122 13193 - 13193 33220 - 33220 1
2/ 2 121/122 13194 - 13194 33221 - 33221 1
2/ 2 122/122 13227 - 13250 33222 - 33245 24 D)
1/ 2 5/ 19 13228 - 17108 34676 3881 E) ***
2/ 2 1/222 13251 - 13251 33246 - 33246 1 F)
2/ 2 2/222 13275 - 13286 33247 - 33258 12
...
All those adjacent blocks got moved into extent D), which is nice -
but the next interior node E) was never updated to reflect its new
starting point - it says the leaf extents beneath it start at 13228,
when in fact they start at 13251.
So as we move blocks one by one out of original extent C) above, we
need to keep updating C)'s parent node B) for a proper starting point.
fix_parents() does this.
Once the tree is corrupted like this, more corruption can
ensue post-resize, because we traverse the tree by interior nodes,
relying on their start block to know where we are in the tree.
If it gets off, we'll end up inserting blocks into the wrong part
of the tree, etc.
I have a testcase using fsx to create a complex extent tree which
is then moved during resize; it hit this corruption quite easily,
and with this fix, it succeeds.
Note the first hunk in the commit is for going the other way,
moving the last block of an extent to the extent after it; this
needs the same sort of fix-up, although I haven't seen it in
practice.
[1] We leave the last block because a single-block extent is its
own case, and there is no merging code in that case. \o/
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
2013-09-09 18:47:21 +04:00
|
|
|
retval = ext2fs_extent_fix_parents(handle);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
2009-07-20 07:37:44 +04:00
|
|
|
} else
|
|
|
|
retval = ext2fs_extent_insert(handle,
|
|
|
|
EXT2_EXTENT_INSERT_AFTER, &newextent);
|
2008-05-20 19:17:46 +04:00
|
|
|
if (retval)
|
|
|
|
goto done;
|
2009-07-07 23:30:32 +04:00
|
|
|
/* Now pointing at inserted extent; move back to prev */
|
2009-07-20 07:37:44 +04:00
|
|
|
retval = ext2fs_extent_get(handle,
|
|
|
|
EXT2_EXTENT_PREV_LEAF,
|
|
|
|
&extent);
|
2009-07-07 23:30:32 +04:00
|
|
|
if (retval)
|
|
|
|
goto done;
|
2008-05-20 19:17:46 +04:00
|
|
|
}
|
resize2fs: fix ENOSPC corruption case
http://people.redhat.com/esandeen/livecd-creator-imagefile.bz2
contains an image (for now) which, when resized to 578639, corrupts
the filesystem.
This is a bit crazy, I guess, because the fs currently has only
1 free block, but still, we should be graceful about the failure.
Perhaps it would make sense to check the requested valuea against
the minimum value resize2fs would compute for "-P" and fail (at
least without a force).
But in any case, this exposed 2 bugs when moving that one block
required an extent split, which is what hit the ENOSPC.
For starters, ext2fs_extent_set_bmap() in the "(re/un)mapping last
block in extent" case was replacing the old extent before the
new one was created; when the new extent creation failed, it
left us in an inconsistent state. Simply changing the order of
the two should fix this problem.
Next, ext2fs_extent_insert was calling ext2fs_extent_delete()
on *any* error, including one caused by failure to allocate a new
block to split the node to hold that extent ... the handle was left
unchanged, and we deleted the -original- extent.
As a quick fix for this, just don't do the delete if we fail the split,
though this may need to be smarter. I don't think we have terribly
consistent behavior about where a handle is left on various errors.
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2009-05-21 01:36:26 +04:00
|
|
|
extent.e_len--;
|
|
|
|
retval = ext2fs_extent_replace(handle, 0, &extent);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
2008-05-20 19:17:46 +04:00
|
|
|
} else if (logical == extent.e_lblk) {
|
2008-07-13 22:19:14 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf("(re/un)mapping first block in extent\n");
|
|
|
|
#endif
|
2009-07-10 08:19:28 +04:00
|
|
|
if (physical) {
|
2009-07-20 07:37:44 +04:00
|
|
|
if (has_prev &&
|
|
|
|
(logical == (prev_extent.e_lblk +
|
|
|
|
prev_extent.e_len)) &&
|
|
|
|
(physical == (prev_extent.e_pblk +
|
|
|
|
prev_extent.e_len)) &&
|
|
|
|
(new_uninit == prev_uninit) &&
|
|
|
|
((int) prev_extent.e_len < max_len-1)) {
|
|
|
|
retval = ext2fs_extent_get(handle,
|
|
|
|
EXT2_EXTENT_PREV_LEAF, &prev_extent);
|
2009-07-10 08:19:28 +04:00
|
|
|
if (retval)
|
|
|
|
goto done;
|
2009-07-20 07:37:44 +04:00
|
|
|
prev_extent.e_len++;
|
2009-07-10 08:19:28 +04:00
|
|
|
retval = ext2fs_extent_replace(handle, 0,
|
2009-07-20 07:37:44 +04:00
|
|
|
&prev_extent);
|
|
|
|
} else
|
2009-07-10 08:19:28 +04:00
|
|
|
retval = ext2fs_extent_insert(handle,
|
2009-07-20 07:37:44 +04:00
|
|
|
0, &newextent);
|
2009-07-10 08:19:28 +04:00
|
|
|
if (retval)
|
|
|
|
goto done;
|
2009-07-20 06:43:33 +04:00
|
|
|
retval = ext2fs_extent_get(handle,
|
|
|
|
EXT2_EXTENT_NEXT_LEAF,
|
|
|
|
&extent);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
2009-07-10 08:19:28 +04:00
|
|
|
}
|
2008-05-20 19:17:46 +04:00
|
|
|
extent.e_pblk++;
|
|
|
|
extent.e_lblk++;
|
|
|
|
extent.e_len--;
|
|
|
|
retval = ext2fs_extent_replace(handle, 0, &extent);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
resize2fs: fix interior extent node corruption
If we have an extent tree like this (from debuge2fs's "ex" command):
Level Entries Logical Physical Length Flags
...
2/ 2 60/ 63 13096 - 13117 650024 - 650045 22
2/ 2 61/ 63 13134 - 13142 650062 - 650070 9
2/ 2 62/ 63 13193 - 13194 650121 - 650122 2
2/ 2 63/ 63 13227 - 13227 650155 - 650155 1 A)
1/ 2 4/ 14 13228 - 17108 655367 3881 B)
2/ 2 1/117 13228 - 13251 650156 - 650179 24 C)
2/ 2 2/117 13275 - 13287 650203 - 650215 13
2/ 2 3/117 13348 - 13353 650276 - 650281 6
...
and we resize the fs in such a way that all of those blocks must
be moved down, we do them one at a time. Eventually we move 1-block
extent A) to a lower block, and then follow it with the other
blocks in the next logical offsets from extent C) in the next
interior node B).
The userspace extent code tries to merge, so when it finds that
logical 13228 can be merged with logical 13227 into a single extent,
it does. And so on, all through extent C), up to block 13250 (why
not 13251? [1]), and eventually move the node block as well.
So we end up with this when all the blocks are moved post-resize:
Level Entries Logical Physical Length Flags
...
2/ 2 120/122 13193 - 13193 33220 - 33220 1
2/ 2 121/122 13194 - 13194 33221 - 33221 1
2/ 2 122/122 13227 - 13250 33222 - 33245 24 D)
1/ 2 5/ 19 13228 - 17108 34676 3881 E) ***
2/ 2 1/222 13251 - 13251 33246 - 33246 1 F)
2/ 2 2/222 13275 - 13286 33247 - 33258 12
...
All those adjacent blocks got moved into extent D), which is nice -
but the next interior node E) was never updated to reflect its new
starting point - it says the leaf extents beneath it start at 13228,
when in fact they start at 13251.
So as we move blocks one by one out of original extent C) above, we
need to keep updating C)'s parent node B) for a proper starting point.
fix_parents() does this.
Once the tree is corrupted like this, more corruption can
ensue post-resize, because we traverse the tree by interior nodes,
relying on their start block to know where we are in the tree.
If it gets off, we'll end up inserting blocks into the wrong part
of the tree, etc.
I have a testcase using fsx to create a complex extent tree which
is then moved during resize; it hit this corruption quite easily,
and with this fix, it succeeds.
Note the first hunk in the commit is for going the other way,
moving the last block of an extent to the extent after it; this
needs the same sort of fix-up, although I haven't seen it in
practice.
[1] We leave the last block because a single-block extent is its
own case, and there is no merging code in that case. \o/
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
2013-09-09 18:47:21 +04:00
|
|
|
retval = ext2fs_extent_fix_parents(handle);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
2008-05-20 19:17:46 +04:00
|
|
|
} else {
|
|
|
|
__u32 orig_length;
|
2014-02-07 00:34:00 +04:00
|
|
|
blk64_t orig_lblk;
|
|
|
|
struct ext2fs_extent orig_extent;
|
|
|
|
errcode_t r2;
|
2008-05-20 19:17:46 +04:00
|
|
|
|
2008-07-13 22:19:14 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf("(re/un)mapping in middle of extent\n");
|
|
|
|
#endif
|
2008-05-20 19:17:46 +04:00
|
|
|
/* need to split this extent; later */
|
2014-02-07 00:34:00 +04:00
|
|
|
orig_lblk = extent.e_lblk;
|
2008-05-20 19:17:46 +04:00
|
|
|
orig_length = extent.e_len;
|
2014-02-07 00:34:00 +04:00
|
|
|
orig_extent = extent;
|
2008-05-20 19:17:46 +04:00
|
|
|
|
|
|
|
/* shorten pre-split extent */
|
|
|
|
extent.e_len = (logical - extent.e_lblk);
|
|
|
|
retval = ext2fs_extent_replace(handle, 0, &extent);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
|
|
|
/* insert our new extent, if any */
|
|
|
|
if (physical) {
|
|
|
|
/* insert new extent after current */
|
|
|
|
retval = ext2fs_extent_insert(handle,
|
|
|
|
EXT2_EXTENT_INSERT_AFTER, &newextent);
|
2014-02-07 00:34:00 +04:00
|
|
|
if (retval) {
|
|
|
|
r2 = ext2fs_extent_goto(handle, orig_lblk);
|
|
|
|
if (r2 == 0)
|
|
|
|
ext2fs_extent_replace(handle, 0,
|
|
|
|
&orig_extent);
|
2008-05-20 19:17:46 +04:00
|
|
|
goto done;
|
2014-02-07 00:34:00 +04:00
|
|
|
}
|
2008-05-20 19:17:46 +04:00
|
|
|
}
|
|
|
|
/* add post-split extent */
|
|
|
|
extent.e_pblk += extent.e_len + 1;
|
|
|
|
extent.e_lblk += extent.e_len + 1;
|
|
|
|
extent.e_len = orig_length - extent.e_len - 1;
|
|
|
|
retval = ext2fs_extent_insert(handle,
|
|
|
|
EXT2_EXTENT_INSERT_AFTER, &extent);
|
2014-02-07 00:34:00 +04:00
|
|
|
if (retval) {
|
|
|
|
if (physical) {
|
|
|
|
r2 = ext2fs_extent_goto(handle,
|
|
|
|
newextent.e_lblk);
|
|
|
|
if (r2 == 0)
|
|
|
|
ext2fs_extent_delete(handle, 0);
|
|
|
|
}
|
|
|
|
r2 = ext2fs_extent_goto(handle, orig_lblk);
|
|
|
|
if (r2 == 0)
|
|
|
|
ext2fs_extent_replace(handle, 0, &orig_extent);
|
2008-05-20 19:17:46 +04:00
|
|
|
goto done;
|
2014-02-07 00:34:00 +04:00
|
|
|
}
|
2008-05-20 19:17:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
/* get handle back to its position */
|
|
|
|
if (orig_height > handle->max_depth)
|
|
|
|
orig_height = handle->max_depth; /* In case we shortened the tree */
|
2012-12-23 03:50:33 +04:00
|
|
|
ext2fs_extent_goto2(handle, orig_height, orig_lblk);
|
2008-05-20 19:17:46 +04:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2008-05-21 01:13:41 +04:00
|
|
|
errcode_t ext2fs_extent_delete(ext2_extent_handle_t handle, int flags)
|
2007-10-15 07:04:58 +04:00
|
|
|
{
|
|
|
|
struct extent_path *path;
|
|
|
|
char *cp;
|
|
|
|
struct ext3_extent_header *eh;
|
2008-06-07 19:53:56 +04:00
|
|
|
errcode_t retval = 0;
|
2007-10-15 07:04:58 +04:00
|
|
|
|
|
|
|
EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
|
|
|
|
|
|
|
|
if (!(handle->fs->flags & EXT2_FLAG_RW))
|
|
|
|
return EXT2_ET_RO_FILSYS;
|
|
|
|
|
|
|
|
if (!handle->path)
|
|
|
|
return EXT2_ET_NO_CURRENT_NODE;
|
|
|
|
|
2009-07-20 07:32:09 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
{
|
|
|
|
struct ext2fs_extent extent;
|
|
|
|
|
|
|
|
retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT,
|
|
|
|
&extent);
|
|
|
|
if (retval == 0) {
|
|
|
|
printf("extent delete %u ", handle->ino);
|
|
|
|
dbg_print_extent(0, &extent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2007-10-15 07:04:58 +04:00
|
|
|
path = handle->path + handle->level;
|
|
|
|
if (!path->curr)
|
|
|
|
return EXT2_ET_NO_CURRENT_NODE;
|
|
|
|
|
|
|
|
cp = path->curr;
|
|
|
|
|
|
|
|
if (path->left) {
|
|
|
|
memmove(cp, cp + sizeof(struct ext3_extent_idx),
|
|
|
|
path->left * sizeof(struct ext3_extent_idx));
|
|
|
|
path->left--;
|
2008-03-14 06:55:40 +03:00
|
|
|
} else {
|
|
|
|
struct ext3_extent_idx *ix = path->curr;
|
|
|
|
ix--;
|
|
|
|
path->curr = ix;
|
2007-10-15 07:04:58 +04:00
|
|
|
}
|
2008-05-21 01:13:41 +04:00
|
|
|
if (--path->entries == 0)
|
2007-10-15 07:04:58 +04:00
|
|
|
path->curr = 0;
|
|
|
|
|
2008-05-21 01:13:41 +04:00
|
|
|
/* if non-root node has no entries left, remove it & parent ptr to it */
|
|
|
|
if (path->entries == 0 && handle->level) {
|
|
|
|
if (!(flags & EXT2_EXTENT_DELETE_KEEP_EMPTY)) {
|
|
|
|
struct ext2fs_extent extent;
|
2007-10-15 07:04:58 +04:00
|
|
|
|
2008-05-21 01:13:41 +04:00
|
|
|
retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP,
|
|
|
|
&extent);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
2007-10-15 07:04:58 +04:00
|
|
|
|
2008-05-21 01:13:41 +04:00
|
|
|
retval = ext2fs_extent_delete(handle, flags);
|
2012-06-11 07:52:11 +04:00
|
|
|
handle->inode->i_blocks -=
|
|
|
|
(handle->fs->blocksize *
|
|
|
|
EXT2FS_CLUSTER_RATIO(handle->fs)) / 512;
|
2009-06-18 02:49:01 +04:00
|
|
|
retval = ext2fs_write_inode(handle->fs, handle->ino,
|
|
|
|
handle->inode);
|
2009-10-26 04:46:58 +03:00
|
|
|
ext2fs_block_alloc_stats2(handle->fs,
|
|
|
|
extent.e_pblk, -1);
|
2008-05-21 01:13:41 +04:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
eh = (struct ext3_extent_header *) path->buf;
|
|
|
|
eh->eh_entries = ext2fs_cpu_to_le16(path->entries);
|
|
|
|
if ((path->entries == 0) && (handle->level == 0))
|
|
|
|
eh->eh_depth = handle->max_depth = 0;
|
|
|
|
retval = update_path(handle);
|
|
|
|
}
|
2007-10-15 07:04:58 +04:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
errcode_t ext2fs_extent_get_info(ext2_extent_handle_t handle,
|
|
|
|
struct ext2_extent_info *info)
|
|
|
|
{
|
|
|
|
struct extent_path *path;
|
|
|
|
|
|
|
|
EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
|
|
|
|
|
|
|
|
memset(info, 0, sizeof(struct ext2_extent_info));
|
|
|
|
|
|
|
|
path = handle->path + handle->level;
|
|
|
|
if (path) {
|
|
|
|
if (path->curr)
|
|
|
|
info->curr_entry = ((char *) path->curr - path->buf) /
|
|
|
|
sizeof(struct ext3_extent_idx);
|
|
|
|
else
|
|
|
|
info->curr_entry = 0;
|
|
|
|
info->num_entries = path->entries;
|
|
|
|
info->max_entries = path->max_entries;
|
|
|
|
info->bytes_avail = (path->max_entries - path->entries) *
|
|
|
|
sizeof(struct ext3_extent);
|
|
|
|
}
|
|
|
|
|
|
|
|
info->curr_level = handle->level;
|
|
|
|
info->max_depth = handle->max_depth;
|
|
|
|
info->max_lblk = ((__u64) 1 << 32) - 1;
|
|
|
|
info->max_pblk = ((__u64) 1 << 48) - 1;
|
|
|
|
info->max_len = (1UL << 15);
|
|
|
|
info->max_uninit_len = (1UL << 15) - 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
/*
|
2012-12-23 03:50:33 +04:00
|
|
|
* Override debugfs's prompt
|
2007-10-15 07:04:58 +04:00
|
|
|
*/
|
2008-03-18 06:17:13 +03:00
|
|
|
const char *debug_prog_name = "tst_extents";
|
2008-05-20 19:14:20 +04:00
|
|
|
|
2007-10-15 07:04:58 +04:00
|
|
|
#endif
|
|
|
|
|