Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix deb/ar.go: Read() may return less bytes. Also fix trailing slashes. #106

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 18 additions & 15 deletions deb/ar.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,16 @@ func (d *Ar) Next() (*ArEntry, error) {
}
}

line := make([]byte, 60)

count, err := d.in.Read(line)
if err != nil {
return nil, err
}
if count == 1 && line[0] == '\n' {
return nil, io.EOF
line, err := ioutil.ReadAll(&io.LimitedReader{R:d.in, N:60})
if len(line) < 60 {
if len(line) == 0 || (len(line) == 1 && line[0] == '\n') {
return nil, io.EOF
} else {
return nil, fmt.Errorf("Caught a short read at the end")
}
}
if count != 60 {
return nil, fmt.Errorf("Caught a short read at the end")
if err != nil && err != io.EOF {
return nil, err
}
entry, err := parseArEntry(line)
if err != nil {
Expand Down Expand Up @@ -160,8 +159,12 @@ func parseArEntry(line []byte) (*ArEntry, error) {
}

entry := ArEntry{
Name: strings.TrimSpace(string(line[0:16])),
FileMode: strings.TrimSpace(string(line[48:58])),
// Found a valid deb packages with trailing slash in ar names.
// According to wikipedia, that is System V extension. -- Ebik.
Name: strings.TrimSuffix(
strings.TrimSpace(string(line[0:16])), "/",
),
FileMode: strings.TrimSpace(string(line[40:48])),
}

for target, value := range map[*int64][]byte{
Expand All @@ -187,12 +190,12 @@ func parseArEntry(line []byte) (*ArEntry, error) {
// Given a brand spank'n new os.File entry, go ahead and make sure it looks
// like an `ar(1)` archive, and not some random file.
func checkAr(reader io.Reader) error {
header := make([]byte, 8)
if _, err := reader.Read(header); err != nil {
header, err := ioutil.ReadAll(&io.LimitedReader{R: reader, N: 8})
if err != nil {
return err
}
if string(header) != "!<arch>\n" {
return fmt.Errorf("Header doesn't look right!")
return fmt.Errorf("Header is invalid for an 'ar' archive.")
}
return nil
}
Expand Down
120 changes: 120 additions & 0 deletions deb/ar_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package deb_test

import (
"io"
"io/ioutil"
"log"
"fmt"
"testing"
"runtime"
"pault.ag/go/debian/deb"
)


/* ar is binary format, exact number of spaces is required here */
var foobar_ar = "!<arch>\n" +
"foo.txt/ 0 0 0 644 7 `\n" +
"FooBar\n\n" +
"bar.txt 1559837289 1000 1000 100644 21 `\n" +
"FooBar\nFooBar\nFooBar\n\n"

type wholeReader struct {
Content []byte
Pos int
}

func (r *wholeReader) Read(p []byte) (n int, err error) {
if r.Pos >= len(r.Content) {
return 0, io.EOF
}
n = copy(p, r.Content[r.Pos:])
r.Pos += n
return n, nil
}

type byteReader struct {
Content []byte
Pos int
}

func (r *byteReader) Read(p []byte) (n int, err error) {
if r.Pos >= len(r.Content) {
return 0, io.EOF
}
if len(p) == 0 {
return 0, nil
}
p[0] = r.Content[r.Pos]
r.Pos += 1
return 1, nil
}

func failCaller2(t *testing.T, msg string, a ...interface{}) {
outmsg := fmt.Sprintf(msg, a...)
_, fileName, fileLine, ok := runtime.Caller(2)
if (ok) {
log.Printf("%s at %s:%d\n", outmsg, fileName, fileLine)
} else {
log.Printf("%s\n", outmsg)
}
t.FailNow()
}

func isok(t *testing.T, err error) {
if err != nil {
failCaller2(t, "Error! Error is not nil! - %s", err)
}
}

func iseof(t *testing.T, err error) {
if err != io.EOF {
failCaller2(t, "Error! Error is not EOF! - %s", err)
}
}

func assert(t *testing.T, expr bool) {
if !expr {
failCaller2(t, "Assertion failed!")
}
}

func testFoobarAr(reader io.Reader, t *testing.T) {
ar, err := deb.LoadAr(reader)
isok(t, err)

foo, err := ar.Next()
isok(t, err)
assert(t, foo.Name == "foo.txt")
assert(t, foo.Timestamp == 0)
assert(t, foo.OwnerID == 0)
assert(t, foo.GroupID == 0)
assert(t, foo.FileMode == "644")
assert(t, foo.Size == 7)
part := make([]byte, 4)
n, err := foo.Data.Read(part)
isok(t, err)
assert(t, n <= 4)
assert(t, string(part[:n]) == "FooB"[:n])

bar, err := ar.Next()
isok(t, err)
assert(t, bar.Name == "bar.txt")
assert(t, bar.Timestamp == 1559837289)
assert(t, bar.OwnerID == 1000)
assert(t, bar.GroupID == 1000)
assert(t, bar.FileMode == "100644")
assert(t, bar.Size == 21)
data, err := ioutil.ReadAll(bar.Data)
isok(t, err)
assert(t, string(data) == "FooBar\nFooBar\nFooBar\n")

na, err := ar.Next()
iseof(t, err)
assert(t, na == nil)
}

func TestFoobarAr(t *testing.T) {
foobar_b := []byte(foobar_ar)
testFoobarAr(&wholeReader{Content: foobar_b}, t)
testFoobarAr(&byteReader{Content: foobar_b}, t)
}