Home | History | Annotate | Download | only in e2fsck
      1 /*
      2  * emptydir.c --- clear empty directory blocks
      3  *
      4  * Copyright (C) 1998 Theodore Ts'o
      5  *
      6  * %Begin-Header%
      7  * This file may be redistributed under the terms of the GNU Public
      8  * License.
      9  * %End-Header%
     10  *
     11  * This file has the necessary routines to search for empty directory
     12  * blocks and get rid of them.
     13  */
     14 
     15 #include "e2fsck.h"
     16 #include "problem.h"
     17 
     18 /*
     19  * For e2fsck.h
     20  */
     21 struct empty_dir_info_struct {
     22 	ext2_dblist empty_dblist;
     23 	ext2fs_block_bitmap empty_dir_blocks;
     24 	ext2fs_inode_bitmap dir_map;
     25 	char *block_buf;
     26 	ext2_ino_t ino;
     27 	struct ext2_inode inode;
     28 	blk_t	logblk;
     29 	blk_t	freed_blocks;
     30 };
     31 
     32 typedef struct empty_dir_info_struct *empty_dir_info;
     33 
     34 extern empty_dir_info init_empty_dir(e2fsck_t ctx);
     35 extern void free_empty_dirblock(empty_dir_info edi);
     36 extern void add_empty_dirblock(empty_dir_info edi,
     37 			       struct ext2_db_entry *db);
     38 extern void process_empty_dirblock(e2fsck_t ctx, empty_dir_info edi);
     39 
     40 
     41 empty_dir_info init_empty_dir(e2fsck_t ctx)
     42 {
     43 	empty_dir_info	edi;
     44 	errcode_t	retval;
     45 
     46 	edi = malloc(sizeof(struct empty_dir_info_struct));
     47 	if (!edi)
     48 		return NULL;
     49 
     50 	memset(edi, 0, sizeof(struct empty_dir_info_struct));
     51 
     52 	retval = ext2fs_init_dblist(ctx->fs, &edi->empty_dblist);
     53 	if (retval)
     54 		goto errout;
     55 
     56 	retval = ext2fs_allocate_block_bitmap(ctx->fs, _("empty dirblocks"),
     57 					      &edi->empty_dir_blocks);
     58 	if (retval)
     59 		goto errout;
     60 
     61 	retval = ext2fs_allocate_inode_bitmap(ctx->fs, _("empty dir map"),
     62 					      &edi->dir_map);
     63 	if (retval)
     64 		goto errout;
     65 
     66 	return (edi);
     67 
     68 errout:
     69 	free_empty_dirblock(edi);
     70 	return NULL;
     71 }
     72 
     73 void free_empty_dirblock(empty_dir_info edi)
     74 {
     75 	if (!edi)
     76 		return;
     77 	if (edi->empty_dblist)
     78 		ext2fs_free_dblist(edi->empty_dblist);
     79 	if (edi->empty_dir_blocks)
     80 		ext2fs_free_block_bitmap(edi->empty_dir_blocks);
     81 	if (edi->dir_map)
     82 		ext2fs_free_inode_bitmap(edi->dir_map);
     83 
     84 	memset(edi, 0, sizeof(struct empty_dir_info_struct));
     85 	free(edi);
     86 }
     87 
     88 void add_empty_dirblock(empty_dir_info edi,
     89 			struct ext2_db_entry *db)
     90 {
     91 	if (!edi || !db)
     92 		return;
     93 
     94 	if (db->ino == 11)
     95 		return;		/* Inode number 11 is usually lost+found */
     96 
     97 	printf(_("Empty directory block %u (#%d) in inode %u\n"),
     98 	       db->blk, db->blockcnt, db->ino);
     99 
    100 	ext2fs_mark_block_bitmap(edi->empty_dir_blocks, db->blk);
    101 	if (ext2fs_test_inode_bitmap(edi->dir_map, db->ino))
    102 		return;
    103 	ext2fs_mark_inode_bitmap(edi->dir_map, db->ino);
    104 
    105 	ext2fs_add_dir_block(edi->empty_dblist, db->ino,
    106 			     db->blk, db->blockcnt);
    107 }
    108 
    109 /*
    110  * Helper function used by fix_directory.
    111  *
    112  * XXX need to finish this.  General approach is to use bmap to
    113  * iterate over all of the logical blocks using the bmap function, and
    114  * copy the block reference as necessary.  Big question --- what do
    115  * about error recovery?
    116  *
    117  * Also question --- how to free the indirect blocks.
    118  */
    119 int empty_pass1(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt,
    120 		blk_t ref_block, int ref_offset, void *priv_data)
    121 {
    122 	empty_dir_info edi = (empty_dir_info) priv_data;
    123 	blk_t	block, new_block;
    124 	errcode_t	retval;
    125 
    126 	if (blockcnt < 0)
    127 		return 0;
    128 	block = *block_nr;
    129 	do {
    130 		retval = ext2fs_bmap(fs, edi->ino, &edi->inode,
    131 				     edi->block_buf, 0, edi->logblk,
    132 				     &new_block);
    133 		if (retval)
    134 			return DIRENT_ABORT;   /* XXX what to do? */
    135 		if (new_block == 0)
    136 			break;
    137 		edi->logblk++;
    138 	} while (ext2fs_test_block_bitmap(edi->empty_dir_blocks, new_block));
    139 
    140 	if (new_block == block)
    141 		return 0;
    142 	if (new_block == 0)
    143 		edi->freed_blocks++;
    144 	*block_nr = new_block;
    145 	return BLOCK_CHANGED;
    146 }
    147 
    148 static int fix_directory(ext2_filsys fs,
    149 			 struct ext2_db_entry *db,
    150 			 void *priv_data)
    151 {
    152 	errcode_t	retval;
    153 
    154 	empty_dir_info edi = (empty_dir_info) priv_data;
    155 
    156 	edi->logblk = 0;
    157 	edi->freed_blocks = 0;
    158 	edi->ino = db->ino;
    159 
    160 	retval = ext2fs_read_inode(fs, db->ino, &edi->inode);
    161 	if (retval)
    162 		return 0;
    163 
    164 	retval = ext2fs_block_iterate2(fs, db->ino, 0, edi->block_buf,
    165 				       empty_pass1, edi);
    166 	if (retval)
    167 		return 0;
    168 
    169 	if (edi->freed_blocks) {
    170 		edi->inode.i_size -= edi->freed_blocks * fs->blocksize;
    171 		ext2fs_iblk_add_blocks(fs, &edi->inode, edi->freed_blocks);
    172 		retval = ext2fs_write_inode(fs, db->ino, &edi->inode);
    173 		if (retval)
    174 			return 0;
    175 	}
    176 	return 0;
    177 }
    178 
    179 void process_empty_dirblock(e2fsck_t ctx, empty_dir_info edi)
    180 {
    181 	if (!edi)
    182 		return;
    183 
    184 	edi->block_buf = malloc(ctx->fs->blocksize * 3);
    185 
    186 	if (edi->block_buf) {
    187 		(void) ext2fs_dblist_iterate(edi->empty_dblist,
    188 					     fix_directory, &edi);
    189 	}
    190 	free(edi->block_buf);
    191 	free_empty_dirblock(edi);
    192 }
    193 
    194