2010-07-22 17:37:35 +04:00
|
|
|
/*
|
|
|
|
* punch.c --- deallocate blocks allocated to an inode
|
|
|
|
*
|
|
|
|
* Copyright (C) 2010 Theodore Ts'o.
|
|
|
|
*
|
|
|
|
* %Begin-Header%
|
|
|
|
* This file may be redistributed under the terms of the GNU Library
|
|
|
|
* General Public License, version 2.
|
|
|
|
* %End-Header%
|
|
|
|
*/
|
|
|
|
|
2011-09-19 01:34:37 +04:00
|
|
|
#include "config.h"
|
2010-07-22 17:37:35 +04:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#if HAVE_UNISTD_H
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include "ext2_fs.h"
|
|
|
|
#include "ext2fs.h"
|
2014-03-14 17:34:10 +04:00
|
|
|
#include "ext2fsP.h"
|
2010-07-22 17:37:35 +04:00
|
|
|
|
|
|
|
#undef PUNCH_DEBUG
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function returns 1 if the specified block is all zeros
|
|
|
|
*/
|
|
|
|
static int check_zero_block(char *buf, int blocksize)
|
|
|
|
{
|
|
|
|
char *cp = buf;
|
|
|
|
int left = blocksize;
|
|
|
|
|
|
|
|
while (left > 0) {
|
|
|
|
if (*cp++)
|
|
|
|
return 0;
|
|
|
|
left--;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This clever recursive function handles i_blocks[] as well as
|
|
|
|
* indirect, double indirect, and triple indirect blocks. It iterates
|
|
|
|
* over the entries in the i_blocks array or indirect blocks, and for
|
|
|
|
* each one, will recursively handle any indirect blocks and then
|
|
|
|
* frees and deallocates the blocks.
|
|
|
|
*/
|
|
|
|
static errcode_t ind_punch(ext2_filsys fs, struct ext2_inode *inode,
|
|
|
|
char *block_buf, blk_t *p, int level,
|
2015-11-30 23:26:21 +03:00
|
|
|
blk64_t start, blk64_t count, int max)
|
2010-07-22 17:37:35 +04:00
|
|
|
{
|
|
|
|
errcode_t retval;
|
2013-12-12 21:06:03 +04:00
|
|
|
blk_t b;
|
|
|
|
int i;
|
|
|
|
blk64_t offset, incr;
|
2010-07-22 17:37:35 +04:00
|
|
|
int freed = 0;
|
|
|
|
|
|
|
|
#ifdef PUNCH_DEBUG
|
2015-11-30 23:26:21 +03:00
|
|
|
printf("Entering ind_punch, level %d, start %llu, count %llu, "
|
2010-07-22 17:37:35 +04:00
|
|
|
"max %d\n", level, start, count, max);
|
|
|
|
#endif
|
2015-11-30 23:26:21 +03:00
|
|
|
incr = 1ULL << ((EXT2_BLOCK_SIZE_BITS(fs->super) - 2) * level);
|
|
|
|
for (i = 0, offset = 0; i < max; i++, p++, offset += incr) {
|
2013-10-07 17:51:35 +04:00
|
|
|
if (offset >= start + count)
|
2010-07-22 17:37:35 +04:00
|
|
|
break;
|
|
|
|
if (*p == 0 || (offset+incr) <= start)
|
|
|
|
continue;
|
|
|
|
b = *p;
|
|
|
|
if (level > 0) {
|
|
|
|
blk_t start2;
|
|
|
|
#ifdef PUNCH_DEBUG
|
|
|
|
printf("Reading indirect block %u\n", b);
|
|
|
|
#endif
|
|
|
|
retval = ext2fs_read_ind_block(fs, b, block_buf);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
start2 = (start > offset) ? start - offset : 0;
|
|
|
|
retval = ind_punch(fs, inode, block_buf + fs->blocksize,
|
|
|
|
(blk_t *) block_buf, level - 1,
|
|
|
|
start2, count - offset,
|
|
|
|
fs->blocksize >> 2);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
retval = ext2fs_write_ind_block(fs, b, block_buf);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
if (!check_zero_block(block_buf, fs->blocksize))
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#ifdef PUNCH_DEBUG
|
2013-12-12 21:06:03 +04:00
|
|
|
printf("Freeing block %u (offset %llu)\n", b, offset);
|
2010-07-22 17:37:35 +04:00
|
|
|
#endif
|
|
|
|
ext2fs_block_alloc_stats(fs, b, -1);
|
|
|
|
*p = 0;
|
|
|
|
freed++;
|
|
|
|
}
|
|
|
|
#ifdef PUNCH_DEBUG
|
|
|
|
printf("Freed %d blocks\n", freed);
|
|
|
|
#endif
|
|
|
|
return ext2fs_iblk_sub_blocks(fs, inode, freed);
|
|
|
|
}
|
|
|
|
|
2015-11-30 23:26:21 +03:00
|
|
|
#define BLK_T_MAX ((blk_t)~0ULL)
|
2010-07-22 17:37:35 +04:00
|
|
|
static errcode_t ext2fs_punch_ind(ext2_filsys fs, struct ext2_inode *inode,
|
2015-11-30 23:26:21 +03:00
|
|
|
char *block_buf, blk64_t start, blk64_t end)
|
2010-07-22 17:37:35 +04:00
|
|
|
{
|
|
|
|
errcode_t retval;
|
|
|
|
char *buf = 0;
|
|
|
|
int level;
|
|
|
|
int num = EXT2_NDIR_BLOCKS;
|
|
|
|
blk_t *bp = inode->i_block;
|
|
|
|
blk_t addr_per_block;
|
2013-12-12 21:06:03 +04:00
|
|
|
blk64_t max = EXT2_NDIR_BLOCKS;
|
2015-11-30 23:26:21 +03:00
|
|
|
blk_t count;
|
|
|
|
|
|
|
|
/* Check start/end don't overflow the 2^32-1 indirect block limit */
|
|
|
|
if (start > BLK_T_MAX)
|
|
|
|
return 0;
|
|
|
|
if (end >= BLK_T_MAX || end - start + 1 >= BLK_T_MAX)
|
|
|
|
count = BLK_T_MAX - start;
|
|
|
|
else
|
|
|
|
count = end - start + 1;
|
2010-07-22 17:37:35 +04:00
|
|
|
|
|
|
|
if (!block_buf) {
|
|
|
|
retval = ext2fs_get_array(3, fs->blocksize, &buf);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
block_buf = buf;
|
|
|
|
}
|
|
|
|
|
2015-11-30 23:26:21 +03:00
|
|
|
addr_per_block = (blk_t)fs->blocksize >> 2;
|
2010-07-22 17:37:35 +04:00
|
|
|
|
2013-12-12 21:06:03 +04:00
|
|
|
for (level = 0; level < 4; level++, max *= (blk64_t)addr_per_block) {
|
2010-07-22 17:37:35 +04:00
|
|
|
#ifdef PUNCH_DEBUG
|
2015-11-30 23:26:21 +03:00
|
|
|
printf("Main loop level %d, start %llu count %u "
|
2013-12-12 21:06:03 +04:00
|
|
|
"max %llu num %d\n", level, start, count, max, num);
|
2010-07-22 17:37:35 +04:00
|
|
|
#endif
|
|
|
|
if (start < max) {
|
|
|
|
retval = ind_punch(fs, inode, block_buf, bp, level,
|
|
|
|
start, count, num);
|
|
|
|
if (retval)
|
|
|
|
goto errout;
|
|
|
|
if (count > max)
|
|
|
|
count -= max - start;
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
start = 0;
|
|
|
|
} else
|
|
|
|
start -= max;
|
|
|
|
bp += num;
|
|
|
|
if (level == 0) {
|
|
|
|
num = 1;
|
|
|
|
max = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
retval = 0;
|
|
|
|
errout:
|
|
|
|
if (buf)
|
|
|
|
ext2fs_free_mem(&buf);
|
|
|
|
return retval;
|
|
|
|
}
|
2015-11-30 23:26:21 +03:00
|
|
|
#undef BLK_T_MAX
|
2010-07-22 17:37:35 +04:00
|
|
|
|
|
|
|
#ifdef PUNCH_DEBUG
|
|
|
|
|
|
|
|
#define dbg_printf(f, a...) printf(f, ## a)
|
|
|
|
|
|
|
|
static void dbg_print_extent(char *desc, struct ext2fs_extent *extent)
|
|
|
|
{
|
|
|
|
if (desc)
|
|
|
|
printf("%s: ", desc);
|
|
|
|
printf("extent: lblk %llu--%llu, len %u, pblk %llu, flags: ",
|
|
|
|
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);
|
|
|
|
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#define dbg_print_extent(desc, ex) do { } while (0)
|
|
|
|
#define dbg_printf(f, a...) do { } while (0)
|
|
|
|
#endif
|
|
|
|
|
2013-12-16 08:50:03 +04:00
|
|
|
/* Free a range of blocks, respecting cluster boundaries */
|
|
|
|
static errcode_t punch_extent_blocks(ext2_filsys fs, ext2_ino_t ino,
|
|
|
|
struct ext2_inode *inode,
|
|
|
|
blk64_t lfree_start, blk64_t free_start,
|
|
|
|
__u32 free_count, int *freed)
|
|
|
|
{
|
|
|
|
blk64_t pblk;
|
|
|
|
int freed_now = 0;
|
|
|
|
__u32 cluster_freed;
|
|
|
|
errcode_t retval = 0;
|
|
|
|
|
|
|
|
/* No bigalloc? Just free each block. */
|
|
|
|
if (EXT2FS_CLUSTER_RATIO(fs) == 1) {
|
|
|
|
*freed += free_count;
|
|
|
|
while (free_count-- > 0)
|
|
|
|
ext2fs_block_alloc_stats2(fs, free_start++, -1);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to free up to the next cluster boundary. We assume that all
|
|
|
|
* blocks in a logical cluster map to blocks from the same physical
|
|
|
|
* cluster, and that the offsets within the [pl]clusters match.
|
|
|
|
*/
|
|
|
|
if (free_start & EXT2FS_CLUSTER_MASK(fs)) {
|
|
|
|
retval = ext2fs_map_cluster_block(fs, ino, inode,
|
|
|
|
lfree_start, &pblk);
|
|
|
|
if (retval)
|
|
|
|
goto errout;
|
|
|
|
if (!pblk) {
|
|
|
|
ext2fs_block_alloc_stats2(fs, free_start, -1);
|
|
|
|
freed_now++;
|
|
|
|
}
|
|
|
|
cluster_freed = EXT2FS_CLUSTER_RATIO(fs) -
|
|
|
|
(free_start & EXT2FS_CLUSTER_MASK(fs));
|
|
|
|
if (cluster_freed > free_count)
|
|
|
|
cluster_freed = free_count;
|
|
|
|
free_count -= cluster_freed;
|
|
|
|
free_start += cluster_freed;
|
|
|
|
lfree_start += cluster_freed;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Free whole clusters from the middle of the range. */
|
2016-03-18 19:45:19 +03:00
|
|
|
while (free_count > 0 && free_count >= (unsigned) EXT2FS_CLUSTER_RATIO(fs)) {
|
2013-12-16 08:50:03 +04:00
|
|
|
ext2fs_block_alloc_stats2(fs, free_start, -1);
|
|
|
|
freed_now++;
|
|
|
|
cluster_freed = EXT2FS_CLUSTER_RATIO(fs);
|
|
|
|
free_count -= cluster_freed;
|
|
|
|
free_start += cluster_freed;
|
|
|
|
lfree_start += cluster_freed;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Try to free the last cluster. */
|
|
|
|
if (free_count > 0) {
|
|
|
|
retval = ext2fs_map_cluster_block(fs, ino, inode,
|
|
|
|
lfree_start, &pblk);
|
|
|
|
if (retval)
|
|
|
|
goto errout;
|
|
|
|
if (!pblk) {
|
|
|
|
ext2fs_block_alloc_stats2(fs, free_start, -1);
|
|
|
|
freed_now++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
errout:
|
|
|
|
*freed += freed_now;
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2010-07-22 17:37:35 +04:00
|
|
|
static errcode_t ext2fs_punch_extent(ext2_filsys fs, ext2_ino_t ino,
|
|
|
|
struct ext2_inode *inode,
|
|
|
|
blk64_t start, blk64_t end)
|
|
|
|
{
|
|
|
|
ext2_extent_handle_t handle = 0;
|
|
|
|
struct ext2fs_extent extent;
|
|
|
|
errcode_t retval;
|
2013-12-16 08:50:03 +04:00
|
|
|
blk64_t free_start, next, lfree_start;
|
2010-07-22 17:37:35 +04:00
|
|
|
__u32 free_count, newlen;
|
|
|
|
int freed = 0;
|
2013-10-07 17:35:22 +04:00
|
|
|
int op;
|
2010-07-22 17:37:35 +04:00
|
|
|
|
|
|
|
retval = ext2fs_extent_open2(fs, ino, inode, &handle);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
2013-12-12 22:14:50 +04:00
|
|
|
/*
|
|
|
|
* Find the extent closest to the start of the punch range. We don't
|
|
|
|
* check the return value because _goto() sets the current node to the
|
|
|
|
* next-lowest extent if 'start' is in a hole, and doesn't set a
|
|
|
|
* current node if there was a real error reading the extent tree.
|
|
|
|
* In that case, _get() will error out.
|
2013-12-12 22:25:40 +04:00
|
|
|
*
|
|
|
|
* Note: If _get() returns 'no current node', that simply means that
|
|
|
|
* there aren't any blocks mapped past this point in the file, so we're
|
|
|
|
* done.
|
2013-12-12 22:14:50 +04:00
|
|
|
*/
|
2010-07-22 17:37:35 +04:00
|
|
|
ext2fs_extent_goto(handle, start);
|
|
|
|
retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent);
|
2013-12-12 22:25:40 +04:00
|
|
|
if (retval == EXT2_ET_NO_CURRENT_NODE) {
|
|
|
|
retval = 0;
|
|
|
|
goto errout;
|
|
|
|
} else if (retval)
|
2010-07-22 17:37:35 +04:00
|
|
|
goto errout;
|
|
|
|
while (1) {
|
2013-10-07 17:35:22 +04:00
|
|
|
op = EXT2_EXTENT_NEXT_LEAF;
|
2010-07-22 17:37:35 +04:00
|
|
|
dbg_print_extent("main loop", &extent);
|
|
|
|
next = extent.e_lblk + extent.e_len;
|
|
|
|
dbg_printf("start %llu, end %llu, next %llu\n",
|
|
|
|
(unsigned long long) start,
|
|
|
|
(unsigned long long) end,
|
|
|
|
(unsigned long long) next);
|
|
|
|
if (start <= extent.e_lblk) {
|
2014-02-07 00:29:15 +04:00
|
|
|
/*
|
|
|
|
* Have we iterated past the end of the punch region?
|
|
|
|
* If so, we can stop.
|
|
|
|
*/
|
2010-07-22 17:37:35 +04:00
|
|
|
if (end < extent.e_lblk)
|
2014-02-07 00:29:15 +04:00
|
|
|
break;
|
2011-07-07 21:50:22 +04:00
|
|
|
dbg_printf("Case #%d\n", 1);
|
2010-07-22 17:37:35 +04:00
|
|
|
/* Start of deleted region before extent;
|
|
|
|
adjust beginning of extent */
|
|
|
|
free_start = extent.e_pblk;
|
2013-12-16 08:50:03 +04:00
|
|
|
lfree_start = extent.e_lblk;
|
2010-07-22 17:37:35 +04:00
|
|
|
if (next > end)
|
|
|
|
free_count = end - extent.e_lblk + 1;
|
|
|
|
else
|
|
|
|
free_count = extent.e_len;
|
|
|
|
extent.e_len -= free_count;
|
|
|
|
extent.e_lblk += free_count;
|
|
|
|
extent.e_pblk += free_count;
|
|
|
|
} else if (end >= next-1) {
|
2014-02-07 00:29:15 +04:00
|
|
|
/*
|
|
|
|
* Is the punch region beyond this extent? This can
|
|
|
|
* happen if start is already inside a hole. Try to
|
|
|
|
* advance to the next extent if this is the case.
|
|
|
|
*/
|
2010-07-22 17:37:35 +04:00
|
|
|
if (start >= next)
|
2014-02-07 00:29:15 +04:00
|
|
|
goto next_extent;
|
2010-07-22 17:37:35 +04:00
|
|
|
/* End of deleted region after extent;
|
|
|
|
adjust end of extent */
|
2011-07-07 21:50:22 +04:00
|
|
|
dbg_printf("Case #%d\n", 2);
|
2010-07-22 17:37:35 +04:00
|
|
|
newlen = start - extent.e_lblk;
|
|
|
|
free_start = extent.e_pblk + newlen;
|
2013-12-16 08:50:03 +04:00
|
|
|
lfree_start = extent.e_lblk + newlen;
|
2010-07-22 17:37:35 +04:00
|
|
|
free_count = extent.e_len - newlen;
|
|
|
|
extent.e_len = newlen;
|
|
|
|
} else {
|
|
|
|
struct ext2fs_extent newex;
|
|
|
|
|
2011-07-07 21:50:22 +04:00
|
|
|
dbg_printf("Case #%d\n", 3);
|
2010-07-22 17:37:35 +04:00
|
|
|
/* The hard case; we need to split the extent */
|
|
|
|
newex.e_pblk = extent.e_pblk +
|
|
|
|
(end + 1 - extent.e_lblk);
|
|
|
|
newex.e_lblk = end + 1;
|
|
|
|
newex.e_len = next - end - 1;
|
|
|
|
newex.e_flags = extent.e_flags;
|
|
|
|
|
|
|
|
extent.e_len = start - extent.e_lblk;
|
|
|
|
free_start = extent.e_pblk + extent.e_len;
|
2013-12-16 08:50:03 +04:00
|
|
|
lfree_start = extent.e_lblk + extent.e_len;
|
2010-07-22 17:37:35 +04:00
|
|
|
free_count = end - start + 1;
|
|
|
|
|
|
|
|
dbg_print_extent("inserting", &newex);
|
|
|
|
retval = ext2fs_extent_insert(handle,
|
|
|
|
EXT2_EXTENT_INSERT_AFTER, &newex);
|
|
|
|
if (retval)
|
|
|
|
goto errout;
|
libext2fs: fix parents when modifying extents
In ext2fs_extent_set_bmap() and ext2fs_punch_extent(), fix the parents
when altering either end of an extent so that the parent nodes reflect
the added mapping.
There's a slight complication to using fix_parents: if there are two
mappings to an lblk in the tree, the value of handle->path->curr can
point to either extent afterwards), which is documented in a comment.
Some additional color commentary from Darrick:
In the _set_bmap() case, I noticed that the "remapping last block in
extent" case would produce symptoms if we are trying to remap a
block from "extent" to "next_extent", and the two extents are
pointed to by different index nodes. _extent_replace(...,
next_extent) updates e_lblk in the leaf extent, but because there's
no _extent_fix_parents() call, the index nodes never get updated.
In the _punch_extent() case, we conclude that we need to split an
extent into two pieces since we're punching out the middle. If the
extent is the last extent in the block, the second extent will be
inserted into a new leaf node block. Without _fix_parents(), the
index node doesn't seem to get updated.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
2014-03-15 19:36:34 +04:00
|
|
|
retval = ext2fs_extent_fix_parents(handle);
|
|
|
|
if (retval)
|
|
|
|
goto errout;
|
|
|
|
/*
|
|
|
|
* Now pointing at inserted extent; so go back.
|
|
|
|
*
|
|
|
|
* We cannot use EXT2_EXTENT_PREV to go back; note the
|
|
|
|
* subtlety in the comment for fix_parents().
|
|
|
|
*/
|
|
|
|
retval = ext2fs_extent_goto(handle, extent.e_lblk);
|
2010-07-22 17:37:35 +04:00
|
|
|
if (retval)
|
|
|
|
goto errout;
|
|
|
|
}
|
|
|
|
if (extent.e_len) {
|
|
|
|
dbg_print_extent("replacing", &extent);
|
|
|
|
retval = ext2fs_extent_replace(handle, 0, &extent);
|
2014-02-07 00:30:59 +04:00
|
|
|
if (retval)
|
|
|
|
goto errout;
|
|
|
|
retval = ext2fs_extent_fix_parents(handle);
|
2010-07-22 17:37:35 +04:00
|
|
|
} else {
|
2013-10-07 17:35:22 +04:00
|
|
|
struct ext2fs_extent newex;
|
2013-12-12 22:23:51 +04:00
|
|
|
blk64_t old_lblk, next_lblk;
|
2011-07-07 21:50:22 +04:00
|
|
|
dbg_printf("deleting current extent%s\n", "");
|
2013-12-12 22:23:51 +04:00
|
|
|
|
2013-10-07 17:35:22 +04:00
|
|
|
/*
|
2013-12-12 22:23:51 +04:00
|
|
|
* Save the location of the next leaf, then slip
|
|
|
|
* back to the current extent.
|
2013-10-07 17:35:22 +04:00
|
|
|
*/
|
2013-12-12 22:23:51 +04:00
|
|
|
retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT,
|
|
|
|
&newex);
|
|
|
|
if (retval)
|
|
|
|
goto errout;
|
|
|
|
old_lblk = newex.e_lblk;
|
|
|
|
|
2013-10-07 17:35:22 +04:00
|
|
|
retval = ext2fs_extent_get(handle,
|
2013-12-12 22:23:51 +04:00
|
|
|
EXT2_EXTENT_NEXT_LEAF,
|
2013-10-07 17:35:22 +04:00
|
|
|
&newex);
|
2013-12-12 22:23:51 +04:00
|
|
|
if (retval == EXT2_ET_EXTENT_NO_NEXT)
|
|
|
|
next_lblk = old_lblk;
|
|
|
|
else if (retval)
|
|
|
|
goto errout;
|
|
|
|
else
|
|
|
|
next_lblk = newex.e_lblk;
|
|
|
|
|
|
|
|
retval = ext2fs_extent_goto(handle, old_lblk);
|
|
|
|
if (retval)
|
|
|
|
goto errout;
|
|
|
|
|
|
|
|
/* Now delete the extent. */
|
|
|
|
retval = ext2fs_extent_delete(handle, 0);
|
|
|
|
if (retval)
|
|
|
|
goto errout;
|
|
|
|
|
2014-02-07 00:30:59 +04:00
|
|
|
retval = ext2fs_extent_fix_parents(handle);
|
|
|
|
if (retval && retval != EXT2_ET_NO_CURRENT_NODE)
|
|
|
|
goto errout;
|
|
|
|
retval = 0;
|
|
|
|
|
2014-05-27 21:02:12 +04:00
|
|
|
/*
|
|
|
|
* Jump forward to the next extent. If there are
|
|
|
|
* errors, the ext2fs_extent_get down below will
|
|
|
|
* capture them for us.
|
|
|
|
*/
|
|
|
|
(void)ext2fs_extent_goto(handle, next_lblk);
|
2013-12-12 22:23:51 +04:00
|
|
|
op = EXT2_EXTENT_CURRENT;
|
2010-07-22 17:37:35 +04:00
|
|
|
}
|
|
|
|
if (retval)
|
|
|
|
goto errout;
|
|
|
|
dbg_printf("Free start %llu, free count = %u\n",
|
|
|
|
free_start, free_count);
|
2013-12-16 08:50:03 +04:00
|
|
|
retval = punch_extent_blocks(fs, ino, inode, lfree_start,
|
|
|
|
free_start, free_count, &freed);
|
|
|
|
if (retval)
|
|
|
|
goto errout;
|
2010-07-22 17:37:35 +04:00
|
|
|
next_extent:
|
2013-10-07 17:35:22 +04:00
|
|
|
retval = ext2fs_extent_get(handle, op,
|
2010-07-22 17:37:35 +04:00
|
|
|
&extent);
|
2013-10-07 17:35:22 +04:00
|
|
|
if (retval == EXT2_ET_EXTENT_NO_NEXT ||
|
|
|
|
retval == EXT2_ET_NO_CURRENT_NODE)
|
2010-07-22 17:37:35 +04:00
|
|
|
break;
|
|
|
|
if (retval)
|
|
|
|
goto errout;
|
|
|
|
}
|
|
|
|
dbg_printf("Freed %d blocks\n", freed);
|
|
|
|
retval = ext2fs_iblk_sub_blocks(fs, inode, freed);
|
|
|
|
errout:
|
|
|
|
ext2fs_extent_free(handle);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2014-03-03 09:34:40 +04:00
|
|
|
static errcode_t ext2fs_punch_inline_data(ext2_filsys fs, ext2_ino_t ino,
|
|
|
|
struct ext2_inode *inode,
|
2015-07-13 15:31:51 +03:00
|
|
|
blk64_t start,
|
|
|
|
blk64_t end EXT2FS_ATTR((unused)))
|
2014-03-03 09:34:40 +04:00
|
|
|
{
|
|
|
|
errcode_t retval;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In libext2fs ext2fs_punch is based on block unit. So that
|
|
|
|
* means that if start > 0 we don't need to do nothing. Due
|
|
|
|
* to this we will remove all inline data in ext2fs_punch()
|
|
|
|
* now.
|
|
|
|
*/
|
|
|
|
if (start > 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
memset((char *)inode->i_block, 0, EXT4_MIN_INLINE_DATA_SIZE);
|
|
|
|
inode->i_size = 0;
|
|
|
|
retval = ext2fs_write_inode(fs, ino, inode);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
return ext2fs_inline_data_ea_remove(fs, ino);
|
|
|
|
}
|
|
|
|
|
2010-07-22 17:37:35 +04:00
|
|
|
/*
|
2015-11-30 23:26:21 +03:00
|
|
|
* Deallocate all logical _blocks_ starting at start to end, inclusive.
|
|
|
|
* If end is ~0ULL, then this is effectively truncate.
|
2010-07-22 17:37:35 +04:00
|
|
|
*/
|
2013-12-17 03:56:36 +04:00
|
|
|
errcode_t ext2fs_punch(ext2_filsys fs, ext2_ino_t ino,
|
|
|
|
struct ext2_inode *inode,
|
|
|
|
char *block_buf, blk64_t start,
|
|
|
|
blk64_t end)
|
2010-07-22 17:37:35 +04:00
|
|
|
{
|
|
|
|
errcode_t retval;
|
|
|
|
struct ext2_inode inode_buf;
|
|
|
|
|
|
|
|
if (start > end)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
/* Read inode structure if necessary */
|
|
|
|
if (!inode) {
|
|
|
|
retval = ext2fs_read_inode(fs, ino, &inode_buf);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
inode = &inode_buf;
|
|
|
|
}
|
2014-03-03 09:34:40 +04:00
|
|
|
if (inode->i_flags & EXT4_INLINE_DATA_FL)
|
|
|
|
return ext2fs_punch_inline_data(fs, ino, inode, start, end);
|
|
|
|
else if (inode->i_flags & EXT4_EXTENTS_FL)
|
2010-07-22 17:37:35 +04:00
|
|
|
retval = ext2fs_punch_extent(fs, ino, inode, start, end);
|
2015-11-30 23:26:21 +03:00
|
|
|
else
|
|
|
|
retval = ext2fs_punch_ind(fs, inode, block_buf, start, end);
|
2010-07-22 17:37:35 +04:00
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
|
2015-11-30 23:26:21 +03:00
|
|
|
#ifdef PUNCH_DEBUG
|
|
|
|
printf("%u: write inode size now %u blocks %u\n",
|
|
|
|
ino, inode->i_size, inode->i_blocks);
|
|
|
|
#endif
|
2010-07-22 17:37:35 +04:00
|
|
|
return ext2fs_write_inode(fs, ino, inode);
|
|
|
|
}
|