Many files:
  Checkin of e2fsprogs 0.5b

diff --git a/e2fsck/pass1b.c b/e2fsck/pass1b.c
new file mode 100644
index 0000000..dd657bf
--- /dev/null
+++ b/e2fsck/pass1b.c
@@ -0,0 +1,640 @@
+/*
+ * pass1b.c --- Pass #1b of e2fsck
+ *
+ * This file contains pass1B, pass1C, and pass1D of e2fsck.  They are
+ * only invoked if pass 1 discovered blocks which are in use by more
+ * than one inode.
+ * 
+ * Pass1B scans the data blocks of all the inodes again, generating a
+ * complete list of duplicate blocks and which inodes have claimed
+ * them.
+ *
+ * Pass1C does a tree-traversal of the filesystem, to determine the
+ * parent directories of these inodes.  This step is necessary so that
+ * e2fsck can print out the pathnames of affected inodes.
+ *
+ * Pass1D is a reconciliation pass.  For each inode with duplicate
+ * blocks, the user is prompted if s/he would like to clone the file
+ * (so that the file gets a fresh copy of the duplicated blocks) or
+ * simply to delete the file.
+ * 
+ * Copyright (C) 1993, 1994 Theodore Ts'o.  This file may be
+ * redistributed under the terms of the GNU Public License.
+ * 
+ */
+
+#include <time.h>
+
+#include <et/com_err.h>
+#include "e2fsck.h"
+
+/*
+ * This is structure is allocated for each time that a block is
+ * claimed by more than one file.  So if a particular block is claimed
+ * by 3 files, then three copies of this structure will be allocated,
+ * one for each conflict.
+ *
+ * The linked list structure is as follows:
+ *
+ * dup_blk -->  block #34  --> block #35  --> block #47
+ * 		inode #12      inode #14      inode #17
+ * 		num_bad = 3    num_bad = 2    num_bad = 2
+ * 		  |              |               |
+ * 		  V              V               V
+ * 		block #34      block #35      block #47
+ * 		inode #14      inode #15      inode #23
+ * 		  |
+ * 		  V
+ * 		block #34
+ * 		inode #15
+ *
+ * The num_bad field indicates how many inodes are sharing a
+ * particular block, and is only stored in the first element of the
+ * linked list for a particular block.  As the block conflicts are
+ * resolved, num_bad is decremented; when it reaches 1, then we no
+ * longer need to worry about that block.
+ */
+struct dup_block {
+	blk_t		block;		/* Block number */
+	ino_t		ino;		/* Inode number */
+	int		num_bad;
+	/* Pointer to next dup record with different block */
+	struct dup_block *next_block;
+	/* Pointer to next dup record with different inode */
+	struct dup_block *next_inode;
+};
+
+/*
+ * This structure stores information about a particular inode which
+ * is sharing blocks with other inodes.  This information is collected
+ * to display to the user, so that the user knows what files he or she
+ * is dealing with, when trying to decide how to resolve the conflict
+ * of multiply-claimed blocks.
+ */
+struct dup_inode {
+	ino_t		ino;
+	time_t		mtime;
+	char		*pathname;
+	int		num_dupblocks;
+	int		flags;
+	struct dup_inode	*next;
+};
+
+#define DUP_INODE_DONT_FREE_PATHNAME	0x1
+
+static int process_pass1b_block(ext2_filsys fs, blk_t	*blocknr,
+				int	blockcnt, void	*private);
+static void delete_file(ext2_filsys fs, struct dup_inode *dp,
+			char *block_buf);
+static int clone_file(ext2_filsys fs, struct dup_inode *dp, char* block_buf);
+static void pass1b(ext2_filsys fs, char *block_buf);
+static void pass1c(ext2_filsys fs, char *block_buf);
+static void pass1d(ext2_filsys fs, char *block_buf);
+
+static struct dup_block *dup_blk = 0;
+static struct dup_inode *dup_ino = 0;
+static int dup_inode_count = 0;
+
+/*
+ * For pass1_check_directory and pass1_get_blocks
+ */
+extern ino_t stashed_ino;
+extern struct ext2_inode *stashed_inode;
+
+static char *inode_dup_map;
+
+/*
+ * Main procedure for handling duplicate blocks
+ */
+void pass1_dupblocks(ext2_filsys fs, char *block_buf)
+{
+	errcode_t		retval;
+	struct dup_block	*p, *q, *next_p, *next_q;
+	struct dup_inode	*r, *next_r;
+	
+	retval = ext2fs_allocate_inode_bitmap(fs, &inode_dup_map);
+	if (retval) {
+		com_err("ext2fs_allocate_inode_bitmap", retval,
+			"while allocating inode_dup_map");
+		fatal_error(0);
+	}
+	
+	pass1b(fs, block_buf);
+	pass1c(fs, block_buf);
+	pass1d(fs, block_buf);
+
+	/*
+	 * Time to free all of the accumulated data structures that we
+	 * don't need anymore.
+	 */
+	free(inode_dup_map);   	inode_dup_map = 0;
+	free(block_dup_map);    block_dup_map = 0;
+	for (p = dup_blk; p; p = next_p) {
+		next_p = p->next_block;
+		for (q = p; q; q = next_q) {
+			next_q = q->next_inode;
+			free(q);
+		}
+	}
+	for (r = dup_ino; r; r = next_r) {
+		next_r = r->next;
+		if (r->pathname && !(r->flags & DUP_INODE_DONT_FREE_PATHNAME))
+			free(r->pathname);
+		free(r);
+	}
+}
+
+/*
+ * Scan the inodes looking for inodes that contain duplicate blocks.
+ */
+struct process_block_struct {
+	ino_t	ino;
+	int	dup_blocks;
+};
+
+void pass1b(ext2_filsys fs, char *block_buf)
+{
+	ino_t	ino;
+	struct ext2_inode inode;
+	ext2_inode_scan	scan;
+	errcode_t	retval;
+	struct process_block_struct pb;
+	struct dup_inode *dp;
+	
+	printf("Duplicate blocks found... invoking duplicate block passes.\n");
+	printf("Pass 1B: Rescan for duplicate/bad blocks\n");
+	retval = ext2fs_open_inode_scan(fs, inode_buffer_blocks, &scan);
+	if (retval) {
+		com_err(program_name, retval, "while opening inode scan");
+		fatal_error(0);
+	}
+	retval = ext2fs_get_next_inode(scan, &ino, &inode);
+	if (retval) {
+		com_err(program_name, retval, "while starting inode scan");
+		fatal_error(0);
+	}
+	stashed_inode = &inode;
+	while (ino) {
+		stashed_ino = ino;
+		if ((ino != EXT2_BAD_INO) &&
+		    (!ext2fs_test_inode_bitmap(fs, inode_used_map, ino) ||
+		     !inode_has_valid_blocks(&inode)))
+			goto next;
+
+		pb.ino = ino;
+		pb.dup_blocks = 0;
+		retval = ext2fs_block_iterate(fs, ino, 0, block_buf,
+					      process_pass1b_block, &pb);
+		if (pb.dup_blocks) {
+			if (ino != EXT2_BAD_INO)
+				printf("\n");
+			dp = allocate_memory(sizeof(struct dup_inode),
+					     "duplicate inode record");
+			dp->ino = ino;
+			dp->mtime = inode.i_mtime;
+			dp->num_dupblocks = pb.dup_blocks;
+			dp->pathname = 0;
+			dp->flags = 0;
+			dp->next = dup_ino;
+			dup_ino = dp;
+			if (ino != EXT2_BAD_INO)
+				dup_inode_count++;
+		}
+		if (retval)
+			com_err(program_name, retval,
+				"while calling ext2fs_block_iterate in pass1b");
+		
+	next:
+		retval = ext2fs_get_next_inode(scan, &ino, &inode);
+		if (retval) {
+			com_err(program_name, retval,
+				"while doing inode scan");
+			fatal_error(0);
+		}
+	}
+	ext2fs_close_inode_scan(scan);
+	fs->get_blocks = 0;
+	fs->check_directory = 0;
+}
+
+int process_pass1b_block(ext2_filsys fs,
+			 blk_t	*block_nr,
+			 int blockcnt,
+			 void *private)
+{
+	struct process_block_struct *p;
+	struct dup_block *dp, *q, *r;
+	int i;
+
+	if (!*block_nr)
+		return 0;
+	p = (struct process_block_struct *) private;
+	
+	if (ext2fs_test_block_bitmap(fs, block_dup_map, *block_nr)) {
+		/* OK, this is a duplicate block */
+		if (p->ino != EXT2_BAD_INO) {
+			if (!p->dup_blocks)
+				printf("Duplicate/bad block(s) in inode %ld:",
+				       p->ino);
+			printf(" %ld", *block_nr);
+		}
+		p->dup_blocks++;
+		ext2fs_mark_block_bitmap(fs, block_dup_map, *block_nr);
+		ext2fs_mark_inode_bitmap(fs, inode_dup_map, p->ino);
+		dp = allocate_memory(sizeof(struct dup_block),
+				      "duplicate block record");
+		dp->block = *block_nr;
+		dp->ino = p->ino;
+		dp->num_bad = 0;
+		q = dup_blk;
+		while (q) {
+			if (q->block == *block_nr)
+				break;
+			q = q->next_block;
+		}
+		if (q) {
+			dp->next_inode = q->next_inode;
+			q->next_inode = dp;
+		} else {
+			dp->next_block = dup_blk;
+			dup_blk = dp;
+		}
+	}
+	/*
+	 * Set the num_bad field
+	 */
+	for (q = dup_blk; q; q = q->next_block) {
+		i = 0;
+		for (r = q; r; r = r->next_inode)
+			i++;
+		q->num_bad = i;
+	}
+	return 0;
+}
+
+/*
+ * Used by pass1c to name the "special" inodes.  They are declared as
+ * writeable strings to prevent const problems.
+ */
+#define num_special_inodes	7
+char special_inode_name[num_special_inodes][40] =
+{
+	"<The NULL inode>",			/* 0 */
+	"<The bad blocks inode>", 		/* 1 */
+	"/",					/* 2 */
+	"<The ACL index inode>",		/* 3 */
+	"<The ACL data inode>",			/* 4 */
+	"<The boot loader inode>",		/* 5 */
+	"<The undelete directory inode>"	/* 6 */
+};
+
+/*
+ * Pass 1c: Scan directories for inodes with duplicate blocks.  This
+ * is used so that we can print pathnames when prompting the user for
+ * what to do.
+ */
+struct process_dir_struct {
+	ext2_filsys	fs;
+	ino_t		dir_ino;
+	int		count;
+};
+
+void pass1c(ext2_filsys fs, char *block_buf)
+{
+	int	i;
+	struct dup_inode	*p;
+	errcode_t	retval;
+	char	buf[80];
+	int	inodes_left = dup_inode_count;
+	int	offset, entry;
+	struct ext2_dir_entry *dirent;
+
+	printf("Pass 1C: Scan directories for inodes with dup blocks.\n");
+
+	/*
+	 * First check to see if any of the inodes with dup blocks is
+	 * the bad block inode or the root inode; handle them as
+	 * special cases.
+	 */
+	for (p = dup_ino; p; p = p->next) {
+		if (p->ino < num_special_inodes) {
+			p->pathname = special_inode_name[p->ino];
+			p->flags |= DUP_INODE_DONT_FREE_PATHNAME;
+			inodes_left--;
+		}
+	}
+
+	/*
+	 * Search through all directories to translate inodes to names
+	 * (by searching for the containing directory for that inode.)
+	 */
+	for (i=0; inodes_left && i < dir_block_count; i++) {
+		retval = io_channel_read_blk(fs->io, dir_blocks[i].blk,
+					     1, block_buf);
+		entry = offset = 0;
+		while (offset < fs->blocksize) {
+			entry++;
+			dirent = (struct ext2_dir_entry *)
+				(block_buf + offset);
+			if (!dirent->inode ||
+			    ((dir_blocks[i].blockcnt == 0) && (entry <= 2)))
+				goto next;
+
+			if (!ext2fs_test_inode_bitmap(fs, inode_dup_map,
+						      dirent->inode))
+				goto next;
+
+			for (p = dup_ino; p; p = p->next) {
+				if (p->ino == dirent->inode)
+					break;
+			}
+
+			if (!p || p->pathname)
+				goto next;
+			
+			(void) ext2fs_get_pathname(fs, dir_blocks[i].ino,
+						   p->ino, &p->pathname);
+			inodes_left--;
+			
+		next:
+			if (dirent->rec_len < 8)
+				break;
+			offset += dirent->rec_len;
+		}
+	}
+
+
+	/*
+	 * If we can't get a name, then put in a generic one.
+	 */
+	for (p = dup_ino; p; p = p->next) {
+		if (!p->pathname) {
+			sprintf(buf, "<Unknown inode #%ld>", p->ino);
+			p->pathname = malloc(strlen(buf)+1);
+			if (!p->pathname) {
+				fprintf(stderr,	"pass1c: couldn't malloc "
+					"generic pathname\n");
+				fatal_error(0);
+			}
+			strcpy(p->pathname, buf);
+		}
+	}
+}	
+
+static void pass1d(ext2_filsys fs, char *block_buf)
+{
+	struct dup_inode	*p, *s;
+	struct dup_block	*q, *r;
+	ino_t	*shared;
+	int	shared_len;
+	int	i;
+	errcode_t	retval;
+	char	*time_str;
+	int	file_ok;
+	
+	printf("Pass 1D: Reconciling duplicate blocks\n");
+	read_bitmaps(fs);
+
+	printf("(There are %d inodes containing duplicate/bad blocks.)\n\n",
+	       dup_inode_count);
+	shared = allocate_memory(sizeof(ino_t) * dup_inode_count,
+				 "Shared inode list");
+	for (p = dup_ino; p; p = p->next) {
+		shared_len = 0;
+		file_ok = 1;
+		if (p->ino == EXT2_BAD_INO)
+			continue;
+
+		/*
+		 * Search through the duplicate records to see which
+		 * inodes share blocks with this one
+		 */
+		for (q = dup_blk; q; q = q->next_block) {
+			/*
+			 * See if this block is used by this inode.
+			 * If it isn't, continue.
+			 */
+			for (r = q; r; r = r->next_inode)
+				if (r->ino == p->ino)
+					break;
+			if (!r)
+				continue;
+			if (q->num_bad > 1)
+				file_ok = 0;
+			/*
+			 * Add all inodes used by this block to the
+			 * shared[] --- which is a unique list, so
+			 * if an inode is already in shared[], don't
+			 * add it again.
+			 */
+			for (r = q; r; r = r->next_inode) {
+				if (r->ino == p->ino)
+					continue;
+				for (i = 0; i < shared_len; i++)
+					if (shared[i] == r->ino)
+						break;
+				if (i == shared_len) {
+					shared[shared_len++] = r->ino;
+				}
+			}
+		}
+		time_str = ctime(&p->mtime);
+		time_str[24] = 0;
+		printf("File %s (inode #%ld, mod time %s) \n",
+		       p->pathname, p->ino, time_str);
+		printf("  has %d duplicate blocks, shared with %d file%s:\n",
+		       p->num_dupblocks, shared_len,
+		       (shared_len>1) ? "s" : "");
+		for (i = 0; i < shared_len; i++) {
+			for (s = dup_ino; s; s = s->next)
+				if (s->ino == shared[i])
+					break;
+			if (!s)
+				continue;
+			time_str = ctime(&s->mtime);
+			time_str[24] = 0;
+			printf("\t%s (inode #%ld, mod time %s)\n",
+			       s->pathname, s->ino, time_str);
+		}
+		if (file_ok) {
+			printf("Duplicated blocks already reassigned or cloned.\n\n");
+			continue;
+		}
+			
+		if (ask("Clone duplicate/bad blocks", 1)) {
+			retval = clone_file(fs, p, block_buf);
+			if (retval)
+				printf("Couldn't clone file: %s\n",
+				       error_message(retval));
+			else {
+				printf("\n");
+				continue;
+			}
+		}
+		if (ask("Delete file", 1))
+			delete_file(fs, p, block_buf);
+		else
+			ext2fs_unmark_valid(fs);
+		printf("\n");
+	}
+}
+
+static int delete_file_block(ext2_filsys fs,
+			     blk_t	*block_nr,
+			     int blockcnt,
+			     void *private)
+{
+	struct dup_block *p;
+
+	if (!*block_nr)
+		return 0;
+
+	if (ext2fs_test_block_bitmap(fs, block_dup_map, *block_nr)) {
+		for (p = dup_blk; p; p = p->next_block)
+			if (p->block == *block_nr)
+				break;
+		if (p) {
+			p->num_bad--;
+			if (p->num_bad == 1)
+				ext2fs_unmark_block_bitmap(fs, block_dup_map,
+							   *block_nr);
+		} else
+			com_err("delete_file_block", 0,
+				"internal error; can't find dup_blk for %d\n",
+				*block_nr);
+	} else {
+		ext2fs_unmark_block_bitmap(fs, block_found_map, *block_nr);
+		ext2fs_unmark_block_bitmap(fs, fs->block_map, *block_nr);
+	}
+		
+	return 0;
+}
+		
+static void delete_file(ext2_filsys fs, struct dup_inode *dp, char* block_buf)
+{
+	errcode_t	retval;
+	struct process_block_struct pb;
+	struct ext2_inode	inode;
+
+	pb.ino = dp->ino;
+	pb.dup_blocks = dp->num_dupblocks;
+	
+	retval = ext2fs_block_iterate(fs, dp->ino, 0, block_buf,
+				      delete_file_block, &pb);
+	if (retval)
+		com_err("delete_file", retval,
+			"while calling ext2fs_block_iterate for inode %d",
+			dp->ino);
+	ext2fs_unmark_inode_bitmap(fs, inode_used_map, dp->ino);
+	ext2fs_unmark_inode_bitmap(fs, inode_dir_map, dp->ino);
+	if (inode_bad_map)
+		ext2fs_unmark_inode_bitmap(fs, inode_bad_map, dp->ino);
+	ext2fs_unmark_inode_bitmap(fs, fs->inode_map, dp->ino);
+	ext2fs_mark_ib_dirty(fs);
+	ext2fs_mark_bb_dirty(fs);
+	retval = ext2fs_read_inode(fs, dp->ino, &inode);
+	if (retval) {
+		com_err("delete_file", retval, "while reading inode %d",
+			dp->ino);
+		return;
+	}
+	inode.i_links_count = 0;
+	inode.i_dtime = time(0);
+	retval = ext2fs_write_inode(fs, dp->ino, &inode);
+	if (retval) {
+		com_err("delete_file", retval, "while writing inode %d",
+			dp->ino);
+		return;
+	}
+}
+
+struct clone_struct {
+	errcode_t	errcode;
+	char	*buf;
+};
+
+static int clone_file_block(ext2_filsys fs,
+			    blk_t	*block_nr,
+			    int blockcnt,
+			    void *private)
+{
+	struct dup_block *p;
+	blk_t	new_block;
+	errcode_t	retval;
+	struct clone_struct *cs = (struct clone_struct *) private;
+
+	if (!*block_nr)
+		return 0;
+
+	if (ext2fs_test_block_bitmap(fs, block_dup_map, *block_nr)) {
+		for (p = dup_blk; p; p = p->next_block)
+			if (p->block == *block_nr)
+				break;
+		if (p) {
+			retval = ext2fs_new_block(fs, 0, block_found_map,
+						  &new_block);
+			if (retval) {
+				cs->errcode = retval;
+				return BLOCK_ABORT;
+			}
+			retval = io_channel_read_blk(fs->io, *block_nr, 1,
+						     cs->buf);
+			if (retval) {
+				cs->errcode = retval;
+				return BLOCK_ABORT;
+			}
+			retval = io_channel_write_blk(fs->io, new_block, 1,
+						      cs->buf);
+			if (retval) {
+				cs->errcode = retval;
+				return BLOCK_ABORT;
+			}
+			p->num_bad--;
+			if (p->num_bad == 1)
+				ext2fs_unmark_block_bitmap(fs, block_dup_map,
+							   *block_nr);
+			*block_nr = new_block;
+			ext2fs_mark_block_bitmap(fs, block_found_map,
+						 new_block);
+			ext2fs_mark_block_bitmap(fs, fs->block_map, new_block);
+			return BLOCK_CHANGED;
+		} else
+			com_err("clone_file_block", 0,
+				"internal error; can't find dup_blk for %d\n",
+				*block_nr);
+	}
+	return 0;
+}
+		
+static int clone_file(ext2_filsys fs, struct dup_inode *dp, char* block_buf)
+{
+	errcode_t	retval;
+	struct clone_struct cs;
+
+	cs.errcode = 0;
+	cs.buf = malloc(fs->blocksize);
+	if (!cs.buf)
+		return ENOMEM;
+	
+	retval = ext2fs_block_iterate(fs, dp->ino, 0, block_buf,
+				      clone_file_block, &cs);
+	ext2fs_mark_bb_dirty(fs);
+	free(cs.buf);
+	if (retval) {
+		com_err("clone_file", retval,
+			"while calling ext2fs_block_iterate for inode %d",
+			dp->ino);
+		return retval;
+	}
+	if (cs.errcode) {
+		com_err("clone_file", retval,
+			"returned from clone_file_block");
+		return retval;
+	}
+	return 0;
+}
+
+
+	
+
+