internal/agent/imagecache/format.go
Ref: Size: 1.7 KiB History
package imagecache
import (
"bytes"
"errors"
"fmt"
"io"
"os"
)
// imageFormat is what a download's first bytes say it is. Detection is by magic
// number rather than URL suffix because the suffix lies: the Ubuntu cloud
// images the fleet boots by default are named ".img" and are qcow2 inside.
type imageFormat string
const (
formatRaw imageFormat = "raw"
formatGzip imageFormat = "gzip"
formatQcow2 imageFormat = "qcow2"
)
var (
gzipMagic = []byte{0x1f, 0x8b}
qcow2Magic = []byte{'Q', 'F', 'I', 0xfb}
)
// magicLen is the longest magic number above: a download shorter than this
// cannot be anything but raw.
const magicLen = 4
// sniff classifies a download from its leading bytes. Anything unrecognized is
// raw — raw has no magic number of its own, and an image the agent cannot name
// is one the guest firmware gets to interpret rather than one the agent
// rejects.
func sniff(head []byte) imageFormat {
switch {
case bytes.HasPrefix(head, gzipMagic):
return formatGzip
case bytes.HasPrefix(head, qcow2Magic):
return formatQcow2
default:
return formatRaw
}
}
// sniffFile classifies the image file at path. A file shorter than magicLen is
// read in full and classified on what it has, so a truncated or empty image is
// raw rather than an error — the checksum has already vouched for the bytes by
// the time anything calls this.
func sniffFile(path string) (imageFormat, error) {
f, err := os.Open(path)
if err != nil {
return "", err
}
defer f.Close()
head := make([]byte, magicLen)
n, err := io.ReadFull(f, head)
if err != nil && !errors.Is(err, io.EOF) && !errors.Is(err, io.ErrUnexpectedEOF) {
return "", fmt.Errorf("read image head: %w", err)
}
return sniff(head[:n]), nil
}