Description: Dump reads uninitialized blocks from ext4 filesystems.
 Dump includes data read from EXT2_EXTENT_FLAGS_UNINIT extents in the
 dump instead of writing zeros (or nothing) to the tape.
 This means that restores are corrupted.
Author: Tim Woodall <debianbugs@woodall.me.uk>
Bug: http://bugs.debian.org/1081009

--- a/common/slave.h
+++ b/common/slave.h
@@ -17,6 +17,7 @@
 struct req {
 	ext2_loff_t dblk;
 	int count;
+	int uninit;
 };
 
 #define SLAVES 3		/* 1 slave writing, 1 reading, 1 for slack */
--- a/dump/dump.h
+++ b/dump/dump.h
@@ -140,7 +140,6 @@ int	maponefile (dump_ino_t maxino, long
 int	mapdirs (dump_ino_t maxino, long long *tapesize);
 
 /* file dumping routines */
-void	blksout (blk_t *blkp, int frags, dump_ino_t ino);
 void	bread (ext2_loff_t blkno, char *buf, int size);
 void	dumpino (struct dinode *dp, dump_ino_t ino, int metaonly);
 #ifdef	__linux__
@@ -153,7 +152,7 @@ void	mkchecksum (union u_spcl *tmpspcl);
 /* tape writing routines */
 int	alloctape (void);
 void	close_rewind (void);
-void	dumpblock (blk_t blkno, int size);
+void	dumpblock (blk64_t blkno, int size, int uninit);
 void	startnewtape (int top);
 time_t	trewind (void);
 void	writerec (const void *dp, int isspcl);
--- a/dump/tape.c
+++ b/dump/tape.c
@@ -238,6 +238,7 @@ writerec(const void *dp, int isspcl)
 {
 	slp->req[trecno].dblk = (ext2_loff_t)0;
 	slp->req[trecno].count = 1;
+	slp->req[trecno].uninit = 0;
 	/* XXX post increment triggers an egcs-1.1.2-12 bug on alpha/sparc */
 	*(union u_spcl *)(*(nextblock)) = *(union u_spcl *)dp;
 
@@ -254,7 +255,7 @@ writerec(const void *dp, int isspcl)
 }
 
 void
-dumpblock(blk_t blkno, int size)
+dumpblock(blk64_t blkno, int size, int uninit)
 {
 	int avail, tpblks;
 	ext2_loff_t dblkno;
@@ -264,6 +265,7 @@ dumpblock(blk_t blkno, int size)
 	while ((avail = MIN(tpblks, ntrec - trecno)) > 0) {
 		slp->req[trecno].dblk = dblkno;
 		slp->req[trecno].count = avail;
+		slp->req[trecno].uninit = uninit;
 		trecno += avail;
 		spcl.c_tapea += avail;
 		if (trecno >= ntrec)
@@ -1220,8 +1222,12 @@ doslave(int cmd,
 		     trecno += p->count, p += p->count) {
 
 			if (p->dblk) {	/* read a disk block */
-				bread(p->dblk, slp->tblock[trecno],
-					p->count * TP_BSIZE);
+				if(p->uninit) {
+					memset(slp->tblock[trecno], 0, p->count * TP_BSIZE);
+				} else {
+					bread(p->dblk, slp->tblock[trecno],
+						p->count * TP_BSIZE);
+				}
 			} else {	/* read record from pipe */
 				if (p->count != 1 || dump_atomic_read( cmd,
 				    (char *)slp->tblock[trecno],
--- a/dump/traverse.c
+++ b/dump/traverse.c
@@ -746,14 +746,23 @@ searchdir(dump_ino_t ino, daddr_t blkno,
 
 #ifdef	__linux__
 
+struct block_info {
+	blk64_t	blocknr;
+	int	uninit;
+};
+
+
 struct block_context {
 	ext2_ino_t ino;
-	blk_t	*buf;
+	struct block_info	*buf;
 	int	cnt;
 	int	max;
 	int	next_block;
+	int	uninit;
 };
 
+static void blksout (struct block_info *blkp, int frags, dump_ino_t ino);
+
 /*
  * Dump a block to the tape
  */
@@ -766,14 +775,16 @@ dumponeblock(UNUSED(ext2_filsys fs), blk
 
 	p = (struct block_context *)private;
 	for (i = p->next_block; i < blockcnt; i++) {
-		p->buf[p->cnt++] = 0;
+		p->buf[p->cnt].blocknr = 0;
+    p->buf[p->cnt++].uninit = 0;
 		if (p->cnt == p->max) {
 			blksout (p->buf, p->cnt, p->ino);
 			p->cnt = 0;
 		}
 	}
-	p->buf[p->cnt++] = *blocknr;
-	if (p->cnt == p->max) {
+	p->buf[p->cnt].blocknr = *blocknr;
+	p->buf[p->cnt++].uninit = p->uninit;
+  if (p->cnt == p->max) {
 		blksout (p->buf, p->cnt, p->ino);
 		p->cnt = 0;
 	}
@@ -840,12 +851,60 @@ dump_xattr(dump_ino_t ino, struct dinode
 		spcl.c_dinode.di_size = sblock->fs_bsize;
 		spcl.c_flags |= DR_EXTATTRIBUTES;
 		spcl.c_extattributes = EXT_XATTR;
-		blksout(&dp->di_file_acl, EXT2_FRAGS_PER_BLOCK(fs->super), ino);
+    struct block_info acl;
+		acl.blocknr = dp->di_file_acl | ((long long)dp->osd2.linux2.l_i_file_acl_high<<32);
+		acl.uninit = 0;
+		blksout(&acl, EXT2_FRAGS_PER_BLOCK(fs->super), ino);
 		spcl.c_flags &= ~DR_EXTATTRIBUTES;
 		spcl.c_extattributes = 0;
 	}
 }
 
+#ifdef __linux__
+/* debian bug #1081009: handle extents with EXT2_EXTENT_FLAGS_UNINIT better */
+/* TODO - see bread for how dump deals with errors */
+static void iterate_extents(struct block_context* bc, ext2_filsys fs, ext2_ino_t ino) {
+	ext2_extent_handle_t handle = NULL;
+	struct ext2fs_extent extent;
+	int op = EXT2_EXTENT_ROOT;
+	errcode_t errcode;
+	int ret = 0;
+
+	errcode = ext2fs_extent_open(fs, ino, &handle);
+	if (errcode) {
+		msg("failed to open ext2 inode %u: %s\n", ino, error_message(errcode));
+		return;
+	}
+	while (1) {
+
+		errcode = ext2fs_extent_get(handle, op, &extent);
+		if (errcode == EXT2_ET_EXTENT_NO_NEXT)
+			break;
+		if (errcode) {
+			msg("failed to get extents inode %u: %s\n", ino, error_message(errcode));
+			goto out;
+		}
+		op = EXT2_EXTENT_NEXT;
+
+		if (extent.e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT) {
+			continue;
+		}
+		if (!(extent.e_flags & EXT2_EXTENT_FLAGS_LEAF)) {
+			continue;
+		}
+
+		bc->uninit = extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT ? 1 : 0;
+		blk64_t start = extent.e_pblk;
+		e2_blkcnt_t blockcnt = extent.e_lblk;
+		for(blk64_t blocknr = start; blocknr < start + extent.e_len; ++blocknr, ++blockcnt) {
+			dumponeblock(fs, &blocknr, blockcnt, 0, 0, bc);
+		}
+	}
+out:
+	ext2fs_extent_free(handle);
+}
+#endif
+
 /*
  * Dump passes 3 and 4.
  *
@@ -978,25 +1037,34 @@ dumpino(struct dinode *dp, dump_ino_t in
 	}
 #ifdef	__linux__
 	bc.max = NINDIR(sblock) * EXT2_FRAGS_PER_BLOCK(fs->super);
-	bc.buf = malloc (bc.max * sizeof (int));
+	bc.buf = malloc (bc.max * sizeof (*bc.buf));
 	bc.cnt = 0;
 	bc.ino = ino;
 	bc.next_block = 0;
+  bc.uninit = 0;
 
-	ext2fs_block_iterate2(fs, (ext2_ino_t)ino, BLOCK_FLAG_DATA_ONLY, NULL, dumponeblock, (void *)&bc);
+	if (dp->di_mode & S_IFREG && dp->di_flags & EXT4_EXTENTS_FL) {
+		iterate_extents(&bc, fs, (ext2_ino_t)ino);
+	} else if (dp->di_mode & S_IFREG && dp->di_flags & EXT4_INLINE_DATA_FL) {
+		/* Untested - Can we ever get here? */
+		msg("Unexpectedly got EXT4_INLINE_DATA_FL while dumping inode %lu\n", ino);
+	} else {
+		ext2fs_block_iterate3(fs, (ext2_ino_t)ino, BLOCK_FLAG_DATA_ONLY, NULL, dumponeblock, (void *)&bc);
+	}
 	/* deal with holes at the end of the inode */
 	if (i_size > ((uint64_t)bc.next_block) * sblock->fs_fsize) {
 		remaining = i_size - ((uint64_t)bc.next_block) * sblock->fs_fsize;
 		for (i = 0; i < (int)howmany(remaining, sblock->fs_fsize); i++) {
-			bc.buf[bc.cnt++] = 0;
+			bc.buf[bc.cnt].blocknr = 0;
+			bc.buf[bc.cnt++].uninit = 0;
 			if (bc.cnt == bc.max) {
-				blksout (bc.buf, bc.cnt, bc.ino);
+         blksout (bc.buf, bc.cnt, bc.ino);
 				bc.cnt = 0;
 			}
 		}
 	}
 	if (bc.cnt > 0) {
-		blksout (bc.buf, bc.cnt, bc.ino);
+     blksout (bc.buf, bc.cnt, bc.ino);
 	}
 	free(bc.buf);
 	dump_xattr(ino, dp);
@@ -1005,8 +1073,11 @@ dumpino(struct dinode *dp, dump_ino_t in
 		cnt = NDADDR * sblock->fs_frag;
 	else
 		cnt = howmany(i_size, sblock->fs_fsize);
-	blksout(&dp->di_db[0], cnt, ino);
-	if ((int64_t) (size = i_size - NDADDR * sblock->fs_bsize) <= 0) {
+	struct block_info bi;
+	bi.blocknr = dp->di_db[0];
+	bi.uninit = 0;
+	blksout(&bi, cnt, ino);
+  if ((int64_t) (size = i_size - NDADDR * sblock->fs_bsize) <= 0) {
 		dump_xattr(ino, dp);
 		return;
 	}
@@ -1245,7 +1316,10 @@ dmpindir(dump_ino_t ino, daddr_t blk, in
 			cnt = NINDIR(sblock) * sblock->fs_frag;
 #endif
 		*size -= NINDIR(sblock) * sblock->fs_bsize;
-		blksout(&idblk[0], cnt, ino);
+		struct block_info bi;
+		bi.blocknr = idblk[0];
+		bi.uninit = 0;
+		blksout(&bi, cnt, ino);
 		return;
 	}
 	ind_level--;
@@ -1261,9 +1335,9 @@ dmpindir(dump_ino_t ino, daddr_t blk, in
  * Collect up the data into tape record sized buffers and output them.
  */
 void
-blksout(blk_t *blkp, int frags, dump_ino_t ino)
+blksout(struct block_info *blkp, int frags, dump_ino_t ino)
 {
-	blk_t *bp;
+  struct block_info *bp;
 	int i, j, count, blks, tbperdb;
 
 	blks = howmany(frags * sblock->fs_fsize, TP_BSIZE);
@@ -1274,7 +1348,7 @@ blksout(blk_t *blkp, int frags, dump_ino
 		else
 			count = i + TP_NINDIR;
 		for (j = i; j < count; j++)
-			if (blkp[j / tbperdb] != 0)
+			if (blkp[j / tbperdb].blocknr != 0)
 				spcl.c_addr[j - i] = 1;
 			else
 				spcl.c_addr[j - i] = 0;
@@ -1282,11 +1356,11 @@ blksout(blk_t *blkp, int frags, dump_ino
 		writeheader(ino);
 		bp = &blkp[i / tbperdb];
 		for (j = i; j < count; j += tbperdb, bp++) {
-			if (*bp != 0) {
+			if (bp->blocknr != 0) {
 				if (j + tbperdb <= count)
-					dumpblock(*bp, (int)sblock->fs_bsize);
+					dumpblock(bp->blocknr, (int)sblock->fs_bsize, bp->uninit);
 				else
-					dumpblock(*bp, (count - j) * TP_BSIZE);
+					dumpblock(bp->blocknr, (count - j) * TP_BSIZE, bp->uninit);
 			}
 		}
 		spcl.c_type = TS_ADDR;
