mirror of
				https://github.com/linuxkit/linuxkit.git
				synced 2025-11-04 00:46:00 +00:00 
			
		
		
		
	include image reference as source in every tar file header
Signed-off-by: Avi Deitcher <avi@deitcher.net>
This commit is contained in:
		@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										31
									
								
								test/cases/000_build/001_tarheaders/tarheaders.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								test/cases/000_build/001_tarheaders/tarheaders.py
									
									
									
									
									
										Normal file
									
								
							@@ -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 <archive.tar>")
 | 
			
		||||
        sys.exit(1)
 | 
			
		||||
    archive_filename = sys.argv[1]
 | 
			
		||||
    list_pax_headers(archive_filename)
 | 
			
		||||
							
								
								
									
										38
									
								
								test/cases/000_build/001_tarheaders/test.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								test/cases/000_build/001_tarheaders/test.sh
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										46
									
								
								test/cases/000_build/001_tarheaders/test.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								test/cases/000_build/001_tarheaders/test.yml
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
		Reference in New Issue
	
	Block a user