golang/go

archive/tar: eats file data

dvyukov opened this issue · 4 comments

The following program fails with the panic:

package main

import (
    "archive/tar"
    "bytes"
    "fmt"
    "io"
    "io/ioutil"
)

func main() {
    data := []byte("aaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
        "0100640\x000374534\x000011" +
        "610\x0000000000007\x001253" +
        "1145371\x00010572\x0000\x00\x00\x00" +
        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ust" +
        "ar  \x00dvyukov\x00\x00\x00\x00\x00\x00\x00\x00" +
        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00eng" +
        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000000")

    t := tar.NewReader(bytes.NewReader(data))
    var headers []*tar.Header
    var contents [][]byte
    for {
        hdr, err := t.Next()
        if err == io.EOF {
            break
        }
        if err != nil {
            return
        }
        fdata, err := ioutil.ReadAll(t)
        if err != nil {
            return
        }
        hdr1 := *hdr
        headers = append(headers, &hdr1)
        contents = append(contents, fdata)
    }
    buf := new(bytes.Buffer)
    w := tar.NewWriter(buf)
    for i, hdr := range headers {
        err := w.WriteHeader(hdr)
        if err != nil {
            panic(err)
        }
        _, err = w.Write(contents[i])
        if err != nil {
            panic(err)
        }
    }
    err := w.Close()
    if err != nil {
        panic(err)
    }
    t1 := tar.NewReader(buf)
    for i := 0; ; i++ {
        _, err := t1.Next()
        if err == io.EOF {
            break
        }
        if err != nil {
            panic(err)
        }
        fdata, err := ioutil.ReadAll(t)
        if err != nil {
            panic(err)
        }
        if !bytes.Equal(fdata, contents[i]) {
            fmt.Printf("got : %q\n", fdata)
            fmt.Printf("want: %q\n", contents[i])
            panic("data differs")
        }
    }
}
got : ""
want: "0000000"
panic: data differs

That is, file data is lost after packing/unpacking.

go version devel +b0532a9 Mon Jun 8 05:13:15 2015 +0000 linux/amd64

There is a type in this test program. Replace

fdata, err := ioutil.ReadAll(t)

with

fdata, err := ioutil.ReadAll(t1)

and the test passes.

If there is any issue here, it's that the second call to ioutil.ReadAll(t) succeeds and returns 512 bytes. How is that possible? Did the underlying Reader somehow reset?

s/type/typo/

And I was wrong in my initial comment. The second call to ioutil.RealAll(t) returns [], nil as expected at EOF.

There is no bug here.

@dadkins thanks for looking at this! Yeah, it should be s/t/t1/, I've fixed my test. Closing.

@dadkins this allowed me to move forward:
#12434
#12435
#12436
:)