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 */
22struct 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
33typedef struct empty_dir_info_struct *empty_dir_info;
34
35extern empty_dir_info init_empty_dir(e2fsck_t ctx);
36extern void free_empty_dirblock(empty_dir_info edi);
37extern void add_empty_dirblock(empty_dir_info edi,
38			       struct ext2_db_entry2 *db);
39extern void process_empty_dirblock(e2fsck_t ctx, empty_dir_info edi);
40
41
42empty_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
69errout:
70	free_empty_dirblock(edi);
71	return NULL;
72}
73
74void 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
89void 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 */
120int 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
149static 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
180void 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