emptydir.c revision f75c28de4731c2cd09f6ca1a23e25c968a1edc2f
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 */ 21struct 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 ino_t ino; 27 struct ext2_inode inode; 28 blk_t logblk; 29 blk_t freed_blocks; 30}; 31 32typedef struct empty_dir_info_struct *empty_dir_info; 33 34extern empty_dir_info init_empty_dir(e2fsck_t ctx); 35extern void free_empty_dirblock(empty_dir_info edi); 36extern void add_empty_dirblock(empty_dir_info edi, 37 struct ext2_db_entry *db); 38extern void process_empty_dirblock(e2fsck_t ctx, empty_dir_info edi); 39 40 41empty_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 68errout: 69 free_empty_dirblock(edi); 70 return NULL; 71} 72 73void 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 88void 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 %d (#%d) in inode %d\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 */ 119int 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 148static 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 edi->inode.i_blocks -= edi->freed_blocks * 172 (fs->blocksize / 512); 173 (void) ext2fs_write_inode(fs, db->ino, &edi->inode); 174 } 175 return 0; 176} 177 178void process_empty_dirblock(e2fsck_t ctx, empty_dir_info edi) 179{ 180 if (!edi) 181 return; 182 183 edi->block_buf = malloc(ctx->fs->blocksize * 3); 184 185 if (edi->block_buf) { 186 (void) ext2fs_dblist_iterate(edi->empty_dblist, 187 fix_directory, &edi); 188 } 189 free(edi->block_buf); 190 free_empty_dirblock(edi); 191} 192 193