From 1fe8cba10793ff75d28f7bf1ced3f405efeb82fe Mon Sep 17 00:00:00 2001 From: Avi Deitcher Date: Thu, 18 Apr 2024 14:08:00 +0300 Subject: [PATCH] include image reference as source in every tar file header Signed-off-by: Avi Deitcher --- src/cmd/linuxkit/moby/apk_tarwriter.go | 16 ++- src/cmd/linuxkit/moby/build.go | 130 ++++++++++-------- src/cmd/linuxkit/moby/image.go | 64 +++++++-- .../000_build/001_tarheaders/tarheaders.py | 31 +++++ test/cases/000_build/001_tarheaders/test.sh | 38 +++++ test/cases/000_build/001_tarheaders/test.yml | 46 +++++++ 6 files changed, 257 insertions(+), 68 deletions(-) create mode 100644 test/cases/000_build/001_tarheaders/tarheaders.py create mode 100644 test/cases/000_build/001_tarheaders/test.sh create mode 100644 test/cases/000_build/001_tarheaders/test.yml diff --git a/src/cmd/linuxkit/moby/apk_tarwriter.go b/src/cmd/linuxkit/moby/apk_tarwriter.go index 45691fae5..4db8cf419 100644 --- a/src/cmd/linuxkit/moby/apk_tarwriter.go +++ b/src/cmd/linuxkit/moby/apk_tarwriter.go @@ -8,17 +8,21 @@ import ( // apkTarWriter apk-aware tar writer that consolidates installed database, so that // it can be called multiple times and will do the union of all such databases, // rather than overwriting the previous one. +// Useful only for things that write to the base filesystem, i.e. init, since everything +// else is inside containers. const apkInstalledPath = "lib/apk/db/installed" type apkTarWriter struct { *tar.Writer - dbs [][]byte - current *bytes.Buffer + dbs [][]byte + current *bytes.Buffer + location string } -func newAPKTarWriter(w *tar.Writer) *apkTarWriter { +func newAPKTarWriter(w *tar.Writer, location string) *apkTarWriter { return &apkTarWriter{ - Writer: w, + Writer: w, + location: location, } } @@ -67,6 +71,10 @@ func (a *apkTarWriter) WriteAPKDB() error { Gid: 0, Typeflag: tar.TypeReg, Size: int64(size), + PAXRecords: map[string]string{ + PaxRecordLinuxkitSource: "LINUXKIT.apkinit", + PaxRecordLinuxkitLocation: a.location, + }, } if err := a.Writer.WriteHeader(hdr); err != nil { return err diff --git a/src/cmd/linuxkit/moby/build.go b/src/cmd/linuxkit/moby/build.go index 600f5518a..8d4b43740 100644 --- a/src/cmd/linuxkit/moby/build.go +++ b/src/cmd/linuxkit/moby/build.go @@ -92,25 +92,25 @@ func outputImage(image *Image, section string, prefix string, m Moby, idMap map[ } src, err := imagePull(&ref, opts.Pull, opts.CacheDir, opts.DockerCache, opts.Arch) if err != nil { - return fmt.Errorf("Could not pull image %s: %v", image.Image, err) + return fmt.Errorf("could not pull image %s: %v", image.Image, err) } configRaw, err := src.Config() if err != nil { - return fmt.Errorf("Failed to retrieve config for %s: %v", image.Image, err) + return fmt.Errorf("failed to retrieve config for %s: %v", image.Image, err) } oci, runtime, err := ConfigToOCI(image, configRaw, idMap) if err != nil { - return fmt.Errorf("Failed to create OCI spec for %s: %v", image.Image, err) + return fmt.Errorf("failed to create OCI spec for %s: %v", image.Image, err) } config, err := json.MarshalIndent(oci, "", " ") if err != nil { - return fmt.Errorf("Failed to create config for %s: %v", image.Image, err) + return fmt.Errorf("failed to create config for %s: %v", image.Image, err) } path := path.Join("containers", section, prefix+image.Name) readonly := oci.Root.Readonly - err = ImageBundle(path, image.ref, config, runtime, iw, readonly, dupMap, opts) + err = ImageBundle(path, section, image.ref, config, runtime, iw, readonly, dupMap, opts) if err != nil { - return fmt.Errorf("Failed to extract root filesystem for %s: %v", image.Image, err) + return fmt.Errorf("failed to extract root filesystem for %s: %v", image.Image, err) } return nil } @@ -153,14 +153,14 @@ func Build(m Moby, w io.Writer, opts BuildOpts) error { if m.Kernel.ref != nil { // get kernel and initrd tarball and ucode cpio archive from container log.Infof("Extract kernel image: %s", m.Kernel.ref) - kf := newKernelFilter(iw, m.Kernel.Cmdline, m.Kernel.Binary, m.Kernel.Tar, m.Kernel.UCode, opts.DecompressKernel) - err := ImageTar(m.Kernel.ref, "", kf, "", opts) + kf := newKernelFilter(m.Kernel.ref, iw, m.Kernel.Cmdline, m.Kernel.Binary, m.Kernel.Tar, m.Kernel.UCode, opts.DecompressKernel) + err := ImageTar("kernel", m.Kernel.ref, "", kf, "", opts) if err != nil { - return fmt.Errorf("Failed to extract kernel image and tarball: %v", err) + return fmt.Errorf("failed to extract kernel image and tarball: %v", err) } err = kf.Close() if err != nil { - return fmt.Errorf("Close error: %v", err) + return fmt.Errorf("close error: %v", err) } } @@ -168,10 +168,10 @@ func Build(m Moby, w io.Writer, opts BuildOpts) error { if len(m.Init) != 0 { log.Infof("Add init containers:") } - apkTar := newAPKTarWriter(iw) + apkTar := newAPKTarWriter(iw, "init") for _, ii := range m.initRefs { log.Infof("Process init image: %s", ii) - err := ImageTar(ii, "", apkTar, resolvconfSymlink, opts) + err := ImageTar("init", ii, "", apkTar, resolvconfSymlink, opts) if err != nil { return fmt.Errorf("failed to build init tarball from %s: %v", ii, err) } @@ -252,9 +252,10 @@ type kernelFilter struct { foundKernel bool foundKTar bool foundUCode bool + ref *reference.Spec } -func newKernelFilter(tw *tar.Writer, cmdline string, kernel string, tar, ucode *string, decompressKernel bool) *kernelFilter { +func newKernelFilter(ref *reference.Spec, tw *tar.Writer, cmdline string, kernel string, tar, ucode *string, decompressKernel bool) *kernelFilter { tarName, kernelName, ucodeName := "kernel.tar", "kernel", "" if tar != nil { tarName = *tar @@ -268,7 +269,7 @@ func newKernelFilter(tw *tar.Writer, cmdline string, kernel string, tar, ucode * if ucode != nil { ucodeName = *ucode } - return &kernelFilter{tw: tw, cmdline: cmdline, kernel: kernelName, tar: tarName, ucode: ucodeName, decompressKernel: decompressKernel} + return &kernelFilter{ref: ref, tw: tw, cmdline: cmdline, kernel: kernelName, tar: tarName, ucode: ucodeName, decompressKernel: decompressKernel} } func (k *kernelFilter) finishTar() error { @@ -299,7 +300,7 @@ func (k *kernelFilter) finishTar() error { } tr := tar.NewReader(k.buffer) - err := tarAppend(k.tw, tr) + err := tarAppend(k.ref, k.tw, tr) k.buffer = nil return err } @@ -348,11 +349,12 @@ func (k *kernelFilter) WriteHeader(hdr *tar.Header) error { // If we handled the ucode, /boot already exist. if !k.foundUCode { whdr := &tar.Header{ - Name: "boot", - Mode: 0755, - Typeflag: tar.TypeDir, - ModTime: defaultModTime, - Format: tar.FormatPAX, + Name: "boot", + Mode: 0755, + Typeflag: tar.TypeDir, + ModTime: defaultModTime, + Format: tar.FormatPAX, + PAXRecords: hdr.PAXRecords, } if err := tw.WriteHeader(whdr); err != nil { return err @@ -360,11 +362,12 @@ func (k *kernelFilter) WriteHeader(hdr *tar.Header) error { } // add the cmdline in /boot/cmdline whdr := &tar.Header{ - Name: "boot/cmdline", - Mode: 0644, - Size: int64(len(k.cmdline)), - ModTime: defaultModTime, - Format: tar.FormatPAX, + Name: "boot/cmdline", + Mode: 0644, + Size: int64(len(k.cmdline)), + ModTime: defaultModTime, + Format: tar.FormatPAX, + PAXRecords: hdr.PAXRecords, } if err := tw.WriteHeader(whdr); err != nil { return err @@ -375,11 +378,12 @@ func (k *kernelFilter) WriteHeader(hdr *tar.Header) error { } // Stash the kernel header and prime the buffer for the kernel k.hdr = &tar.Header{ - Name: "boot/kernel", - Mode: hdr.Mode, - Size: hdr.Size, - ModTime: defaultModTime, - Format: tar.FormatPAX, + Name: "boot/kernel", + Mode: hdr.Mode, + Size: hdr.Size, + ModTime: defaultModTime, + Format: tar.FormatPAX, + PAXRecords: hdr.PAXRecords, } k.buffer = new(bytes.Buffer) case k.tar: @@ -392,22 +396,24 @@ func (k *kernelFilter) WriteHeader(hdr *tar.Header) error { // If we handled the kernel, /boot already exist. if !k.foundKernel { whdr := &tar.Header{ - Name: "boot", - Mode: 0755, - Typeflag: tar.TypeDir, - ModTime: defaultModTime, - Format: tar.FormatPAX, + Name: "boot", + Mode: 0755, + Typeflag: tar.TypeDir, + ModTime: defaultModTime, + Format: tar.FormatPAX, + PAXRecords: hdr.PAXRecords, } if err := tw.WriteHeader(whdr); err != nil { return err } } whdr := &tar.Header{ - Name: "boot/ucode.cpio", - Mode: hdr.Mode, - Size: hdr.Size, - ModTime: defaultModTime, - Format: tar.FormatPAX, + Name: "boot/ucode.cpio", + Mode: hdr.Mode, + Size: hdr.Size, + ModTime: defaultModTime, + Format: tar.FormatPAX, + PAXRecords: hdr.PAXRecords, } if err := tw.WriteHeader(whdr); err != nil { return err @@ -419,7 +425,7 @@ func (k *kernelFilter) WriteHeader(hdr *tar.Header) error { return nil } -func tarAppend(iw *tar.Writer, tr *tar.Reader) error { +func tarAppend(ref *reference.Spec, iw *tar.Writer, tr *tar.Reader) error { for { hdr, err := tr.Next() if err == io.EOF { @@ -428,6 +434,12 @@ func tarAppend(iw *tar.Writer, tr *tar.Reader) error { if err != nil { return err } + hdr.Format = tar.FormatPAX + if hdr.PAXRecords == nil { + hdr.PAXRecords = make(map[string]string) + } + hdr.PAXRecords[PaxRecordLinuxkitSource] = ref.String() + hdr.PAXRecords[PaxRecordLinuxkitLocation] = "kernel" err = iw.WriteHeader(hdr) if err != nil { return err @@ -478,7 +490,7 @@ func decompressKernel(src *bytes.Buffer) (*bytes.Buffer, error) { versionMajor := int(s[versionIdx]) versionMinor := int(s[versionIdx+1]) if versionMajor < 2 && versionMinor < 8 { - return nil, fmt.Errorf("Unsupported bzImage version: %d.%d", versionMajor, versionMinor) + return nil, fmt.Errorf("unsupported bzImage version: %d.%d", versionMajor, versionMinor) } setupSectors := uint32(s[setupSectorsIdx]) @@ -488,7 +500,7 @@ func decompressKernel(src *bytes.Buffer) (*bytes.Buffer, error) { log.Debugf("bzImage: Payload at Offset: %d Length: %d", payloadOff, payloadLen) if len(s) < int(payloadOff+payloadLen) { - return nil, fmt.Errorf("Compressed bzImage payload exceeds size of image") + return nil, fmt.Errorf("compressed bzImage payload exceeds size of image") } if bytes.HasPrefix(s[payloadOff:], []byte(gzipMagic)) { @@ -496,10 +508,10 @@ func decompressKernel(src *bytes.Buffer) (*bytes.Buffer, error) { return gunzip(bytes.NewBuffer(s[payloadOff : payloadOff+payloadLen])) } // TODO(rn): Add more supported formats - return nil, fmt.Errorf("Unsupported bzImage payload format at offset %d", payloadOff) + return nil, fmt.Errorf("unsupported bzImage payload format at offset %d", payloadOff) } - return nil, fmt.Errorf("No compressed kernel or no supported format found") + return nil, fmt.Errorf("no compressed kernel or no supported format found") } func gunzip(src *bytes.Buffer) (*bytes.Buffer, error) { @@ -529,7 +541,7 @@ func metadata(m Moby, md string) ([]byte, error) { case "yaml": return yaml.Marshal(m) default: - return []byte{}, fmt.Errorf("Unsupported metadata type: %s", md) + return []byte{}, fmt.Errorf("unsupported metadata type: %s", md) } } @@ -540,10 +552,10 @@ func filesystem(m Moby, tw *tar.Writer, idMap map[string]uint32) error { if len(m.Files) != 0 { log.Infof("Add files:") } - for _, f := range m.Files { + for filecount, f := range m.Files { log.Infof(" %s", f.Path) if f.Path == "" { - return errors.New("Did not specify path for file") + return errors.New("did not specify path for file") } // tar archives should not have absolute paths if f.Path[0] == '/' { @@ -557,7 +569,7 @@ func filesystem(m Moby, tw *tar.Writer, idMap map[string]uint32) error { var err error mode, err = strconv.ParseInt(f.Mode, 8, 32) if err != nil { - return fmt.Errorf("Cannot parse file mode as octal value: %v", err) + return fmt.Errorf("cannot parse file mode as octal value: %v", err) } } dirMode := mode @@ -586,10 +598,10 @@ func filesystem(m Moby, tw *tar.Writer, idMap map[string]uint32) error { } if !f.Directory && f.Symlink == "" && f.Contents == nil { if f.Source == "" && f.Metadata == "" { - return fmt.Errorf("Contents of file (%s) not specified", f.Path) + return fmt.Errorf("contents of file (%s) not specified", f.Path) } if f.Source != "" && f.Metadata != "" { - return fmt.Errorf("Specified Source and Metadata for file: %s", f.Path) + return fmt.Errorf("specified Source and Metadata for file: %s", f.Path) } if f.Source != "" { source := f.Source @@ -600,7 +612,7 @@ func filesystem(m Moby, tw *tar.Writer, idMap map[string]uint32) error { _, err := os.Stat(source) if err != nil { // skip if not found or readable - log.Debugf("Skipping file [%s] as not readable and marked optional", source) + log.Debugf("skipping file [%s] as not readable and marked optional", source) continue } } @@ -617,10 +629,10 @@ func filesystem(m Moby, tw *tar.Writer, idMap map[string]uint32) error { } } else { if f.Metadata != "" { - return fmt.Errorf("Specified Contents and Metadata for file: %s", f.Path) + return fmt.Errorf("specified Contents and Metadata for file: %s", f.Path) } if f.Source != "" { - return fmt.Errorf("Specified Contents and Source for file: %s", f.Path) + return fmt.Errorf("specified Contents and Source for file: %s", f.Path) } } // we need all the leading directories @@ -644,6 +656,10 @@ func filesystem(m Moby, tw *tar.Writer, idMap map[string]uint32) error { Uid: int(uid), Gid: int(gid), Format: tar.FormatPAX, + PAXRecords: map[string]string{ + PaxRecordLinuxkitSource: "linuxkit.files", + PaxRecordLinuxkitLocation: fmt.Sprintf("files[%d]", filecount), + }, } err := tw.WriteHeader(hdr) if err != nil { @@ -660,10 +676,14 @@ func filesystem(m Moby, tw *tar.Writer, idMap map[string]uint32) error { Uid: int(uid), Gid: int(gid), Format: tar.FormatPAX, + PAXRecords: map[string]string{ + PaxRecordLinuxkitSource: "linuxkit.files", + PaxRecordLinuxkitLocation: fmt.Sprintf("files[%d]", filecount), + }, } if f.Directory { if f.Contents != nil { - return errors.New("Directory with contents not allowed") + return errors.New("directory with contents not allowed") } hdr.Typeflag = tar.TypeDir err := tw.WriteHeader(hdr) diff --git a/src/cmd/linuxkit/moby/image.go b/src/cmd/linuxkit/moby/image.go index c7818aa12..bc5a12c6c 100644 --- a/src/cmd/linuxkit/moby/image.go +++ b/src/cmd/linuxkit/moby/image.go @@ -14,6 +14,14 @@ import ( log "github.com/sirupsen/logrus" ) +const ( + // PaxRecordLinuxkitSource report the package source for a specific file + PaxRecordLinuxkitSource = "LINUXKIT.source" + // PaxRecordLinuxkitLocation report the location of the file in the linuxkit.yaml + // that led to this file being in this location + PaxRecordLinuxkitLocation = "LINUXKIT.location" +) + type tarWriter interface { Close() error Flush() error @@ -140,7 +148,8 @@ var touch = map[string]tar.Header{ } // tarPrefix creates the leading directories for a path -func tarPrefix(path string, tw tarWriter) error { +// path is the path to prefix, location is where this appears in the linuxkit.yaml file +func tarPrefix(path, location string, ref *reference.Spec, tw tarWriter) error { if path == "" { return nil } @@ -160,6 +169,10 @@ func tarPrefix(path string, tw tarWriter) error { ModTime: defaultModTime, Typeflag: tar.TypeDir, Format: tar.FormatPAX, + PAXRecords: map[string]string{ + PaxRecordLinuxkitSource: ref.String(), + PaxRecordLinuxkitLocation: location, + }, } if err := tw.WriteHeader(hdr); err != nil { return err @@ -170,13 +183,14 @@ func tarPrefix(path string, tw tarWriter) error { } // ImageTar takes a Docker image and outputs it to a tar stream -func ImageTar(ref *reference.Spec, prefix string, tw tarWriter, resolv string, opts BuildOpts) (e error) { +// location is where it is in the linuxkit.yaml file +func ImageTar(location string, ref *reference.Spec, prefix string, tw tarWriter, resolv string, opts BuildOpts) (e error) { log.Debugf("image tar: %s %s", ref, prefix) if prefix != "" && prefix[len(prefix)-1] != '/' { return fmt.Errorf("prefix does not end with /: %s", prefix) } - err := tarPrefix(prefix, tw) + err := tarPrefix(prefix, location, ref, tw) if err != nil { return err } @@ -185,12 +199,12 @@ func ImageTar(ref *reference.Spec, prefix string, tw tarWriter, resolv string, o // If pull==true, then it always tries to pull from registry. src, err := imagePull(ref, opts.Pull, opts.CacheDir, opts.DockerCache, opts.Arch) if err != nil { - return fmt.Errorf("Could not pull image %s: %v", ref, err) + return fmt.Errorf("could not pull image %s: %v", ref, err) } contents, err := src.TarReader() if err != nil { - return fmt.Errorf("Could not unpack image %s: %v", ref, err) + return fmt.Errorf("could not unpack image %s: %v", ref, err) } defer contents.Close() @@ -214,6 +228,12 @@ func ImageTar(ref *reference.Spec, prefix string, tw tarWriter, resolv string, o // force PAX format, since it allows for unlimited Name/Linkname // and we move all files below prefix. hdr.Format = tar.FormatPAX + // ensure we record the source of the file in the PAX header + if hdr.PAXRecords == nil { + hdr.PAXRecords = make(map[string]string) + } + hdr.PAXRecords[PaxRecordLinuxkitSource] = ref.String() + hdr.PAXRecords[PaxRecordLinuxkitLocation] = location if exclude[hdr.Name] { log.Debugf("image tar: %s %s exclude %s", ref, prefix, hdr.Name) _, err = io.Copy(io.Discard, tr) @@ -286,6 +306,12 @@ func ImageTar(ref *reference.Spec, prefix string, tw tarWriter, resolv string, o continue } hdr := touch[name] + // ensure that we record the source of the file + if hdr.PAXRecords == nil { + hdr.PAXRecords = make(map[string]string) + } + hdr.PAXRecords[PaxRecordLinuxkitSource] = ref.String() + hdr.PAXRecords[PaxRecordLinuxkitLocation] = location origName := hdr.Name hdr.Name = prefix + origName hdr.Format = tar.FormatPAX @@ -329,7 +355,7 @@ func ImageTar(ref *reference.Spec, prefix string, tw tarWriter, resolv string, o } // ImageBundle produces an OCI bundle at the given path in a tarball, given an image and a config.json -func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runtime, tw tarWriter, readonly bool, dupMap map[string]string, opts BuildOpts) error { // nolint: lll +func ImageBundle(prefix, location string, ref *reference.Spec, config []byte, runtime Runtime, tw tarWriter, readonly bool, dupMap map[string]string, opts BuildOpts) error { // nolint: lll // if read only, just unpack in rootfs/ but otherwise set up for overlay rootExtract := "rootfs" if !readonly { @@ -340,12 +366,12 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt root := path.Join(prefix, rootExtract) var foundElsewhere = dupMap[ref.String()] != "" if !foundElsewhere { - if err := ImageTar(ref, root+"/", tw, "", opts); err != nil { + if err := ImageTar(location, ref, root+"/", tw, "", opts); err != nil { return err } dupMap[ref.String()] = root } else { - if err := tarPrefix(prefix+"/", tw); err != nil { + if err := tarPrefix(prefix+"/", location, ref, tw); err != nil { return err } root = dupMap[ref.String()] @@ -357,6 +383,10 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt Size: int64(len(config)), ModTime: defaultModTime, Format: tar.FormatPAX, + PAXRecords: map[string]string{ + PaxRecordLinuxkitSource: ref.String(), + PaxRecordLinuxkitLocation: location, + }, } if err := tw.WriteHeader(hdr); err != nil { return err @@ -375,6 +405,10 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt Typeflag: tar.TypeDir, ModTime: defaultModTime, Format: tar.FormatPAX, + PAXRecords: map[string]string{ + PaxRecordLinuxkitSource: ref.String(), + PaxRecordLinuxkitLocation: location, + }, } if err := tw.WriteHeader(hdr); err != nil { return err @@ -386,6 +420,10 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt Typeflag: tar.TypeDir, ModTime: defaultModTime, Format: tar.FormatPAX, + PAXRecords: map[string]string{ + PaxRecordLinuxkitSource: ref.String(), + PaxRecordLinuxkitLocation: location, + }, } if err := tw.WriteHeader(hdr); err != nil { return err @@ -406,6 +444,10 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt Typeflag: tar.TypeDir, ModTime: defaultModTime, Format: tar.FormatPAX, + PAXRecords: map[string]string{ + PaxRecordLinuxkitSource: ref.String(), + PaxRecordLinuxkitLocation: location, + }, } if err := tw.WriteHeader(hdr); err != nil { return err @@ -424,7 +466,7 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt // write the runtime config runtimeConfig, err := json.MarshalIndent(runtime, "", " ") if err != nil { - return fmt.Errorf("Failed to create runtime config for %s: %v", ref, err) + return fmt.Errorf("failed to create runtime config for %s: %v", ref, err) } hdr = &tar.Header{ @@ -433,6 +475,10 @@ func ImageBundle(prefix string, ref *reference.Spec, config []byte, runtime Runt Size: int64(len(runtimeConfig)), ModTime: defaultModTime, Format: tar.FormatPAX, + PAXRecords: map[string]string{ + PaxRecordLinuxkitSource: ref.String(), + PaxRecordLinuxkitLocation: location, + }, } if err := tw.WriteHeader(hdr); err != nil { return err diff --git a/test/cases/000_build/001_tarheaders/tarheaders.py b/test/cases/000_build/001_tarheaders/tarheaders.py new file mode 100644 index 000000000..73dd781a3 --- /dev/null +++ b/test/cases/000_build/001_tarheaders/tarheaders.py @@ -0,0 +1,31 @@ +import tarfile +import sys + +def list_pax_headers(archive_path): + # Open the tar archive + try: + with tarfile.open(archive_path, 'r') as tar: + # Iterate over each member in the tar archive + for member in tar.getmembers(): + # ignore the root directory, which just exists + if member.name == '.': + continue + # Check if there are any PAX headers + if member.pax_headers: + # Check for the specific PAX header + if 'LINUXKIT.source' not in member.pax_headers: + print(f"File: {member.name} is missing LINUXKIT.source PAX Header.") + if 'LINUXKIT.location' not in member.pax_headers: + print(f"File: {member.name} is missing LINUXKIT.source PAX Header.") + else: + print(f"File: {member.name} has No PAX Headers.") + except Exception as e: + print("Failed to read tar archive:", e) + sys.exit(1) + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Usage: python list_pax_headers.py ") + sys.exit(1) + archive_filename = sys.argv[1] + list_pax_headers(archive_filename) diff --git a/test/cases/000_build/001_tarheaders/test.sh b/test/cases/000_build/001_tarheaders/test.sh new file mode 100644 index 000000000..837b1a574 --- /dev/null +++ b/test/cases/000_build/001_tarheaders/test.sh @@ -0,0 +1,38 @@ +#!/bin/sh +# SUMMARY: Check that tar output format build contains proper headers for each file +# LABELS: + +set -e + +# Source libraries. Uncomment if needed/defined +#. "${RT_LIB}" +. "${RT_PROJECT_ROOT}/_lib/lib.sh" + +NAME=tarheaders + +clean_up() { + rm -f ${NAME}* +} + +trap clean_up EXIT + +# do not include the sbom, because the SBoM unique IDs per file/package are *not* deterministic, +# (currently based upon syft), and thus will make the file non-reproducible +linuxkit build --no-sbom --format tar --name "${NAME}" ./test.yml + +# Check that the tarball contains the expected headers +# see that python is installed +PYTHON= +if which python ; then PYTHON=python ; elif which python3; then PYTHON=python3 ; else +echo "Failed to find any executable python or python3" +exit 1 +fi +FAILED=$(python ./tarheaders.py "${NAME}.tar") + +if [ -n "${FAILED}" ]; then + echo "Failed to find linuxkit.packagesource headers for the following files:" + echo "${FAILED}" + exit 1 +fi + +exit 0 diff --git a/test/cases/000_build/001_tarheaders/test.yml b/test/cases/000_build/001_tarheaders/test.yml new file mode 100644 index 000000000..4d0317483 --- /dev/null +++ b/test/cases/000_build/001_tarheaders/test.yml @@ -0,0 +1,46 @@ +# NOTE: Images build from this file likely do not run +kernel: + image: linuxkit/kernel:6.6.13 + cmdline: "console=ttyS0" +init: + - linuxkit/init:45a1ad5919f0b6acf0f0cf730e9434abfae11fe6 + - linuxkit/runc:6062483d748609d505f2bcde4e52ee64a3329f5f + - linuxkit/containerd:e7a92d9f3282039eac5fb1b07cac2b8664cbf0ad + +onboot: + - name: dhcpcd + image: linuxkit/dhcpcd:e9e3580f2de00e73e7b316a007186d22fea056ee + command: ["/sbin/dhcpcd", "--nobackground", "-f", "/dhcpcd.conf", "-1"] + # Add some random unsorted caps + capabilities: + - CAP_SETGID + - CAP_DAC_OVERRIDE + +services: + - name: testservice + image: linuxkit/ip:bb250017b05de5e16ac436b1eb19a39c87b5a252 + # Some environments + env: + - BENV=true + - ARANDOMENV=foobar + # Some mounts + mounts: + - type: cgroup + options: ["rw","nosuid","noexec","nodev","relatime"] + - type: overlay + source: overlay + destination: writeable-host-etc + options: ["rw", "lowerdir=/etc", "upperdir=/run/hostetc/upper", "workdir=/run/hostetc/work"] + # Some binds + binds: + - /var/run:/var/run + - /foobar:/foobar + - /etc/foobar:/etc/foobar + - /etc/aaa:/etc/aaa + # And some runtime settings + runtime: + mkdir: ["/var/lib/docker","/var/lib/aaa"] + +files: + - path: etc/linuxkit-config + metadata: yaml