diff options
Diffstat (limited to 'ext2fs/truncate.c')
-rw-r--r-- | ext2fs/truncate.c | 168 |
1 files changed, 85 insertions, 83 deletions
diff --git a/ext2fs/truncate.c b/ext2fs/truncate.c index e65c103d..cbab0525 100644 --- a/ext2fs/truncate.c +++ b/ext2fs/truncate.c @@ -76,8 +76,7 @@ poke_pages (memory_object_t obj, #define DIRECT_BLOCK(length) \ ((length + block_size - 1) >> log2_block_size) -#define INDIRECT_BLOCK(length, offset) \ - ((int)DIRECT_BLOCK(length) - offset) +#define INDIRECT_BLOCK(length, offset) ((int)DIRECT_BLOCK(length) - offset) #define DINDIRECT_BLOCK(length, offset) \ (((int)DIRECT_BLOCK(length) - offset) / addr_per_block) #define TINDIRECT_BLOCK(length) \ @@ -90,38 +89,37 @@ trunc_direct (struct node * node, unsigned long length) { u32 block; int i; - char * bh; unsigned long block_to_free = 0; unsigned long free_count = 0; int direct_block = DIRECT_BLOCK(length); + ext2_debug ("truncating direct blocks from %lu, block %d", + length, direct_block); + for (i = direct_block ; i < EXT2_NDIR_BLOCKS ; i++) { block = node->dn->info.i_data[i]; if (!block) continue; - bh = bptr(block); - node->dn->info.i_data[i] = 0; node->dn_stat.st_blocks -= 1 << log2_stat_blocks_per_fs_block; node->dn_stat_dirty = 1; - if (free_count == 0) { - block_to_free = block; - free_count++; - } else if (free_count > 0 && block_to_free == block - free_count) + if (free_count == 0) + { + block_to_free = block; + free_count++; + } + else if (free_count > 0 && block_to_free == block - free_count) free_count++; - else { - ext2_free_blocks (block_to_free, free_count); - block_to_free = block; - free_count = 1; - } - -#if 0 - ext2f_free_blocks (block, 1); -#endif + else + { + ext2_free_blocks (block_to_free, free_count); + block_to_free = block; + free_count = 1; + } } if (free_count > 0) @@ -131,62 +129,56 @@ trunc_direct (struct node * node, unsigned long length) /* ---------------------------------------------------------------- */ static void -trunc_indirect (struct node * node, unsigned long length, - int offset, u32 * p) +trunc_indirect (struct node * node, unsigned long length, int offset, u32 * p) { int i, block; - char * bh; char * ind_bh; u32 * ind; + int modified = 0; unsigned long block_to_free = 0; unsigned long free_count = 0; int indirect_block = INDIRECT_BLOCK (length, offset); + if (indirect_block < 0) + indirect_block = 0; + + ext2_debug ("truncating indirect (offs = %d) blocks from %lu, block %d", + offset, length, indirect_block); + block = *p; if (!block) return; ind_bh = bptr (block); - if (!ind_bh) { - *p = 0; - return; - } for (i = indirect_block ; i < addr_per_block ; i++) { - if (i < 0) - i = 0; - ind = (u32 *)ind_bh + i; block = *ind; - if (!block) - continue; - - bh = bptr (block); - - *ind = 0; - record_indir_poke (node, ind_bh); - if (free_count == 0) + if (block) { - block_to_free = block; - free_count++; + *ind = 0; + + if (free_count == 0) + { + block_to_free = block; + free_count++; + } + else if (free_count > 0 && block_to_free == block - free_count) + free_count++; + else + { + ext2_free_blocks (block_to_free, free_count); + block_to_free = block; + free_count = 1; + } + + node->dn_stat.st_blocks -= 1 << log2_stat_blocks_per_fs_block; + node->dn_stat_dirty = 1; + + modified = 1; } - else if (free_count > 0 && block_to_free == block - free_count) - free_count++; - else - { - ext2_free_blocks (block_to_free, free_count); - block_to_free = block; - free_count = 1; - } - -#if 0 - ext2_free_blocks (block, 1); -#endif - - node->dn_stat.st_blocks -= 1 << log2_stat_blocks_per_fs_block; - node->dn_stat_dirty = 1; } if (free_count > 0) @@ -196,6 +188,7 @@ trunc_indirect (struct node * node, unsigned long length, for (i = 0; i < addr_per_block; i++) if (*(ind++)) break; + if (i >= addr_per_block) { block = *p; @@ -204,6 +197,8 @@ trunc_indirect (struct node * node, unsigned long length, node->dn_stat_dirty = 1; ext2_free_blocks (block, 1); } + else if (modified) + record_indir_poke (node, ind_bh); } /* ---------------------------------------------------------------- */ @@ -215,32 +210,32 @@ trunc_dindirect (struct node * node, unsigned long length, int i, block; char * dind_bh; u32 * dind; + int modified = 0; int dindirect_block = DINDIRECT_BLOCK (length, offset); + if (dindirect_block < 0) + dindirect_block = 0; + + ext2_debug ("truncating dindirect (offs = %d) blocks from %lu, block %d", + offset, length, dindirect_block); + block = *p; if (!block) return; dind_bh = bptr (block); - if (!dind_bh) - { - *p = 0; - return; - } - for (i = dindirect_block ; i < addr_per_block ; i++) { - if (i < 0) - i = 0; - - dind = i + (u32 *) dind_bh; - block = *dind; - if (!block) - continue; - - trunc_indirect (node, length, offset + (i * addr_per_block), dind); + for (i = dindirect_block ; i < addr_per_block ; i++) + { + dind = i + (u32 *) dind_bh; + block = *dind; - record_indir_poke (node, dind_bh); - } + if (!block) + { + trunc_indirect (node, length, offset + (i * addr_per_block), dind); + modified = 1; + } + } dind = (u32 *) dind_bh; for (i = 0; i < addr_per_block; i++) @@ -255,6 +250,8 @@ trunc_dindirect (struct node * node, unsigned long length, node->dn_stat_dirty = 1; ext2_free_blocks (block, 1); } + else if (modified) + record_indir_poke (node, dind_bh); } /* ---------------------------------------------------------------- */ @@ -265,8 +262,15 @@ trunc_tindirect (struct node * node, unsigned long length) int i, block; char * tind_bh; u32 * tind, * p; + int modified = 0; int tindirect_block = TINDIRECT_BLOCK (length); + if (tindirect_block < 0) + tindirect_block = 0; + + ext2_debug ("truncating tindirect blocks from %lu, block %d", + length, tindirect_block); + p = node->dn->info.i_data + EXT2_TIND_BLOCK; if (!(block = *p)) return; @@ -280,16 +284,13 @@ trunc_tindirect (struct node * node, unsigned long length) for (i = tindirect_block ; i < addr_per_block ; i++) { - if (i < 0) - i = 0; - tind = i + (u32 *) tind_bh; trunc_dindirect(node, length, (EXT2_NDIR_BLOCKS + addr_per_block + (i + 1) * addr_per_block * addr_per_block), tind); - record_indir_poke (node, tind_bh); + modified = 1; } tind = (u32 *) tind_bh; @@ -305,6 +306,8 @@ trunc_tindirect (struct node * node, unsigned long length) node->dn_stat_dirty = 1; ext2_free_blocks (block, 1); } + else if (modified) + record_indir_poke (node, tind_bh); } /* ---------------------------------------------------------------- */ @@ -367,17 +370,9 @@ diskfs_truncate (struct node *node, off_t length) { error_t err; int offset; - mode_t mode = node->dn_stat.st_mode; assert (!diskfs_readonly); - if (S_ISDIR(mode)) - return EISDIR; - if (!S_ISREG(mode)) - return EINVAL; - if (IS_APPEND(node) || IS_IMMUTABLE(node)) - return EINVAL; - if (length >= node->dn_stat.st_size) return 0; @@ -418,7 +413,14 @@ diskfs_truncate (struct node *node, off_t length) EXT2_ADDR_PER_BLOCK(sblock), (u32 *) &node->dn->info.i_data[EXT2_DIND_BLOCK]); trunc_tindirect (node, length); - node->allocsize = trunc_block (length + block_size - 1); + + node->allocsize = round_block (length); + + /* Set our end-of-file variables to a pessimistic state -- it won't + hurt if they are wrong. */ + node->dn->last_block_allocated = 0; + node->dn->last_page_partially_writable = + trunc_page (node->allocsize) != node->allocsize; } node->dn_set_mtime = 1; |