diff options
Diffstat (limited to 'e2fsck/emptydir.c')
-rw-r--r-- | e2fsck/emptydir.c | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/e2fsck/emptydir.c b/e2fsck/emptydir.c new file mode 100644 index 0000000..7aea7b6 --- /dev/null +++ b/e2fsck/emptydir.c @@ -0,0 +1,194 @@ +/* + * emptydir.c --- clear empty directory blocks + * + * Copyright (C) 1998 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + * + * This file has the necessary routines to search for empty directory + * blocks and get rid of them. + */ + +#include "config.h" +#include "e2fsck.h" +#include "problem.h" + +/* + * For e2fsck.h + */ +struct empty_dir_info_struct { + ext2_dblist empty_dblist; + ext2fs_block_bitmap empty_dir_blocks; + ext2fs_inode_bitmap dir_map; + char *block_buf; + ext2_ino_t ino; + struct ext2_inode inode; + blk64_t logblk; + blk64_t freed_blocks; +}; + +typedef struct empty_dir_info_struct *empty_dir_info; + +extern empty_dir_info init_empty_dir(e2fsck_t ctx); +extern void free_empty_dirblock(empty_dir_info edi); +extern void add_empty_dirblock(empty_dir_info edi, + struct ext2_db_entry2 *db); +extern void process_empty_dirblock(e2fsck_t ctx, empty_dir_info edi); + + +empty_dir_info init_empty_dir(e2fsck_t ctx) +{ + empty_dir_info edi; + errcode_t retval; + + edi = e2fsck_allocate_memzero(ctx, sizeof(struct empty_dir_info_struct), + "empty dir info"); + if (retval) + return NULL; + + retval = ext2fs_init_dblist(ctx->fs, &edi->empty_dblist); + if (retval) + goto errout; + + retval = ext2fs_allocate_block_bitmap(ctx->fs, _("empty dirblocks"), + &edi->empty_dir_blocks); + if (retval) + goto errout; + + retval = ext2fs_allocate_inode_bitmap(ctx->fs, _("empty dir map"), + &edi->dir_map); + if (retval) + goto errout; + + return (edi); + +errout: + free_empty_dirblock(edi); + return NULL; +} + +void free_empty_dirblock(empty_dir_info edi) +{ + if (!edi) + return; + if (edi->empty_dblist) + ext2fs_free_dblist(edi->empty_dblist); + if (edi->empty_dir_blocks) + ext2fs_free_block_bitmap(edi->empty_dir_blocks); + if (edi->dir_map) + ext2fs_free_inode_bitmap(edi->dir_map); + + memset(edi, 0, sizeof(struct empty_dir_info_struct)); + ext2fs_free_mem(&edi); +} + +void add_empty_dirblock(empty_dir_info edi, + struct ext2_db_entry2 *db) +{ + if (!edi || !db) + return; + + if (db->ino == 11) + return; /* Inode number 11 is usually lost+found */ + + printf(_("Empty directory block %u (#%d) in inode %u\n"), + db->blk, db->blockcnt, db->ino); + + ext2fs_mark_block_bitmap2(edi->empty_dir_blocks, db->blk); + if (ext2fs_test_inode_bitmap(edi->dir_map, db->ino)) + return; + ext2fs_mark_inode_bitmap(edi->dir_map, db->ino); + + ext2fs_add_dir_block2(edi->empty_dblist, db->ino, + db->blk, db->blockcnt); +} + +/* + * Helper function used by fix_directory. + * + * XXX need to finish this. General approach is to use bmap to + * iterate over all of the logical blocks using the bmap function, and + * copy the block reference as necessary. Big question --- what do + * about error recovery? + * + * Also question --- how to free the indirect blocks. + */ +int empty_pass1(ext2_filsys fs, blk64_t *block_nr, e2_blkcnt_t blockcnt, + blk64_t ref_block, int ref_offset, void *priv_data) +{ + empty_dir_info edi = (empty_dir_info) priv_data; + blk64_t block, new_block; + errcode_t retval; + + if (blockcnt < 0) + return 0; + block = *block_nr; + do { + retval = ext2fs_bmap2(fs, edi->ino, &edi->inode, + edi->block_buf, 0, edi->logblk, 0, + &new_block); + if (retval) + return DIRENT_ABORT; /* XXX what to do? */ + if (new_block == 0) + break; + edi->logblk++; + } while (ext2fs_test_block_bitmap2(edi->empty_dir_blocks, new_block)); + + if (new_block == block) + return 0; + if (new_block == 0) + edi->freed_blocks++; + *block_nr = new_block; + return BLOCK_CHANGED; +} + +static int fix_directory(ext2_filsys fs, + struct ext2_db_entry2 *db, + void *priv_data) +{ + errcode_t retval; + + empty_dir_info edi = (empty_dir_info) priv_data; + + edi->logblk = 0; + edi->freed_blocks = 0; + edi->ino = db->ino; + + retval = ext2fs_read_inode(fs, db->ino, &edi->inode); + if (retval) + return 0; + + retval = ext2fs_block_iterate3(fs, db->ino, 0, edi->block_buf, + empty_pass1, edi); + if (retval) + return 0; + + if (edi->freed_blocks) { + edi->inode.i_size -= edi->freed_blocks * fs->blocksize; + ext2fs_iblk_add_blocks(fs, &edi->inode, edi->freed_blocks); + retval = ext2fs_write_inode(fs, db->ino, &edi->inode); + if (retval) + return 0; + } + return 0; +} + +void process_empty_dirblock(e2fsck_t ctx, empty_dir_info edi) +{ + if (!edi) + return; + + retval = ext2f_get_mem(ctx, ctx->fs->blocksize * 3, + &edi->block_buf); + + if (edi->block_buf) { + (void) ext2fs_dblist_iterate2(edi->empty_dblist, + fix_directory, &edi); + } + ext2fs_free_mem(&edi->block_buf); + free_empty_dirblock(edi); +} + |