Package: dump
Version: 0.4b47-4+~tjw12r5
Severity: normal

Dear Maintainer,

Dump cannot handle more than 2^32 blocks. This was reported upstream
some years ago along with a patch that I am including here.

I'm reporting this as I have another ext4 patch that I've built on top
of this one.

I've also attached a testcase that doesn't require >12TB of data to
reproduce (it needs about 3GB of available disk space)

It's crafted to my setup and will require modification for anybody else
to use.

-- System Information:
Debian Release: 12.7
  APT prefers stable-security
  APT policy: (500, 'stable-security'), (500, 'stable')
Architecture: amd64 (x86_64)

Kernel: Linux 6.1.0-25-amd64 (SMP w/4 CPU threads; PREEMPT)
Kernel taint flags: TAINT_WARN
Locale: LANG=en_GB.UTF-8, LC_CTYPE=en_GB.UTF-8 (charmap=UTF-8), LANGUAGE not set
Shell: /bin/sh linked to /usr/bin/dash
Init: sysvinit (via /sbin/init)

Versions of packages dump depends on:
ii  libblkid1     2.38.1-5+deb12u1
ii  libbz2-1.0    1.0.8-5+b1
ii  libc6         2.36-9+deb12u8
ii  libcom-err2   1.47.0-2
ii  libext2fs2    1.47.0-2
ii  liblzo2-2     2.10-2
ii  libreadline8  8.2-1.3
ii  libselinux1   3.4-1+b6
ii  tar           1.34+dfsg-1.2+deb12u1
ii  zlib1g        1:1.2.13.dfsg-1

dump recommends no packages.

dump suggests no packages.

-- no debconf information
diff -urN dump-0.4b47.orig/dump/dump.h dump-0.4b47/dump/dump.h
--- dump-0.4b47.orig/dump/dump.h        2022-05-03 10:02:27.000000000 +0000
+++ dump-0.4b47/dump/dump.h     2022-05-03 10:02:27.000000000 +0000
@@ -140,8 +140,8 @@
 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   blksout (blk64_t *blkp, int frags, dump_ino_t ino);
+void   bread (ext2_loff_t blkno, char *buf, size_t size);
 void   dumpino (struct dinode *dp, dump_ino_t ino, int metaonly);
 #ifdef __linux__
 void   dumpdirino (struct dinode *dp, dump_ino_t ino);
@@ -153,7 +153,7 @@
 /* tape writing routines */
 int    alloctape (void);
 void   close_rewind (void);
-void   dumpblock (blk_t blkno, int size);
+void   dumpblock (blk64_t blkno, int size);
 void   startnewtape (int top);
 time_t trewind (void);
 void   writerec (const void *dp, int isspcl);
diff -urN dump-0.4b47.orig/dump/tape.c dump-0.4b47/dump/tape.c
--- dump-0.4b47.orig/dump/tape.c        2022-05-03 10:02:27.000000000 +0000
+++ dump-0.4b47/dump/tape.c     2022-05-03 10:02:27.000000000 +0000
@@ -254,7 +254,7 @@
 }
 
 void
-dumpblock(blk_t blkno, int size)
+dumpblock(blk64_t blkno, int size)
 {
        int avail, tpblks;
        ext2_loff_t dblkno;
diff -urN dump-0.4b47.orig/dump/traverse.c dump-0.4b47/dump/traverse.c
--- dump-0.4b47.orig/dump/traverse.c    2021-01-01 16:28:57.000000000 +0000
+++ dump-0.4b47/dump/traverse.c 2021-01-01 16:28:57.000000000 +0000
@@ -748,7 +748,7 @@
 
 struct block_context {
        ext2_ino_t ino;
-       blk_t   *buf;
+       blk64_t *buf;
        int     cnt;
        int     max;
        int     next_block;
@@ -758,8 +758,8 @@
  * Dump a block to the tape
  */
 static int
-dumponeblock(UNUSED(ext2_filsys fs), blk_t *blocknr, e2_blkcnt_t blockcnt,
-            UNUSED(blk_t ref_block), UNUSED(int ref_offset), void * private)
+dumponeblock(UNUSED(ext2_filsys fs), blk64_t *blocknr, e2_blkcnt_t blockcnt,
+            UNUSED(blk64_t ref_block), UNUSED(int ref_offset), void * private)
 {
        struct block_context *p;
        e2_blkcnt_t i;
@@ -840,7 +840,7 @@
                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);
+               blksout((blk64_t *)&dp->di_file_acl, 
EXT2_FRAGS_PER_BLOCK(fs->super), ino);
                spcl.c_flags &= ~DR_EXTATTRIBUTES;
                spcl.c_extattributes = 0;
        }
@@ -978,12 +978,12 @@
        }
 #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 (blk64_t));
        bc.cnt = 0;
        bc.ino = ino;
        bc.next_block = 0;
 
-       ext2fs_block_iterate2(fs, (ext2_ino_t)ino, BLOCK_FLAG_DATA_ONLY, NULL, 
dumponeblock, (void *)&bc);
+       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;
@@ -1261,9 +1261,9 @@
  * Collect up the data into tape record sized buffers and output them.
  */
 void
-blksout(blk_t *blkp, int frags, dump_ino_t ino)
+blksout(blk64_t *blkp, int frags, dump_ino_t ino)
 {
-       blk_t *bp;
+       blk64_t *bp;
        int i, j, count, blks, tbperdb;
 
        blks = howmany(frags * sblock->fs_fsize, TP_BSIZE);
@@ -1394,7 +1394,7 @@
 int    breaderrors = 0;
 
 void
-bread(ext2_loff_t blkno, char *buf, int size)
+bread(ext2_loff_t blkno, char *buf, size_t size)
 {
        int cnt, i;
 
#!/bin/bash

set -Eeuo pipefail

homedir=$( readlink -f $( dirname $0 ) )
cd $homedir

rm -f d1
rm -f d2
rm -fr d1.mnt
rm -fr d2.mnt
rm -fr mnt
rm -fr restore

cleanup() {
  umount $homedir/mnt >&/dev/null || true
  vgchange -an vg30T >&/dev/null || true
  [[ -z ${ld3} ]] || losetup -d ${ld3} >&/dev/null || true
  [[ -z ${ld2} ]] || losetup -d ${ld2} >&/dev/null || true
  umount $homedir/d1.mnt >&/dev/null || true
  umount $homedir/d2.mnt >&/dev/null || true
  [[ -z ${ld1} ]] || losetup -d ${ld1} >&/dev/null || true
  [[ -z ${ld0} ]] || losetup -d ${ld0} >&/dev/null || true
  rm -f $homedir/d1
  rm -f $homedir/d2
  rm -fr $homedir/d1.mnt
  rm -fr $homedir/d2.mnt
  rm -fr $homedir/mnt
  rm -fr $homedir/restore
}

trap cleanup EXIT

tfdfr=0
tfdbr=0
tbdfr=0
tbdbr=0
cfdfr=0
cfdbr=0
cbdfr=0
cbdbr=0

mkdir -p mnt

truncate -s 3G d1
ld0=$( losetup -f )

losetup ${ld0} d1

mkfs.ext4 ${ld0} >&/dev/null

mkdir -p d1.mnt

mount ${ld0} d1.mnt

truncate -s 1G d2
ld1=$( losetup -f )
losetup ${ld1} d2
mkfs.ext4 ${ld1} >&/dev/null
mkdir -p d2.mnt
mount ${ld1} d2.mnt

truncate -s 15T d1.mnt/pv
truncate -s 15T d2.mnt/pv

ld2=$( losetup -f )
losetup ${ld2} d1.mnt/pv
ld3=$( losetup -f )
losetup ${ld3} d2.mnt/pv

vgcreate vg30T ${ld2} ${ld3} >&/dev/null

lvcreate -n big -l 7860000 vg30T >&/dev/null

mkfs.ext4 /dev/vg30T/big >&/dev/null

mount /dev/vg30T/big mnt

echo "fallocate bigfile1 - this is slow"
fallocate -l 10T mnt/bigfile1
echo "fallocate bigfile2 - this is slow"
fallocate -l 10T mnt/bigfile2

dd if=/dev/urandom of=mnt/bigblock bs=1K count=10K >&/dev/null

echo "Removing bigfiles - this is slow"
rm mnt/bigfile1 mnt/bigfile2

sync

umount mnt
mount -o ro /dev/vg30T/big mnt

#debugfs -R "stat bigblock" /dev/vg30T/big | cat
#debugfs -R "stat bigblock" /dev/vg30T/big | tail -n 1
#debugfs -R "stat bigblock" /dev/vg30T/big | tail -n 1 | sed 's/.*-//'
extent=$( debugfs -R "stat bigblock" /dev/vg30T/big 2>/dev/null | tail -n 1 | 
sed 's/.*-//' )

if [[ $extent -le 4294967295 ]]; then
  echo "bigblock does not extend to an extent >= 2^32. Test needs fixing"
  exit 1
fi

test_pipe() {
  $1 -q -0 $3 -f - 2>/dev/null | $2 -C -D mnt/ -f - >&/dev/null
}

test_file() {
  rm -fr restore

  mkdir -p restore
  cd restore
  $1 -q -0 $3 -f - 2>/dev/null | $2 -r -f - >&/dev/null || return 1
  rm restoresymtable
  cd ..

  diff -q -ur --no-dereference mnt/ restore/ >&/dev/null || return 1
}

dumpdev=/dev/vg30T/big

echo
echo "Testing fixed dump, fixed restore compare"
test_pipe dump restore $dumpdev || cfdfr=1

echo
echo "Testing fixed dump, bookworm restore compare"
test_pipe dump restore.orig $dumpdev || cfdbr=1

echo
echo "Testing bookworm dump, fixed restore compare"
test_pipe dump.orig restore $dumpdev || cbdfr=1

echo
echo "Testing bookworm dump, bookworm restore compare"
test_pipe dump.orig restore.orig $dumpdev || cbdbr=1
echo

echo
echo "Testing fixed dump, fixed restore"
test_file dump restore $dumpdev || tfdfr=1

echo
echo "Testing fixed dump, bookworm restore"
test_file dump restore.orig $dumpdev || tfdbr=1

echo
echo "Testing bookworm dump, fixed restore"
test_file dump.orig restore $dumpdev || tbdfr=1

echo
echo "Testing bookworm dump, bookworm restore"
test_file dump.orig restore.orig $dumpdev || tbdbr=1
echo

umount mnt >& /dev/null
vgchange -an vg30T >& /dev/null
losetup -d ${ld3}
losetup -d ${ld2}
umount d1.mnt
umount d2.mnt
losetup -d ${ld1}
losetup -d ${ld0}
rmdir mnt
rmdir d1.mnt
rmdir d2.mnt
rm -f d1
rm -f d2
rm -fr restore

echo
echo
echo

[[ $tfdfr -eq $cfdfr ]] || echo "SERIOUS BUG --- Mismatch between restore -C 
and actual restore for fixed dump and fixed restore" >&2
[[ $tfdfr -eq 0 ]] || echo "SERIOUS BUG --- mismatch between fixed dump and 
fixed restore" >&2

[[ $tbdbr -eq $cbdbr ]] || echo "Bookworm BUG -- Mismatch between restore -C 
and actual restore for bookworm dump and bookworm restore" >&2
[[ $tbdbr -eq 0 ]] || echo "Bookworm BUG -- mismatch between bookworm dump and 
bookworm restore" >&2

[[ $tfdbr -eq $cfdbr ]] || echo "Compatibility - Mismatch between restore -C 
and actual restore for fixed dump and bookworm restore" >&2
[[ $tfdbr -eq 0 ]] || echo "Compatibility - Bookworm restore fails to handle 
fixed dump data" >&2

[[ $tbdfr -eq $cbdfr ]] || echo "Compatibility - Mismatch between restore -C 
and actual restore for bookworm dump and fixed restore" >&2
[[ $tbdfr -eq 0 ]] || echo "Compatibility - Bookworm dump generates invalid 
dump data" >&2

Reply via email to