summaryrefslogtreecommitdiffstats
path: root/e2fsck/emptydir.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--e2fsck/emptydir.c194
1 files changed, 194 insertions, 0 deletions
diff --git a/e2fsck/emptydir.c b/e2fsck/emptydir.c
new file mode 100644
index 0000000..7aea7b6
--- /dev/null
+++ b/e2fsck/emptydir.c
@@ -0,0 +1,194 @@
+/*
+ * emptydir.c --- clear empty directory blocks
+ *
+ * Copyright (C) 1998 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ *
+ * This file has the necessary routines to search for empty directory
+ * blocks and get rid of them.
+ */
+
+#include "config.h"
+#include "e2fsck.h"
+#include "problem.h"
+
+/*
+ * For e2fsck.h
+ */
+struct empty_dir_info_struct {
+ ext2_dblist empty_dblist;
+ ext2fs_block_bitmap empty_dir_blocks;
+ ext2fs_inode_bitmap dir_map;
+ char *block_buf;
+ ext2_ino_t ino;
+ struct ext2_inode inode;
+ blk64_t logblk;
+ blk64_t freed_blocks;
+};
+
+typedef struct empty_dir_info_struct *empty_dir_info;
+
+extern empty_dir_info init_empty_dir(e2fsck_t ctx);
+extern void free_empty_dirblock(empty_dir_info edi);
+extern void add_empty_dirblock(empty_dir_info edi,
+ struct ext2_db_entry2 *db);
+extern void process_empty_dirblock(e2fsck_t ctx, empty_dir_info edi);
+
+
+empty_dir_info init_empty_dir(e2fsck_t ctx)
+{
+ empty_dir_info edi;
+ errcode_t retval;
+
+ edi = e2fsck_allocate_memzero(ctx, sizeof(struct empty_dir_info_struct),
+ "empty dir info");
+ if (retval)
+ return NULL;
+
+ retval = ext2fs_init_dblist(ctx->fs, &edi->empty_dblist);
+ if (retval)
+ goto errout;
+
+ retval = ext2fs_allocate_block_bitmap(ctx->fs, _("empty dirblocks"),
+ &edi->empty_dir_blocks);
+ if (retval)
+ goto errout;
+
+ retval = ext2fs_allocate_inode_bitmap(ctx->fs, _("empty dir map"),
+ &edi->dir_map);
+ if (retval)
+ goto errout;
+
+ return (edi);
+
+errout:
+ free_empty_dirblock(edi);
+ return NULL;
+}
+
+void free_empty_dirblock(empty_dir_info edi)
+{
+ if (!edi)
+ return;
+ if (edi->empty_dblist)
+ ext2fs_free_dblist(edi->empty_dblist);
+ if (edi->empty_dir_blocks)
+ ext2fs_free_block_bitmap(edi->empty_dir_blocks);
+ if (edi->dir_map)
+ ext2fs_free_inode_bitmap(edi->dir_map);
+
+ memset(edi, 0, sizeof(struct empty_dir_info_struct));
+ ext2fs_free_mem(&edi);
+}
+
+void add_empty_dirblock(empty_dir_info edi,
+ struct ext2_db_entry2 *db)
+{
+ if (!edi || !db)
+ return;
+
+ if (db->ino == 11)
+ return; /* Inode number 11 is usually lost+found */
+
+ printf(_("Empty directory block %u (#%d) in inode %u\n"),
+ db->blk, db->blockcnt, db->ino);
+
+ ext2fs_mark_block_bitmap2(edi->empty_dir_blocks, db->blk);
+ if (ext2fs_test_inode_bitmap(edi->dir_map, db->ino))
+ return;
+ ext2fs_mark_inode_bitmap(edi->dir_map, db->ino);
+
+ ext2fs_add_dir_block2(edi->empty_dblist, db->ino,
+ db->blk, db->blockcnt);
+}
+
+/*
+ * Helper function used by fix_directory.
+ *
+ * XXX need to finish this. General approach is to use bmap to
+ * iterate over all of the logical blocks using the bmap function, and
+ * copy the block reference as necessary. Big question --- what do
+ * about error recovery?
+ *
+ * Also question --- how to free the indirect blocks.
+ */
+int empty_pass1(ext2_filsys fs, blk64_t *block_nr, e2_blkcnt_t blockcnt,
+ blk64_t ref_block, int ref_offset, void *priv_data)
+{
+ empty_dir_info edi = (empty_dir_info) priv_data;
+ blk64_t block, new_block;
+ errcode_t retval;
+
+ if (blockcnt < 0)
+ return 0;
+ block = *block_nr;
+ do {
+ retval = ext2fs_bmap2(fs, edi->ino, &edi->inode,
+ edi->block_buf, 0, edi->logblk, 0,
+ &new_block);
+ if (retval)
+ return DIRENT_ABORT; /* XXX what to do? */
+ if (new_block == 0)
+ break;
+ edi->logblk++;
+ } while (ext2fs_test_block_bitmap2(edi->empty_dir_blocks, new_block));
+
+ if (new_block == block)
+ return 0;
+ if (new_block == 0)
+ edi->freed_blocks++;
+ *block_nr = new_block;
+ return BLOCK_CHANGED;
+}
+
+static int fix_directory(ext2_filsys fs,
+ struct ext2_db_entry2 *db,
+ void *priv_data)
+{
+ errcode_t retval;
+
+ empty_dir_info edi = (empty_dir_info) priv_data;
+
+ edi->logblk = 0;
+ edi->freed_blocks = 0;
+ edi->ino = db->ino;
+
+ retval = ext2fs_read_inode(fs, db->ino, &edi->inode);
+ if (retval)
+ return 0;
+
+ retval = ext2fs_block_iterate3(fs, db->ino, 0, edi->block_buf,
+ empty_pass1, edi);
+ if (retval)
+ return 0;
+
+ if (edi->freed_blocks) {
+ edi->inode.i_size -= edi->freed_blocks * fs->blocksize;
+ ext2fs_iblk_add_blocks(fs, &edi->inode, edi->freed_blocks);
+ retval = ext2fs_write_inode(fs, db->ino, &edi->inode);
+ if (retval)
+ return 0;
+ }
+ return 0;
+}
+
+void process_empty_dirblock(e2fsck_t ctx, empty_dir_info edi)
+{
+ if (!edi)
+ return;
+
+ retval = ext2f_get_mem(ctx, ctx->fs->blocksize * 3,
+ &edi->block_buf);
+
+ if (edi->block_buf) {
+ (void) ext2fs_dblist_iterate2(edi->empty_dblist,
+ fix_directory, &edi);
+ }
+ ext2fs_free_mem(&edi->block_buf);
+ free_empty_dirblock(edi);
+}
+