2013-03-02 20 views
5

Stavo solo sperimentando con archive/tar e compress/gzip, per l'elaborazione automatizzata di alcuni backup che ho.Perché l'hash md5 della parte tar di un tar.gz tramite TeeReader è errato?

Il mio problema è: ho vari file .tar e file .tar.gz in giro, e quindi voglio estrarre l'hash (md5) del file .tar.gz e l'hash (md5) di anche il file .tar, idealmente in una corsa.

Il codice di esempio che ho finora funziona perfettamente per gli hash dei file in .tar.gz anche per .gz, ma l'hash per .tar è sbagliato e non riesco a scoprirlo qual è il problema

Ho guardato il file tar/reader.go e ho visto che ci sono alcuni salti, ma ho pensato che tutto dovrebbe essere eseguito sull'interfaccia io.Reader e quindi il TeeReader dovrebbe comunque catturare tutti i byte.

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)) 
} 

Ora per il seguente esempio:

$ 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 

Su Linux Mint 14 (basata su Ubuntu 12.04) con il passare 1.02 dal Ubuntu repository il risultato per il mio programma go è:

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

Quindi tutti gli hash tranne tb.tar sono come previsto. (Naturalmente se riprovi questo esempio, il tuo .tar e .tar.gz sarà diverso da questo, a causa di diversi timestamp)

Qualsiasi suggerimento su come farlo funzionare sarebbe molto apprezzato, io preferirei davvero averlo in 1 run però (con i TeeReader).

risposta

5

Il problema si verifica perché tar non legge ogni byte dal lettore. Dopo aver eseguito l'hashing di ciascun file, è necessario svuotare il lettore per verificare che ogni byte sia letto e sottoposto a hashing. Il modo in cui lo faccio normalmente è io.Copy() da leggere fino a 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)) 
} 

Un'altra opzione è quella di aggiungere solo io.Copy(tarMd5, gz) prima della chiamata tarMd5.Sum(). Penso che il primo modo sia più chiaro anche se dovessi aggiungere/modificare quattro righe anziché una.