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