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 
init_empty_dir(e2fsck_t ctx)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 
free_empty_dirblock(empty_dir_info edi)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 
add_empty_dirblock(empty_dir_info edi,struct ext2_db_entry2 * db)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  */
empty_pass1(ext2_filsys fs,blk64_t * block_nr,e2_blkcnt_t blockcnt,blk64_t ref_block,int ref_offset,void * priv_data)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 
fix_directory(ext2_filsys fs,struct ext2_db_entry2 * db,void * priv_data)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 
process_empty_dirblock(e2fsck_t ctx,empty_dir_info edi)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