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