Skip to content
Open
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
90 changes: 90 additions & 0 deletions binary.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package cpio

import (
"encoding/binary"
"io"
"time"
)

const (
binaryHeaderLength = 26
)

func readInt16(b []byte) int {
return int(binary.LittleEndian.Uint16(b))
}

func int64FromInt32(b []byte) int64 {
// BigEndian order is called out in the cpio spec for the 16 bit words.
// This hard-codes LittleEndian Machine within the 16-bit words which
// should actually be parameterized by machine/archive
t := int64(binary.BigEndian.Uint32(
[]byte{
b[1],
b[0],
b[3],
b[2],
},
))
return t
}

func readBinaryHeader(r io.Reader) (*Header, error) {
// TODO: support binary-be

var buf [binaryHeaderLength - 2]byte // -2 for magic already read
if _, err := io.ReadFull(r, buf[:]); err != nil {
return nil, err
}
hdr := &Header{}

hdr.DeviceID = readInt16(buf[:2])
hdr.Inode = int64(readInt16(buf[2:4]))
hdr.Mode = FileMode(readInt16(buf[4:6]))
hdr.UID = readInt16(buf[6:8])
hdr.GID = readInt16(buf[8:10])
hdr.Links = readInt16(buf[10:12])
// skips rdev at buf[12:14]
hdr.ModTime = time.Unix(int64FromInt32(buf[14:18]), 0)
nameSize := readInt16(buf[18:20])
if nameSize < 1 {
return nil, ErrHeader
}

hdr.Size = int64(int64FromInt32(buf[20:])) // :24 is the end

name := make([]byte, nameSize)
if _, err := io.ReadFull(r, name); err != nil {
return nil, err
}
hdr.Name = string(name[:nameSize-1])
if hdr.Name == headerEOF {
return nil, io.EOF
}

// store padding between end of file and next header
hdr.pad = (2 - (hdr.Size % 2)) % 2

// skip to end of header/start of file
// +2 for magic bytes already read
pad := (2 - (2+len(buf)+len(name))%2) % 2
if pad > 0 {
if _, err := io.ReadFull(r, buf[:pad]); err != nil {
return nil, err
}
}

// read link name
if hdr.Mode&^ModePerm == ModeSymlink {
if hdr.Size < 1 {
return nil, ErrHeader
}
b := make([]byte, hdr.Size)
if _, err := io.ReadFull(r, b); err != nil {
return nil, err
}
hdr.Linkname = string(b)
hdr.Size = 0
}
return hdr, nil
}
28 changes: 28 additions & 0 deletions binary_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package cpio

import (
"io"
"os"
"testing"
)

func TestReadBinary(t *testing.T) {
f, err := os.Open("testdata/test_binary.cpio")
if err != nil {
t.Fatalf("error opening test file: %v", err)
}
defer f.Close()

r := NewReader(f)
for {
_, err := r.Next()
if err == io.EOF {
return
}
if err != nil {
t.Errorf("error moving to the next header: %v", err)
return
}
// TODO: validate header fields
}
}
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/jcrowgey/go-cpio

go 1.12

require github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e h1:hHg27A0RSSp2Om9lubZpiMgVbvn39bsUmW9U5h0twqc=
github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A=
29 changes: 27 additions & 2 deletions reader.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
package cpio

import (
"bytes"
"io"
"io/ioutil"
)

var magic map[string][]byte = map[string][]byte{
"binary-le": []byte{0xc7, 0x71}, // 070707
"svr4": []byte{0x30, 0x37, 0x30, 0x37, 0x30, 0x31}, // "070701"
"svr4-crc": []byte{0x30, 0x37, 0x30, 0x37, 0x30, 0x32}, // "070702"
}

// A Reader provides sequential access to the contents of a CPIO archive. A CPIO
// archive consists of a sequence of files. The Next method advances to the next
// file in the archive (including the first), and then it can be treated as an
Expand Down Expand Up @@ -67,6 +74,24 @@ func (r *Reader) next() (*Header, error) {

// ReadHeader creates a new Header, reading from r.
func readHeader(r io.Reader) (*Header, error) {
// currently only SVR4 format is supported
return readSVR4Header(r)
var binMagic [2]byte
if _, err := io.ReadFull(r, binMagic[:]); err != nil {
return nil, err
}
if bytes.Equal(binMagic[:], magic["binary-le"]) {
return readBinaryHeader(r)
} else {
var ascMagic [6]byte
copy(ascMagic[:], binMagic[:])
if _, err := io.ReadFull(r, ascMagic[2:]); err != nil {
return nil, err
}
if bytes.Equal(ascMagic[:], magic["svr4"]) {
return readSVR4Header(r, false)
} else if bytes.Equal(ascMagic[:], magic["svr4-crc"]) {
return readSVR4Header(r, true)
} else {
return nil, ErrHeader
}
}
}
58 changes: 24 additions & 34 deletions svr4.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
package cpio

import (
"bytes"
"fmt"
"io"
"strconv"
"time"
)

const (
svr4MaxNameSize = 4096 // MAX_PATH
svr4MaxFileSize = 4294967295
svr4MaxNameSize = 4096 // MAX_PATH
svr4MaxFileSize = 4294967295
svr4HeaderLength = 110
)

var svr4Magic = []byte{0x30, 0x37, 0x30, 0x37, 0x30, 0x31} // 070701

func readHex(s string) int64 {
// errors are ignored and 0 returned
i, _ := strconv.ParseInt(s, 16, 64)
Expand All @@ -26,43 +24,32 @@ func writeHex(b []byte, i int64) {
copy(b, fmt.Sprintf("%08X", i))
}

func readSVR4Header(r io.Reader) (*Header, error) {
var buf [110]byte
if _, err := io.ReadFull(r, buf[:]); err != nil {
return nil, err
}

func readSVR4Header(r io.Reader, hasCRC bool) (*Header, error) {
// TODO: check endianness

// check magic
hasCRC := false
if !bytes.HasPrefix(buf[:], svr4Magic[:5]) {
return nil, ErrHeader
}
if buf[5] == 0x32 { // '2'
hasCRC = true
} else if buf[5] != 0x31 { // '1'
return nil, ErrHeader
var buf [svr4HeaderLength - 6]byte // -6 for magic already read
if _, err := io.ReadFull(r, buf[:]); err != nil {
return nil, err
}

asc := string(buf[:])
hdr := &Header{}

hdr.Inode = readHex(asc[6:14])
hdr.Mode = FileMode(readHex(asc[14:22]))
hdr.UID = int(readHex(asc[22:30]))
hdr.GID = int(readHex(asc[30:38]))
hdr.Links = int(readHex(asc[38:46]))
hdr.ModTime = time.Unix(readHex(asc[46:54]), 0)
hdr.Size = readHex(asc[54:62])
hdr.Inode = readHex(asc[:8])
hdr.Mode = FileMode(readHex(asc[8:16]))
hdr.UID = int(readHex(asc[16:24]))
hdr.GID = int(readHex(asc[24:32]))
hdr.Links = int(readHex(asc[32:40]))
hdr.ModTime = time.Unix(readHex(asc[40:48]), 0)
hdr.Size = readHex(asc[48:56])
if hdr.Size > svr4MaxFileSize {
return nil, ErrHeader
}
nameSize := readHex(asc[94:102])
nameSize := readHex(asc[88:96])
if nameSize < 1 || nameSize > svr4MaxNameSize {
return nil, ErrHeader
}
hdr.Checksum = Checksum(readHex(asc[102:110]))
hdr.Checksum = Checksum(readHex(asc[96:104]))
if !hasCRC && hdr.Checksum != 0 {
return nil, ErrHeader
}
Expand All @@ -80,7 +67,8 @@ func readSVR4Header(r io.Reader) (*Header, error) {
hdr.pad = (4 - (hdr.Size % 4)) % 4

// skip to end of header/start of file
pad := (4 - (len(buf)+len(name))%4) % 4
// +6 for magic bytes already read
pad := (4 - (6+len(buf)+len(name))%4) % 4
if pad > 0 {
if _, err := io.ReadFull(r, buf[:pad]); err != nil {
return nil, err
Expand All @@ -104,15 +92,17 @@ func readSVR4Header(r io.Reader) (*Header, error) {
}

func writeSVR4Header(w io.Writer, hdr *Header) (pad int64, err error) {
var hdrBuf [110]byte
var hdrBuf [svr4HeaderLength]byte
for i := 0; i < len(hdrBuf); i++ {
hdrBuf[i] = '0'
}
magic := svr4Magic
var hMagic [6]byte
if hdr.Checksum != 0 {
magic[5] = 0x32
copy(hMagic[:], magic["svr4-crc"][:])
} else {
copy(hMagic[:], magic["svr4"][:])
}
copy(hdrBuf[:], magic)
copy(hdrBuf[:], hMagic[:])
writeHex(hdrBuf[6:14], hdr.Inode)
writeHex(hdrBuf[14:22], int64(hdr.Mode))
writeHex(hdrBuf[22:30], int64(hdr.UID))
Expand Down
2 changes: 1 addition & 1 deletion svr4_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ var files = []struct {
{"./todo.txt", "Get animal handling license."},
}

func TestRead(t *testing.T) {
func TestReadSVR4(t *testing.T) {
f, err := os.Open("testdata/test_svr4_crc.cpio")
if err != nil {
t.Fatalf("error opening test file: %v", err)
Expand Down
Binary file added testdata/test_binary.cpio
Binary file not shown.