# # demonstrate two bugs in GNU tar. # # make file for GNU make: it creates all necessary test data files # and then runs the bug demonstration. # # brief overview: # we create two files, file1 and file2, and roll them into a # multi-volume tar ball, consisting of two tar files t1-pipe.tar and # t2-pipe.tar. This works still fine. We then extract them again into a # sub-directory, called extract-dir-pipe. This would work fine if we # would just read from the above tar files. But we make it a bit # harder and read the data through a pipe (correctly using the # option --read-full-records). This breaks when we start to read the # second tar file. The file file2 spans both volumes, and it is # reconstructed wrongly. The first part from the first volume is # still fine, but the continuation starts 1024 bytes too early and # therefore includes the header of the second tar volume. The size # of the file is correct, though, so that some data at the end of # the reconstructed file2 is missing, due to the insertion of the # 1024 bytes of header data. # # It does not matter whether the first tar volume is read via pipe # or directly from a file. # # The bug is in the source file src/buffer.c, in the function # flush_read(). There is a handling for multi-volume, and there is a # handling for short reads due to reading from a pipe (obeying the # option --read-full-records). The latter handling jumps to label # short_read. But then the multi-volume handling is skipped, so both # features cannot be used together. # # The second bug occurs when using multiple volumes with small block # sizes (even on ordinary tar files). When writing the second volume, # tar accesses memory outside of its write buffer and crashes. The # same thing would also happen when reading, tar reads from memory # without checking whether it has to fetch more data first. # # author: Jan Bredereke, January 2001 # # which tar to use: TAR=tar #TAR=/bin/tar #TAR=/usr/local/bin/tar # the following lengths are in n * 1024 bytes. TAPE_LENGTH=10 FILE_OVERLAP=3 #FILE1_LENGTH=$(shell echo $$(($(TAPE_LENGTH) - $(FILE_OVERLAP)))) FILE1_LENGTH=7 all: @echo "Run either" @echo " $(MAKE) test-pipe" @echo "or" @echo " $(MAKE) test-blocksize" @echo "To clean up afterwards, run" @echo " $(MAKE) clean" test-pipe: file1 file2 @echo "####Demo for multi-volume / pipe bug:" ls -l file1 file2 @echo "####Creating a multi-volume tar archive:" $(TAR) -c -v --multi-volume --tape-length=$(TAPE_LENGTH) \ --listed-incremental=t.snar \ -f t1-pipe.tar -f t2-pipe.tar ./file1 ./file2 ls -l t?-pipe.tar @echo "####Creating a pipe to read through:" # mkfifo pipe1 mkfifo pipe2 @echo "####Starting to read data into the pipe:" # dd if=t1-pipe.tar bs=1024 count=$(TAPE_LENGTH) of=pipe1 & dd if=t2-pipe.tar bs=4096 count=$(TAPE_LENGTH) of=pipe2 & sleep 2 @echo "####Making the directory to extract into:" mkdir extract-dir-pipe @echo "####Extracting from the multi-volume tar archive:" -cd extract-dir-pipe ; \ $(TAR) -x -v --multi-volume --tape-length=$(TAPE_LENGTH) \ --read-full-records \ -f ../t1-pipe.tar -f ../pipe2 ls -l extract-dir-pipe @echo "####Checking the extracted files for changes:" cmp -s file1 extract-dir-pipe/file1 diff file2 extract-dir-pipe/file2 test-blocksize: file1 file2 @echo "####Demo for multi-volume / block size bug:" ls -l file1 file2 @echo "####Creating a multi-volume tar archive with block size 1:" $(TAR) -c -v --multi-volume --tape-length=$(TAPE_LENGTH) \ --listed-incremental=t.snar --blocking-factor=1 \ --label=FooLabel \ -f t1-block.tar -f t2-block.tar ./file1 ./file2 ls -l t?-block.tar @echo "####Making the directory to extract into:" mkdir extract-dir-block @echo "####Extracting from the multi-volume tar archive:" -cd extract-dir-block ; \ $(TAR) -x -v --multi-volume --blocking-factor=1 \ --label=FooLabel \ -f ../t1-block.tar -f ../t2-block.tar ls -l extract-dir-block @echo "####Checking the extracted files for changes:" cmp -s file1 extract-dir-block/file1 diff file2 extract-dir-block/file2 file1: @echo "####Creating $@ of $(FILE1_LENGTH) * 1024 bytes of zeroes:" dd if=/dev/zero bs=1024 count=$(FILE1_LENGTH) of=$@ file2: @echo "####Creating $@ of size 8192 with a well recognizable pattern:" rm -f $@ for block in " 1" " 2" " 3" " 4" " 5" " 6" " 7" " 8" \ " 9" "10" "11" "12" "13" "14" "15" "16" ; do \ echo "$@ block $${block} bla!bla!bla!bla!bla!bla!bla!bla!bla!bla!bla!bla" >> $@ ; \ for count in 2 3 4 5 6 7 8 ; do \ echo "bla!bla!bla!bla!bla!bla!bla!bla!bla!bla!bla!bla!bla!bla!bla!bla" >> $@ ; \ done ; \ done clean: @echo "####Cleaning away all generated data:" rm -f file1 file2 rm -f t1-pipe.tar t2-pipe.tar t.snar pipe1 pipe2 rm -f t1-block.tar t2-block.tar rm -r -f extract-dir-pipe extract-dir-block