/* * 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 "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; ino_t ino; struct ext2_inode inode; blk_t logblk; blk_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_entry *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 = malloc(sizeof(struct empty_dir_info_struct)); if (!edi) return NULL; memset(edi, 0, sizeof(struct empty_dir_info_struct)); 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)); free(edi); } void add_empty_dirblock(empty_dir_info edi, struct ext2_db_entry *db) { if (!edi || !db) return; if (db->ino == 11) return; /* Inode number 11 is usually lost+found */ printf(_("Empty directory block %d (#%d) in inode %d\n"), db->blk, db->blockcnt, db->ino); ext2fs_mark_block_bitmap(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_block(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, blk_t *block_nr, e2_blkcnt_t blockcnt, blk_t ref_block, int ref_offset, void *priv_data) { empty_dir_info edi = (empty_dir_info) priv_data; blk_t block, new_block; errcode_t retval; if (blockcnt < 0) return 0; block = *block_nr; do { retval = ext2fs_bmap(fs, edi->ino, &edi->inode, edi->block_buf, 0, edi->logblk, &new_block); if (retval) return DIRENT_ABORT; /* XXX what to do? */ if (new_block == 0) break; edi->logblk++; } while (ext2fs_test_block_bitmap(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_entry *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_iterate2(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; edi->inode.i_blocks -= edi->freed_blocks * (fs->blocksize / 512); (void) ext2fs_write_inode(fs, db->ino, &edi->inode); } return 0; } void process_empty_dirblock(e2fsck_t ctx, empty_dir_info edi) { if (!edi) return; edi->block_buf = malloc(ctx->fs->blocksize * 3); if (edi->block_buf) { (void) ext2fs_dblist_iterate(edi->empty_dblist, fix_directory, &edi); } free(edi->block_buf); free_empty_dirblock(edi); }