summaryrefslogtreecommitdiff
path: root/resize/banalysis.c
diff options
context:
space:
mode:
Diffstat (limited to 'resize/banalysis.c')
-rw-r--r--resize/banalysis.c141
1 files changed, 141 insertions, 0 deletions
diff --git a/resize/banalysis.c b/resize/banalysis.c
new file mode 100644
index 00000000..233158b6
--- /dev/null
+++ b/resize/banalysis.c
@@ -0,0 +1,141 @@
+/*
+ * banalysis.c --- Analyze a filesystem for a block struct
+ *
+ * Copyright (C) 1997 Theodore Ts'o. This file may be redistributed
+ * under the terms of the GNU Public License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h>
+#endif
+#include <linux/ext2_fs.h>
+
+#include "ext2fs/ext2fs.h"
+
+#include "ext2fs/brel.h"
+#include "banalysis.h"
+
+struct process_block_struct {
+ struct ext2_block_analyzer_funcs *funcs;
+ struct ext2_inode_context *ctx;
+ void *private;
+};
+
+/*
+ * This function returns 1 if the inode's block entries actually
+ * contain block entries.
+ */
+static int inode_has_valid_blocks(struct ext2_inode *inode)
+{
+ /*
+ * Only directories, regular files, and some symbolic links
+ * have valid block entries.
+ */
+ if (!LINUX_S_ISDIR(inode->i_mode) && !LINUX_S_ISREG(inode->i_mode) &&
+ !LINUX_S_ISLNK(inode->i_mode))
+ return 0;
+
+ /*
+ * If the symbolic link is a "fast symlink", then the symlink
+ * target is stored in the block entries.
+ */
+ if (LINUX_S_ISLNK (inode->i_mode) && inode->i_blocks == 0 &&
+ inode->i_size < EXT2_N_BLOCKS * sizeof (unsigned long))
+ return 0;
+
+ return 1;
+}
+
+static int process_block(ext2_filsys fs, blk_t *block_nr,
+ int blockcnt, blk_t ref_block,
+ int ref_offset, void *private)
+{
+ struct process_block_struct *pb = private;
+ blk_t new_block;
+ struct ext2_block_relocate_entry ent;
+
+ if (ref_block == 0)
+ ref_offset = blockcnt;
+
+ new_block = pb->funcs->block_analyze(fs, *block_nr, ref_block,
+ ref_offset, pb->ctx, pb->private);
+ if (new_block) {
+ ent.new = new_block;
+ ent.offset = ref_offset;
+ if (ref_block) {
+ ent.owner.block_ref = ref_block;
+ ent.flags = 0;
+ } else {
+ ent.owner.inode_ref = pb->ctx->ino;
+ ent.flags = RELOCATE_INODE_REF;
+ }
+ ext2fs_brel_put(pb->ctx->brel, *block_nr, &ent);
+ }
+ return 0;
+}
+
+errcode_t ext2_block_analyze(ext2_filsys fs,
+ struct ext2_block_analyzer_funcs *funcs,
+ ext2_brel block_relocation_table,
+ void *private)
+{
+ ino_t ino;
+ struct ext2_inode inode;
+ errcode_t retval;
+ struct process_block_struct pb;
+ struct ext2_inode_context ctx;
+ ext2_inode_scan scan;
+ char *block_buf;
+
+ retval = ext2fs_open_inode_scan(fs, 0, &scan);
+ if (retval)
+ return retval;
+
+ pb.funcs = funcs;
+ pb.private = private;
+ pb.ctx = &ctx;
+
+ block_buf = malloc(fs->blocksize * 3);
+ if (!block_buf)
+ return ENOMEM;
+
+ retval = ext2fs_get_next_inode(scan, &ino, &inode);
+ if (retval)
+ return retval;
+ ctx.ctx = private;
+ ctx.brel = block_relocation_table;
+ while (ino) {
+ if ((inode.i_links_count == 0) ||
+ !inode_has_valid_blocks(&inode))
+ goto next;
+
+ ctx.ino = ino;
+ ctx.inode = &inode;
+ ctx.error = 0;
+
+ if (funcs->pre_analyze &&
+ !(*funcs->pre_analyze)(fs, &ctx, private))
+ goto next;
+
+ retval = ext2fs_block_iterate2(fs, ino, 0, block_buf,
+ process_block, &pb);
+ if (retval)
+ return retval;
+
+ if (funcs->post_analyze)
+ (funcs->post_analyze)(fs, &ctx, private);
+
+ next:
+ retval = ext2fs_get_next_inode(scan, &ino, &inode);
+ if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
+ goto next;
+ }
+ return 0;
+}