Skip to content

Commit

Permalink
feat(internal): Expose raw cpio header also
Browse files Browse the repository at this point in the history
Signed-off-by: Cezar Craciunoiu <cezar.craciunoiu@unikraft.io>
  • Loading branch information
craciunoiuc committed Apr 23, 2024
1 parent 170d553 commit 7a34675
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 121 deletions.
2 changes: 1 addition & 1 deletion initrd/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func NewFromFile(_ context.Context, path string, opts ...InitrdOption) (Initrd,

// Iterate through the files in the archive.
for {
hdr, err := reader.Next()
hdr, _, err := reader.Next()
if err == io.EOF {
// end of cpio archive
break
Expand Down
100 changes: 2 additions & 98 deletions internal/cli/kraft/cloud/volume/import/cpio.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"bytes"
"context"
"crypto/tls"
"encoding/binary"
"errors"
"fmt"
"io"
Expand All @@ -22,99 +21,6 @@ import (
"kraftkit.sh/internal/cpio"
)

// header is the cpio header used inside the volume importer
type packedHeader struct {
magic [6]byte
inode [8]byte
mode [8]byte
uid [8]byte
gid [8]byte
nlink [8]byte
mtime [8]byte
filesize [8]byte
major [8]byte
minor [8]byte
refMajor [8]byte
refMinor [8]byte
nameSize [8]byte
checksum [8]byte
}

func NewFromHeader(hdr cpio.Header) packedHeader {
var p packedHeader

// Magic
buf := new(bytes.Buffer)
buf.WriteString("070701")
copy(p.magic[:], buf.Bytes())

// Inode
buf = new(bytes.Buffer)
binary.Write(buf, binary.LittleEndian, int64(hdr.Inode))
copy(p.inode[:], buf.Bytes())

// Mode
buf = new(bytes.Buffer)
binary.Write(buf, binary.LittleEndian, int64(hdr.Mode))
copy(p.mode[:], buf.Bytes())

// Uid
buf = new(bytes.Buffer)
binary.Write(buf, binary.LittleEndian, int64(hdr.Uid))
copy(p.uid[:], buf.Bytes())

// Gid
buf = new(bytes.Buffer)
binary.Write(buf, binary.LittleEndian, int64(hdr.Guid))
copy(p.gid[:], buf.Bytes())

// Nlink
buf = new(bytes.Buffer)
binary.Write(buf, binary.LittleEndian, int64(hdr.Links))
copy(p.nlink[:], buf.Bytes())

// ModTime
buf = new(bytes.Buffer)
binary.Write(buf, binary.LittleEndian, int64(hdr.ModTime.Unix()))
copy(p.mtime[:], buf.Bytes())

// FileSize
buf = new(bytes.Buffer)
binary.Write(buf, binary.LittleEndian, int64(hdr.Size))
copy(p.filesize[:], buf.Bytes())

// NameSize
buf = new(bytes.Buffer)
binary.Write(buf, binary.LittleEndian, int64(len(hdr.Name)))
copy(p.nameSize[:], buf.Bytes())

// Checksum
buf = new(bytes.Buffer)
binary.Write(buf, binary.LittleEndian, int64(hdr.Checksum))
copy(p.checksum[:], buf.Bytes())

return p
}

func (p packedHeader) Pack() []byte {
buf := new(bytes.Buffer)
binary.Write(buf, binary.LittleEndian, p.magic)
binary.Write(buf, binary.LittleEndian, p.inode)
binary.Write(buf, binary.LittleEndian, p.mode)
binary.Write(buf, binary.LittleEndian, p.uid)
binary.Write(buf, binary.LittleEndian, p.gid)
binary.Write(buf, binary.LittleEndian, p.nlink)
binary.Write(buf, binary.LittleEndian, p.mtime)
binary.Write(buf, binary.LittleEndian, p.filesize)
binary.Write(buf, binary.LittleEndian, p.major)
binary.Write(buf, binary.LittleEndian, p.minor)
binary.Write(buf, binary.LittleEndian, p.refMajor)
binary.Write(buf, binary.LittleEndian, p.refMinor)
binary.Write(buf, binary.LittleEndian, p.nameSize)
binary.Write(buf, binary.LittleEndian, p.checksum)
return buf.Bytes()
}

// buildCPIO generates a CPIO archive from the data at the given source.
func buildCPIO(ctx context.Context, source string) (path string, size int64, err error) {
if source == "." {
Expand Down Expand Up @@ -187,7 +93,7 @@ func copyCPIO(ctx context.Context, conn *tls.Conn, auth, path string, size int64

// Iterate through the files in the archive.
for {
hdr, err := reader.Next()
hdr, raw, err := reader.Next()
if err == io.EOF {
// end of cpio archive
break
Expand All @@ -196,10 +102,8 @@ func copyCPIO(ctx context.Context, conn *tls.Conn, auth, path string, size int64
return err
}

packedHdr := NewFromHeader(*hdr)

// io.Copy the header
_, err = io.Copy(conn, bytes.NewBuffer(packedHdr.Pack()))
_, err = io.Copy(conn, bytes.NewBuffer(raw.Bytes()))
if err != nil {
return err
}
Expand Down
8 changes: 8 additions & 0 deletions internal/cpio/header.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,14 @@ type Header struct {
pad int64 // bytes to pad before next header
}

// RawHeader exposes the unparsed header data.
type RawHeader [110]byte

// Bytes returns the header as a byte slice.
func (rh *RawHeader) Bytes() []byte {
return rh[:]
}

// FileInfo returns an fs.FileInfo for the Header.
func (h *Header) FileInfo() os.FileInfo {
return fileInfo{h}
Expand Down
15 changes: 7 additions & 8 deletions internal/cpio/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cpio

import (
"io"
"io/ioutil"
)

// Reader provides sequential access to the contents of a CPIO archive.
Expand Down Expand Up @@ -46,27 +45,27 @@ func (r *Reader) Read(p []byte) (n int, err error) {
// in the current file is automatically discarded.
//
// io.EOF is returned at the end of the input.
func (r *Reader) Next() (*Header, error) {
func (r *Reader) Next() (*Header, *RawHeader, error) {
if r.hdr == nil {
return r.next()
}
skp := r.eof + r.hdr.pad
if skp > 0 {
_, err := io.CopyN(ioutil.Discard, r.r, skp)
_, err := io.CopyN(io.Discard, r.r, skp)
if err != nil {
return nil, err
return nil, nil, err
}
}
return r.next()
}

func (r *Reader) next() (*Header, error) {
func (r *Reader) next() (*Header, *RawHeader, error) {
r.eof = 0
hdr, err := readSVR4Header(r.r)
hdr, raw, err := readSVR4Header(r.r)
if err != nil {
return nil, err
return nil, raw, err
}
r.hdr = hdr
r.eof = hdr.Size
return hdr, nil
return hdr, raw, nil
}
28 changes: 14 additions & 14 deletions internal/cpio/svr4.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,23 @@ func writeHex(b []byte, i int64) {
copy(b, fmt.Sprintf("%08X", i))
}

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

// TODO: check endianness

// check magic
hasCRC := false
if !bytes.HasPrefix(buf[:], svr4Magic[:5]) {
return nil, ErrHeader
return nil, nil, ErrHeader
}
if buf[5] == '2' {
hasCRC = true
} else if buf[5] != '1' {
return nil, ErrHeader
return nil, nil, ErrHeader
}

asc := string(buf[:])
Expand All @@ -56,24 +56,24 @@ func readSVR4Header(r io.Reader) (*Header, error) {
Size: readHex(asc[54:62]),
}
if hdr.Size > svr4MaxFileSize {
return nil, ErrHeader
return nil, nil, ErrHeader
}
nameSize := readHex(asc[94:102])
if nameSize < 1 || nameSize > svr4MaxNameSize {
return nil, ErrHeader
return nil, nil, ErrHeader
}
hdr.Checksum = uint32(readHex(asc[102:110]))
if !hasCRC && hdr.Checksum != 0 {
return nil, ErrHeader
return nil, nil, ErrHeader
}

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

// store padding between end of file and next header
Expand All @@ -83,24 +83,24 @@ func readSVR4Header(r io.Reader) (*Header, error) {
pad := (4 - (len(buf)+len(name))%4) % 4
if pad > 0 {
if _, err := io.ReadFull(r, buf[:pad]); err != nil {
return nil, err
return nil, nil, err
}
}

// read link name
if hdr.Mode&^ModePerm == TypeSymlink {
if hdr.Size < 1 || hdr.Size > svr4MaxNameSize {
return nil, ErrHeader
return nil, nil, ErrHeader
}
b := make([]byte, hdr.Size)
if _, err := io.ReadFull(r, b); err != nil {
return nil, err
return nil, nil, err
}
hdr.Linkname = string(b)
hdr.Size = 0
}

return hdr, nil
return hdr, &buf, nil
}

func writeSVR4Header(w io.Writer, hdr *Header) (pad int64, err error) {
Expand Down

0 comments on commit 7a34675

Please sign in to comment.