2013-03-02 2 views
5

Я просто экспериментировал с архивом/tar и сжимал/gzip, для автоматической обработки некоторых резервных копий у меня есть.Почему md5-хэш tar-части tar.gz через TeeReader ошибочен?

Моя проблема заключается в следующем: у меня есть разные файлы .tar и файлы .tar.gz, и я хочу извлечь хэш (md5) файла .tar.gz и хеш (md5) файл .tar, в идеале, за один проход.

Пример кода, который я до сих пор работал отлично для хэшей файлов в .tar.gz, а также для .gz, но хэш для .tar ошибочен, и я не могу узнать в чем проблема.

Я посмотрел файл tar/reader.go, и я увидел, что там есть пропуски, но я думал, что все должно работать над интерфейсом io.Reader и, следовательно, TeeReader все равно должен поймать все байты.

package main 

import (
    "archive/tar" 
    "compress/gzip" 
    "crypto/md5" 
    "fmt" 
    "io" 
    "os" 
) 

func main() { 
    tgz, _ := os.Open("tb.tar.gz") 
    gzMd5 := md5.New() 
    gz, _ := gzip.NewReader(io.TeeReader(tgz, gzMd5)) 
    tarMd5 := md5.New() 
    tr := tar.NewReader(io.TeeReader(gz, tarMd5)) 
    for { 
     fileMd5 := md5.New() 
     hdr, err := tr.Next() 
     if err == io.EOF { 
      break 
     } 
     io.Copy(fileMd5, tr) 
     fmt.Printf("%x %s\n", fileMd5.Sum(nil), hdr.Name) 
    } 
    fmt.Printf("%x tb.tar\n", tarMd5.Sum(nil)) 
    fmt.Printf("%x tb.tar.gz\n", gzMd5.Sum(nil)) 
} 

Теперь в следующем примере:

$ echo "a" > a.txt 
$ echo "b" > b.txt 
$ tar cf tb.tar a.txt b.txt 
$ gzip -c tb.tar > tb.tar.gz 
$ md5sum a.txt b.txt tb.tar tb.tar.gz 

60b725f10c9c85c70d97880dfe8191b3 a.txt 
3b5d5c3712955042212316173ccf37be b.txt 
501352dcd8fbd0b8e3e887f7dafd9392 tb.tar 
90d6ba204493d8e54d3b3b155bb7f370 tb.tar.gz 

на Linux Mint 14 (на основе Ubuntu 12.04) с перейти 1.02 из Ubuntu репозиториев результат для моей идут программы:

$ go run tarmd5.go 
60b725f10c9c85c70d97880dfe8191b3 a.txt 
3b5d5c3712955042212316173ccf37be b.txt 
a26ddab1c324780ccb5199ef4dc38691 tb.tar 
90d6ba204493d8e54d3b3b155bb7f370 tb.tar.gz 

Итак, все хеши, за исключением tb.tar, как и ожидалось. (Конечно, если вы повторите этот пример, ваш .tar и .tar.gz будут отличаться от этого, из-за разных временных меток)

Любой намек о том, как получить его работу, будет очень благодарен, я действительно предпочел бы пусть он за 1 ход (с TeeReaders).

ответ

5

Проблема возникает из-за того, что tar не читает каждый байт от вашего читателя. После хэширования каждого файла вам необходимо очистить читатель, чтобы каждый байт читался и хэшировался. Как я обычно это делаю, используйте io.Copy() для чтения до EOF.

package main 

import (
    "archive/tar" 
    "compress/gzip" 
    "crypto/md5" 
    "fmt" 
    "io" 
    "io/ioutil" 
    "os" 
) 

func main() { 
    tgz, _ := os.Open("tb.tar.gz") 
    gzMd5 := md5.New() 
    gz, _ := gzip.NewReader(io.TeeReader(tgz, gzMd5)) 
    tarMd5 := md5.New() 
    tee := io.TeeReader(gz, tarMd5) // need the reader later 
    tr := tar.NewReader(tee) 
    for { 
     fileMd5 := md5.New() 
     hdr, err := tr.Next() 
     if err == io.EOF { 
      break 
     } 
     io.Copy(fileMd5, tr) 
     fmt.Printf("%x %s\n", fileMd5.Sum(nil), hdr.Name) 
    } 
    io.Copy(ioutil.Discard, tee) // read unused portions of the tar file 
    fmt.Printf("%x tb.tar\n", tarMd5.Sum(nil)) 
    fmt.Printf("%x tb.tar.gz\n", gzMd5.Sum(nil)) 
} 

Другим вариантом является просто добавить io.Copy(tarMd5, gz) перед вашим tarMd5.Sum() вызова. Я думаю, что первый путь более ясен, даже если мне нужно было добавить/изменить четыре строки вместо одной.

Смежные вопросы