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 blk64_t logblk; 29 blk64_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_entry2 *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_entry2 *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_bitmap2(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_block2(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, blk64_t *block_nr, e2_blkcnt_t blockcnt, 120 blk64_t ref_block, int ref_offset, void *priv_data) 121 { 122 empty_dir_info edi = (empty_dir_info) priv_data; 123 blk64_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_bmap2(fs, edi->ino, &edi->inode, 131 edi->block_buf, 0, edi->logblk, 0, 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_bitmap2(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_entry2 *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_iterate3(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_iterate2(edi->empty_dblist, 188 fix_directory, &edi); 189 } 190 free(edi->block_buf); 191 free_empty_dirblock(edi); 192 } 193 194