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