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