ChangeLog, journal.c, problem.c, problem.h, super.c:
  super.c (release_inode_block, release_inode_blocks,
  	release_orphan_inodes): Add code to deal with truncating inodes which
  	are still in use (but which are on the orphan list because they need
  	truncation).
  problem.c, problem.h: Rename PR_0_CLEAR_ORPHAN_INODE to
  	PR_0_ORPHAN_CLEAR_INODE, and remove PR_0_ORPHAN_INODE_INUSE.
  journal.c (e2fsck_run_ext3_journal): Add i18n support, and print a
  	message when the journal is being recovered.

diff --git a/e2fsck/ChangeLog b/e2fsck/ChangeLog
index 51b9e44..07fa523 100644
--- a/e2fsck/ChangeLog
+++ b/e2fsck/ChangeLog
@@ -1,5 +1,17 @@
 2000-10-24    <tytso@snap.thunk.org>
 
+	* super.c (release_inode_block, release_inode_blocks,
+		release_orphan_inodes): Add code to deal with truncating
+		inodes which are still in use (but which are on the orphan
+		list because they need truncation).
+
+	* problem.c, problem.h: Rename PR_0_CLEAR_ORPHAN_INODE to
+		PR_0_ORPHAN_CLEAR_INODE, and remove
+		PR_0_ORPHAN_INODE_INUSE.
+	
+	* journal.c (e2fsck_run_ext3_journal): Add i18n support, and print
+		a message when the journal is being recovered.
+
 	* pass1.c (e2fsck_pass1): Don't check the i_mode field for the
 		journal inode, if it is in use.
 
diff --git a/e2fsck/journal.c b/e2fsck/journal.c
index c9f67d0..8e318b0 100644
--- a/e2fsck/journal.c
+++ b/e2fsck/journal.c
@@ -594,9 +594,10 @@
 	io_manager io_ptr = ctx->fs->io->manager;
 	int blocksize = ctx->fs->blocksize;
 	errcode_t	retval, recover_retval;
-	
+
+	printf(_("%s: recovering journal\n"), ctx->device_name);
 	if (ctx->options & E2F_OPT_READONLY) {
-		printf("%s: won't do journal recovery while read-only\n",
+		printf(_("%s: won't do journal recovery while read-only\n"),
 		       ctx->device_name);
 		return EXT2_ET_FILE_RO;
 	}
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index bbfbb25..df87beb 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -230,8 +230,8 @@
 	  PROMPT_CLEAR, PR_PREEN_OK|PR_PREEN_NOMSG },
 
 	/* Clearing orphan inode */
-	{ PR_0_CLEAR_ORPHAN_INODE,
-	  N_("Clearing @o @i %i (uid=%Iu, gid=%Ig, mode=%Im, size=%Is)\n"),
+	{ PR_0_ORPHAN_CLEAR_INODE,
+	  N_("%s @o @i %i (uid=%Iu, gid=%Ig, mode=%Im, size=%Is)\n"),
 	  PROMPT_NONE, 0 },
 
 	/* Illegal block found in orphaned inode */
@@ -254,11 +254,6 @@
 	  N_("@I @i %i in @o @i list.\n"),
 	  PROMPT_NONE, 0 },
 
-	/* Orphan inode has a non-zero link count */
-	{ PR_0_ORPHAN_INODE_INUSE,
-	  N_("@o @i %i has a non-zero link count.\n"),
-	  PROMPT_NONE, 0 },
-
 	/* Pass 1 errors */
 	
 	/* Pass 1: Checking inodes, blocks, and sizes */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 60ca1d2..b432e64 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -128,7 +128,7 @@
 #define PR_0_JOURNAL_RESET_PROMPT 0x000019
 
 /* Clearing orphan inode */
-#define PR_0_CLEAR_ORPHAN_INODE			0x000020
+#define PR_0_ORPHAN_CLEAR_INODE			0x000020
 	
 /* Illegal block found in orphaned inode */
 #define PR_0_ORPHAN_ILLEGAL_BLOCK_NUM		0x000021
@@ -142,9 +142,6 @@
 /* Illegal inode in orphaned inode list */
 #define PR_0_ORPHAN_ILLEGAL_INODE 		0x000024
 
-/* Orphan inode has a non-zero link count */
-#define PR_0_ORPHAN_INODE_INUSE			0x000025
-
 /*
  * Pass 1 errors
  */
diff --git a/e2fsck/super.c b/e2fsck/super.c
index 7875dc1..05d898f 100644
--- a/e2fsck/super.c
+++ b/e2fsck/super.c
@@ -55,10 +55,15 @@
  * helper function to release an inode
  */
 struct process_block_struct {
-	ino_t	ino;
-	e2fsck_t ctx;
+	e2fsck_t 	ctx;
+	char 		*buf;
 	struct problem_context *pctx;
-	int	abort;
+	int		truncating;
+	int		truncate_offset;
+	blk_t		truncate_block;
+	int		truncated_blocks;
+	int		abort;
+	errcode_t	errcode;
 };
 
 static int release_inode_block(ext2_filsys fs,
@@ -67,9 +72,10 @@
 			       void *priv_data)
 {
 	struct process_block_struct *pb;
-	e2fsck_t ctx;
-	struct problem_context *pctx;
-	blk_t	blk = *block_nr;
+	e2fsck_t 		ctx;
+	struct problem_context	*pctx;
+	blk_t			blk = *block_nr;
+	int			retval = 0;
 
 	pb = (struct process_block_struct *) priv_data;
 	ctx = pb->ctx;
@@ -84,42 +90,110 @@
 	if ((blk < fs->super->s_first_data_block) ||
 	    (blk >= fs->super->s_blocks_count)) {
 		fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_BLOCK_NUM, pctx);
+	abort:
 		pb->abort = 1;
 		return BLOCK_ABORT;
 	}
 
 	if (!ext2fs_test_block_bitmap(fs->block_map, blk)) {
 		fix_problem(ctx, PR_0_ORPHAN_ALREADY_CLEARED_BLOCK, pctx);
-		pb->abort = 1;
-		return BLOCK_ABORT;
+		goto abort;
+	}
+
+	/*
+	 * If we are deleting an orphan, then we leave the fields alone.
+	 * If we are truncating an orphan, then update the inode fields
+	 * and clean up any partial block data.
+	 */
+	if (pb->truncating) {
+		/*
+		 * We only remove indirect blocks if they are
+		 * completely empty.
+		 */
+		if (blockcnt < 0) {
+			int	i, limit;
+			blk_t	*block_nr;
+			
+			pb->errcode = io_channel_read_blk(fs->io, blk, 1,
+							pb->buf);
+			if (pb->errcode)
+				goto abort;
+
+			limit = fs->blocksize >> 2;
+			for (i = 0, block_nr = (blk_t *) pb->buf;
+			     i < limit;	 i++, block_nr++)
+				if (*block_nr)
+					return 0;
+		}
+		/*
+		 * We don't remove direct blocks until we've reached
+		 * the truncation block.
+		 */
+		if (blockcnt >= 0 && blockcnt < pb->truncate_block)
+			return 0;
+		/*
+		 * If part of the last block needs truncating, we do
+		 * it here.
+		 */
+		if ((blockcnt == pb->truncate_block) && pb->truncate_offset) {
+			pb->errcode = io_channel_read_blk(fs->io, blk, 1,
+							pb->buf);
+			if (pb->errcode)
+				goto abort;
+			memset(pb->buf + pb->truncate_offset, 0,
+			       fs->blocksize - pb->truncate_offset);
+			pb->errcode = io_channel_write_blk(fs->io, blk, 1,
+							 pb->buf);
+			if (pb->errcode)
+				goto abort;
+		}
+		pb->truncated_blocks++;
+		*block_nr = 0;
+		retval |= BLOCK_CHANGED;
 	}
 	
 	ext2fs_unmark_block_bitmap(fs->block_map, blk);
 	fs->group_desc[ext2fs_group_of_blk(fs, blk)].bg_free_blocks_count++;
 	fs->super->s_free_blocks_count++;
 	
-	return 0;
+	return retval;
 }
 		
 /*
  * This function releases an inode.  Returns 1 if an inconsistency was
- * found.
+ * found.  If the inode has a link count, then it is being truncated and
+ * not deleted.
  */
-static int release_inode_blocks(e2fsck_t ctx, ino_t ino, char* block_buf,
+static int release_inode_blocks(e2fsck_t ctx, ino_t ino,
+				struct ext2_inode *inode, char* block_buf,
 				struct problem_context *pctx)
 {
-	ext2_filsys fs = ctx->fs;
+	ext2_filsys			fs = ctx->fs;
 	errcode_t			retval;
 	struct process_block_struct 	pb;
 
-	pb.ino = ino;
+	pb.buf = block_buf + 3 * ctx->fs->blocksize;
 	pb.ctx = ctx;
 	pb.abort = 0;
+	pb.errcode = 0;
 	pb.pctx = pctx;
-	retval = ext2fs_block_iterate(fs, ino, 0, block_buf,
-				      release_inode_block, &pb);
+	if (inode->i_links_count) {
+		pb.truncating = 1;
+		pb.truncate_block = (blk_t)
+			((((long long)inode->i_size_high << 32) +
+			  inode->i_size + fs->blocksize - 1) /
+			 fs->blocksize);
+		pb.truncate_offset = inode->i_size % fs->blocksize;
+	} else {
+		pb.truncating = 0;
+		pb.truncate_block = 0;
+		pb.truncate_offset = 0;
+	}
+	pb.truncated_blocks = 0;
+	retval = ext2fs_block_iterate(fs, ino, BLOCK_FLAG_DEPTH_TRAVERSE, 
+				      block_buf, release_inode_block, &pb);
 	if (retval) {
-		com_err("delete_file", retval,
+		com_err("release_inode_blocks", retval,
 			_("while calling ext2fs_block_iterate for inode %d"),
 			ino);
 		return 1;
@@ -127,6 +201,13 @@
 	if (pb.abort)
 		return 1;
 
+	/* Refresh the inode since ext2fs_block_iterate may have changed it */
+	e2fsck_read_inode(ctx, ino, inode, "release_inode_blocks");
+
+	if (pb.truncated_blocks)
+		inode->i_blocks -= pb.truncated_blocks *
+			(fs->blocksize / 512);
+
 	ext2fs_mark_bb_dirty(fs);
 	return 0;
 }
@@ -162,22 +243,19 @@
 		return 1;
 	}
 
-	block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 3,
+	block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
 						    "block interate buffer");
 	e2fsck_read_bitmaps(ctx);
 	
 	while (ino) {
-		e2fsck_read_inode(ctx, ino, &inode, "delete_file");
+		e2fsck_read_inode(ctx, ino, &inode, "release_orphan_inodes");
 		clear_problem_context(&pctx);
 		pctx.ino = ino;
 		pctx.inode = &inode;
+		pctx.str = inode.i_links_count ? "Truncating" : "Clearing";
 
-		fix_problem(ctx, PR_0_CLEAR_ORPHAN_INODE, &pctx);
+		fix_problem(ctx, PR_0_ORPHAN_CLEAR_INODE, &pctx);
 
-		if (inode.i_links_count) {
-			fix_problem(ctx, PR_0_ORPHAN_INODE_INUSE, &pctx);
-			goto abort;
-		}
 		next_ino = inode.i_dtime;
 		if (next_ino &&
 		    ((next_ino < EXT2_FIRST_INODE(fs->super)) ||
@@ -187,20 +265,21 @@
 			goto abort;
 		}
 
-		if (release_inode_blocks(ctx, ino, block_buf, &pctx))
+		if (release_inode_blocks(ctx, ino, &inode, block_buf, &pctx))
 			goto abort;
-		
-		inode.i_dtime = time(0);
-		e2fsck_write_inode(ctx, ino, &inode, "delete_file");
 
-		ext2fs_unmark_inode_bitmap(fs->inode_map, ino);
-		ext2fs_mark_ib_dirty(fs);
-		group = ext2fs_group_of_ino(fs, ino);
-		fs->group_desc[group].bg_free_inodes_count++;
-		fs->super->s_free_inodes_count++;
-		if (LINUX_S_ISDIR(inode.i_mode))
-			fs->group_desc[group].bg_used_dirs_count--;
-		
+		if (!inode.i_links_count) {
+			ext2fs_unmark_inode_bitmap(fs->inode_map, ino);
+			ext2fs_mark_ib_dirty(fs);
+			group = ext2fs_group_of_ino(fs, ino);
+			fs->group_desc[group].bg_free_inodes_count++;
+			fs->super->s_free_inodes_count++;
+			if (LINUX_S_ISDIR(inode.i_mode))
+				fs->group_desc[group].bg_used_dirs_count--;
+			
+			inode.i_dtime = time(0);
+		}
+		e2fsck_write_inode(ctx, ino, &inode, "delete_file");
 		ino = next_ino;
 	}
 	return 0;