From c70a898fba76b28a8456485fa41f2a4d3aaa26ef Mon Sep 17 00:00:00 2001 From: David Trudgian Date: Wed, 18 Oct 2023 13:42:49 +0100 Subject: [PATCH] feat: support AUFS -> OverlayFS whiteout conversion for squashfs Add an option, `OptConvertWhiteout`, for `squashfsConverter`, that when set true will convert AUFS whiteout files in the incoming tar, to OverlayFS whiteouts in the outgoing squashfs. A `.wh.file` marker indicating a whiteout of `file` is replaced by `file` as a 0:0 character device. A `dir/.wh..wh..opq` marker indicating an opaque directory is replaced with the `trusted.overlay.opaque="y"` xattr on `dir`. A two-pass approach is required. AUFS directory opaque markers are not required to be adjacent to the directory to which they apply, in the tar stream. As `tar2sqfs` and `sqfstar` do not support tar files with two instances of the same dir/file, we cannot append an additional tar entry if we come across an opaque marker distant from the target directory entry. Fixes #25 --- pkg/mutate/squashfs.go | 162 ++++++++++++++++-- pkg/mutate/squashfs_test.go | 102 ++++++++++- .../AUFSBlob_sqfstar.golden | Bin 0 -> 4096 bytes .../AUFSBlob_sqfstar_convertWhiteout.golden | Bin 0 -> 4096 bytes .../AUFSBlob_tar2sqfs.golden | Bin 0 -> 4096 bytes .../AUFSBlob_tar2sqfs_convertWhiteout.golden | Bin 0 -> 4096 bytes ...loWorldBlob_sqfstar_convertWhiteout.golden | Bin 0 -> 4096 bytes ...oWorldBlob_tar2sqfs_convertWhiteout.golden | Bin 0 -> 4096 bytes ...16f7e8649e94fb4fc21fe77e8310c060f61caaff8a | 1 + ...b52c051cccf33d85f60827bd269c978fd95f05c3c9 | 16 ++ ...c3832cee4a2f725b15aeb258791640185c3126b2bf | Bin 0 -> 252 bytes .../images/aufs-docker-v2-manifest/index.json | 1 + .../images/aufs-docker-v2-manifest/oci-layout | 3 + 13 files changed, 264 insertions(+), 21 deletions(-) create mode 100644 pkg/mutate/testdata/Test_SquashfsLayer/AUFSBlob_sqfstar.golden create mode 100644 pkg/mutate/testdata/Test_SquashfsLayer/AUFSBlob_sqfstar_convertWhiteout.golden create mode 100644 pkg/mutate/testdata/Test_SquashfsLayer/AUFSBlob_tar2sqfs.golden create mode 100644 pkg/mutate/testdata/Test_SquashfsLayer/AUFSBlob_tar2sqfs_convertWhiteout.golden create mode 100644 pkg/mutate/testdata/Test_SquashfsLayer/HelloWorldBlob_sqfstar_convertWhiteout.golden create mode 100644 pkg/mutate/testdata/Test_SquashfsLayer/HelloWorldBlob_tar2sqfs_convertWhiteout.golden create mode 100644 test/images/aufs-docker-v2-manifest/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a create mode 100644 test/images/aufs-docker-v2-manifest/blobs/sha256/6c9c1b8d1adba535a40046b52c051cccf33d85f60827bd269c978fd95f05c3c9 create mode 100644 test/images/aufs-docker-v2-manifest/blobs/sha256/da55812559dec81445c289c3832cee4a2f725b15aeb258791640185c3126b2bf create mode 100644 test/images/aufs-docker-v2-manifest/index.json create mode 100644 test/images/aufs-docker-v2-manifest/oci-layout diff --git a/pkg/mutate/squashfs.go b/pkg/mutate/squashfs.go index 8ce9c79..41c1bd9 100644 --- a/pkg/mutate/squashfs.go +++ b/pkg/mutate/squashfs.go @@ -5,24 +5,32 @@ package mutate import ( + "archive/tar" "errors" "fmt" "io" "os" "os/exec" "path/filepath" + "strings" "sync" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/types" ) -const layerMediaType types.MediaType = "application/vnd.sylabs.image.layer.v1.squashfs" +const ( + layerMediaType types.MediaType = "application/vnd.sylabs.image.layer.v1.squashfs" + + aufsWhiteoutPrefix = ".wh." + aufsOpaqueMarker = ".wh..wh..opq" +) type squashfsConverter struct { - converter string // Path to converter program. - args []string // Arguments required for converter program. - dir string // Working directory. + converter string // Path to converter program. + args []string // Arguments required for converter program. + dir string // Working directory. + convertWhiteout bool // Convert whiteout markers from AUFS -> OverlayFS } // SquashfsConverterOpt are used to specify squashfs converter options. @@ -45,6 +53,15 @@ func OptSquashfsLayerConverter(converter string) SquashfsConverterOpt { var errSquashfsConverterNotSupported = errors.New("squashfs converter not supported") +// OptConvertWhiteout is set to convert whiteout / opaque markers from AUFS to +// OverlayFS format during the tar -> squashfs conversion. +func OptConvertWhiteout(b bool) SquashfsConverterOpt { + return func(c *squashfsConverter) error { + c.convertWhiteout = b + return nil + } +} + // SquashfsLayer converts the base layer into a layer using the squashfs format. A dir must be // specified, which is used as a working directory during conversion. The caller is responsible for // cleaning up dir. @@ -104,8 +121,8 @@ func SquashfsLayer(base v1.Layer, dir string, opts ...SquashfsConverterOpt) (v1. } // makeSquashfs returns the path to a squashfs file that contains the contents of the uncompressed -// TAR stream from r. -func (c *squashfsConverter) makeSquashfs(r io.Reader) (string, error) { +// TAR stream from l.Uncompressed(). +func (c *squashfsConverter) makeSquashfs(l v1.Layer) (string, error) { dir, err := os.MkdirTemp(c.dir, "") if err != nil { return "", err @@ -113,14 +130,48 @@ func (c *squashfsConverter) makeSquashfs(r io.Reader) (string, error) { path := filepath.Join(dir, "layer.sqfs") + tarIn, err := l.Uncompressed() + if err != nil { + return "", err + } + defer tarIn.Close() + + var opaquePaths map[string]bool + filterErr := make(chan error, 1) + + if c.convertWhiteout { + opqrc, err := l.Uncompressed() + if err != nil { + return "", err + } + defer opqrc.Close() + opaquePaths, err = scanAUFSOpaque(opqrc) + if err != nil { + return "", err + } + + filterIn := tarIn + pr, pw := io.Pipe() + tarIn = pr + go func() { + filterErr <- whiteoutFilter(filterIn, pw, opaquePaths) + }() + } + //nolint:gosec // Arguments are created programatically. cmd := exec.Command(c.converter, append(c.args, path)...) - cmd.Stdin = r + cmd.Stdin = tarIn if out, err := cmd.CombinedOutput(); err != nil { return "", fmt.Errorf("%s error: %w, output: %s", c.converter, err, out) } + if c.convertWhiteout { + if err := <-filterErr; err != nil { + return "", err + } + } + return path, nil } @@ -170,13 +221,7 @@ func (l *squashfsLayer) populate() error { return nil } - rc, err := l.base.Uncompressed() - if err != nil { - return err - } - defer rc.Close() - - path, err := l.converter.makeSquashfs(rc) + path, err := l.converter.makeSquashfs(l.base) if err != nil { return err } @@ -241,3 +286,92 @@ func (l *squashfsLayer) Size() (int64, error) { func (l *squashfsLayer) MediaType() (types.MediaType, error) { return layerMediaType, nil } + +// whiteOutFilter streams a tar file from in to out, replacing AUFS whiteout +// markers with OverlayFS whiteout markers. Due to unrestricted ordering of +// markers vs their target, the list of opaquePaths must be obtained prior to +// filtering and provided to this filter. +func whiteoutFilter(in io.ReadCloser, out io.WriteCloser, opaquePaths map[string]bool) error { + tr := tar.NewReader(in) + tw := tar.NewWriter(out) + defer out.Close() + defer tw.Close() + + for { + header, err := tr.Next() + + if err == io.EOF { + return nil + } + if err != nil { + return err + } + + // Must force to PAX format, to accommodate xattrs + header.Format = tar.FormatPAX + + clean := filepath.Clean(header.Name) + base := filepath.Base(header.Name) + parent := filepath.Dir(header.Name) + + // Don't include .wh..wh..opq opaque directory markers in output. + if base == aufsOpaqueMarker { + // If we don't know the target should be opaque, then provided opaquePaths is incorrect. + if !opaquePaths[parent] { + return fmt.Errorf("unexpected opaque marker found for %q", parent) + } + continue + } + // Set overlayfs xattr on a dir that was previously found to contain a .wh..wh..opq marker. + if opq := opaquePaths[clean]; opq { + if header.PAXRecords == nil { + header.PAXRecords = map[string]string{} + } + header.PAXRecords["SCHILY.xattr."+"trusted.overlay.opaque"] = "y" + } + // Replace a `.wh.` marker with a char dev 0 at + if strings.HasPrefix(base, aufsWhiteoutPrefix) { + target := filepath.Join(parent, strings.TrimPrefix(base, aufsWhiteoutPrefix)) + cHeader := header + cHeader.Name = target + cHeader.Typeflag = tar.TypeChar + cHeader.Devmajor = 0 + cHeader.Devminor = 0 + if err := tw.WriteHeader(header); err != nil { + return err + } + continue + } + + if err := tw.WriteHeader(header); err != nil { + return err + } + + if _, err := io.Copy(tw, tr); err != nil { + return err + } + + } +} + +func scanAUFSOpaque(in io.Reader) (opaquePaths map[string]bool, err error) { + opaquePaths = map[string]bool{} + tr := tar.NewReader(in) + for { + header, err := tr.Next() + + if err == io.EOF { + return opaquePaths, nil + } + if err != nil { + return nil, err + } + + base := filepath.Base(header.Name) + parent := filepath.Dir(header.Name) + + if base == aufsOpaqueMarker { + opaquePaths[parent] = true + } + } +} diff --git a/pkg/mutate/squashfs_test.go b/pkg/mutate/squashfs_test.go index 71b385d..1f48213 100644 --- a/pkg/mutate/squashfs_test.go +++ b/pkg/mutate/squashfs_test.go @@ -49,18 +49,21 @@ func diffSquashFS(tb testing.TB, pathA, pathB string, diffArgs ...string) { func Test_SquashfsLayer(t *testing.T) { tests := []struct { - name string - layer v1.Layer - converter string - diffArgs []string + name string + layer v1.Layer + converter string + convertWhiteout bool + diffArgs []string }{ + // HelloWorld layer contains no whiteouts - convertWhiteout should have no effect on output. { name: "HelloWorldBlob_sqfstar", layer: testLayer(t, "hello-world-docker-v2-manifest", v1.Hash{ Algorithm: "sha256", Hex: "7050e35b49f5e348c4809f5eff915842962cb813f32062d3bbdd35c750dd7d01", }), - converter: "sqfstar", + converter: "sqfstar", + convertWhiteout: false, // Some versions of squashfs-tools do not implement '-root-uid'/'-root-gid', so ignore // differences in ownership. diffArgs: []string{"--no-owner"}, @@ -71,7 +74,89 @@ func Test_SquashfsLayer(t *testing.T) { Algorithm: "sha256", Hex: "7050e35b49f5e348c4809f5eff915842962cb813f32062d3bbdd35c750dd7d01", }), - converter: "tar2sqfs", + converter: "tar2sqfs", + convertWhiteout: false, + }, + { + name: "HelloWorldBlob_sqfstar_convertWhiteout", + layer: testLayer(t, "hello-world-docker-v2-manifest", v1.Hash{ + Algorithm: "sha256", + Hex: "7050e35b49f5e348c4809f5eff915842962cb813f32062d3bbdd35c750dd7d01", + }), + converter: "sqfstar", + convertWhiteout: true, + // Some versions of squashfs-tools do not implement '-root-uid'/'-root-gid', so ignore + // differences in ownership. + diffArgs: []string{"--no-owner"}, + }, + { + name: "HelloWorldBlob_tar2sqfs_convertWhiteout", + layer: testLayer(t, "hello-world-docker-v2-manifest", v1.Hash{ + Algorithm: "sha256", + Hex: "7050e35b49f5e348c4809f5eff915842962cb813f32062d3bbdd35c750dd7d01", + }), + converter: "tar2sqfs", + convertWhiteout: true, + }, + // AUFS layer contains whiteouts. Should be converted to overlayfs form when convertWhiteout = true. + // + // Original (AUFS) + // All regular files. + // + // [drwxr-xr-x] . + // ├── [drwxr-xr-x] dir + // │   └── [-rw-r--r--] .wh..wh..opq + // └── [-rw-r--r--] .wh.file + // + // Converted (OverlayFS) + // .wh.file becomes file as a char 0:0 device + // dir/.wh..wh..opq becomes trusted.overlay.opaque="y" xattr on dir + // + // [drwxr-xr-x] . + // ├── [drwxr-xr-x] dir + // └── [crw-r--r--] file + // + { + name: "AUFSBlob_sqfstar", + layer: testLayer(t, "aufs-docker-v2-manifest", v1.Hash{ + Algorithm: "sha256", + Hex: "da55812559dec81445c289c3832cee4a2f725b15aeb258791640185c3126b2bf", + }), + converter: "sqfstar", + convertWhiteout: false, + // Some versions of squashfs-tools do not implement '-root-uid'/'-root-gid', so ignore + // differences in ownership. + diffArgs: []string{"--no-owner"}, + }, + { + name: "AUFSBlob_tar2sqfs", + layer: testLayer(t, "aufs-docker-v2-manifest", v1.Hash{ + Algorithm: "sha256", + Hex: "da55812559dec81445c289c3832cee4a2f725b15aeb258791640185c3126b2bf", + }), + converter: "tar2sqfs", + convertWhiteout: false, + }, + { + name: "AUFSBlob_sqfstar_convertWhiteout", + layer: testLayer(t, "aufs-docker-v2-manifest", v1.Hash{ + Algorithm: "sha256", + Hex: "da55812559dec81445c289c3832cee4a2f725b15aeb258791640185c3126b2bf", + }), + converter: "sqfstar", + convertWhiteout: true, + // Some versions of squashfs-tools do not implement '-root-uid'/'-root-gid', so ignore + // differences in ownership. + diffArgs: []string{"--no-owner"}, + }, + { + name: "AUFSBlob_tar2sqfs_convertWhiteout", + layer: testLayer(t, "aufs-docker-v2-manifest", v1.Hash{ + Algorithm: "sha256", + Hex: "da55812559dec81445c289c3832cee4a2f725b15aeb258791640185c3126b2bf", + }), + converter: "tar2sqfs", + convertWhiteout: true, }, } for _, tt := range tests { @@ -82,7 +167,10 @@ func Test_SquashfsLayer(t *testing.T) { t.Skip(err) } - l, err := SquashfsLayer(tt.layer, t.TempDir(), OptSquashfsLayerConverter(tt.converter)) + l, err := SquashfsLayer(tt.layer, t.TempDir(), + OptSquashfsLayerConverter(tt.converter), + OptConvertWhiteout(tt.convertWhiteout), + ) if err != nil { t.Fatal(err) } diff --git a/pkg/mutate/testdata/Test_SquashfsLayer/AUFSBlob_sqfstar.golden b/pkg/mutate/testdata/Test_SquashfsLayer/AUFSBlob_sqfstar.golden new file mode 100644 index 0000000000000000000000000000000000000000..8d9f3f9605a113596ad9f6f2aaf5b07de3199e74 GIT binary patch literal 4096 zcmc~OE-YqYfB_~jgONdyA%TIB0VJLPm3jfCA3*8

=xSFNM;#(Bz#NDsCkwMI1W8 zz_NGtm*9gu3P1Q+0@Rp=L+aE5=1F$jC?AgAVIv&F&)jSzuvy{c>zGUasg0+a&sdbHEfq zjY*RtSs31@arCh?ykurzfVl_itWjz-1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ON LU^E0qWC#EN0P#lZ literal 0 HcmV?d00001 diff --git a/pkg/mutate/testdata/Test_SquashfsLayer/AUFSBlob_sqfstar_convertWhiteout.golden b/pkg/mutate/testdata/Test_SquashfsLayer/AUFSBlob_sqfstar_convertWhiteout.golden new file mode 100644 index 0000000000000000000000000000000000000000..109d5c62346b687338db253926b53dd909ef8fa7 GIT binary patch literal 4096 zcmc~OE-YqdfB_~jgONdyA%TIBfd$BOfJ*T&LKp|3{4Y>C0ZPw?(z~Jbe<-kIsJQhw zq2lxj1{P!Um&=qc4VPG_nVu_HP)^PPJ)78c+22MvM7yc^Md%0Zcbue?1m;xKa zu1*dCIiS|$lm-Sji?c^2Op08>n6kmhz<5dOvn5On5_-&HEDbM#E{9nt-oVJf$B~-h literal 0 HcmV?d00001 diff --git a/pkg/mutate/testdata/Test_SquashfsLayer/AUFSBlob_tar2sqfs.golden b/pkg/mutate/testdata/Test_SquashfsLayer/AUFSBlob_tar2sqfs.golden new file mode 100644 index 0000000000000000000000000000000000000000..2b424cb9c91552ee76fa704a0656553c942e2ea3 GIT binary patch literal 4096 zcmc~OE-YqYfB_~jgONdyA%KaI0VJLPm3j!JZ$jz+Pymx(fF=%;w_~Wdm7ElD=mZ0c zvH8p8O_B~DV_cw2@MQx8fT9jIJ+Qx-a__cKUrHj3fb6L4bJjS^FMvYvy|ag5=+;T6fs7I*X$g< zEDbN285k}@9W+XfhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kinXb3O>0L5xd AVgLXD literal 0 HcmV?d00001 diff --git a/pkg/mutate/testdata/Test_SquashfsLayer/AUFSBlob_tar2sqfs_convertWhiteout.golden b/pkg/mutate/testdata/Test_SquashfsLayer/AUFSBlob_tar2sqfs_convertWhiteout.golden new file mode 100644 index 0000000000000000000000000000000000000000..f7679aa389837f56c949114b8904c889807e1681 GIT binary patch literal 4096 zcmc~OE-YqdfB_~jgONdyA%KCAfd$BOfJ$*QLKp|2{LfH20ZPw;(*Mzb1w+NH#|agu zPcX0;o4;J%B;n(5<_rU)ffP%GvKp z$pLjHr!+9QS)4sGVN&E0#*__42F6QLpDkfxkkDfmV`+E^boef)z2Xgw415gvWvN9u ziIsZ!1&M{FsURm;3N%1f06oOOAP&SQpaM`IF+7AR7^O!;U^E0qLtr!nMnhmU1V%$( WGz3ONU^E0qLtr!nMnhn@h5!I9D?{u6 literal 0 HcmV?d00001 diff --git a/pkg/mutate/testdata/Test_SquashfsLayer/HelloWorldBlob_sqfstar_convertWhiteout.golden b/pkg/mutate/testdata/Test_SquashfsLayer/HelloWorldBlob_sqfstar_convertWhiteout.golden new file mode 100644 index 0000000000000000000000000000000000000000..e74b9981932976c9fcbc8fd25df04526c264cfa5 GIT binary patch literal 4096 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 F@Gn&?MUDUf literal 0 HcmV?d00001 diff --git a/pkg/mutate/testdata/Test_SquashfsLayer/HelloWorldBlob_tar2sqfs_convertWhiteout.golden b/pkg/mutate/testdata/Test_SquashfsLayer/HelloWorldBlob_tar2sqfs_convertWhiteout.golden new file mode 100644 index 0000000000000000000000000000000000000000..ec6f5e4b5a6273c07478fc2f76c2ae8f86159c64 GIT binary patch literal 4096 zcmeHKS2P?7yERCNHX;c|iB1y6L~k*OFh`4;i0&Y|2!iNDuM-hMbfPnQ8^aKz9wO?D zUZa-?#xR(B&bqlTcm2=*)7k4=Ywx}G_q5-(qnC#lIobb#oDA@{1d!b%vnB_SQU0yD ze@BUQ|0^PYGwC<@e|w$Z?EC-nw=;XDL9dM_?U<03|W0%fG|hNddRAa>@t!)tjS z>Q(IuSEexo4~{YAd6`t#kkj^NF_W@$7;$kSqwRDxYf)|^$7{cJnqT3-s{O-;?4V>$ zu$Jp&ug1D*-KiPIgLxOsLj^^vyh@RQ3rahUz4c?$VT)}!9J#-_z~q88Tgi~@u<<%Q zOz~K}`#iJwJJvlC-@+{0_i1yd;Y?o0i4Td~iwJ&}hnaM9e{({U=m4v|d&lug=t=Yo z6072U?c5NEZZzuwhuRKAzzy8pc}&qr#Vpugb^;HD<2t|__G9Ph*c?i(H=OqbG(QHS z+9{3-RKk#_jGWVni2Q?AJ@>UREa_Fd%7A}&Jxr66$}09=7N8x7c-mkq($kW)X#>%x zn1X38R?FBG6n>&aWD-8qYr9J;9p{J;T{oQIZHkuX85>LNON-ZB-K^S|C2B8Z)3(}> zd*0(*g=cmg5p?cVMqrOO!1@&b9r^^A4kte-fpq!i+=2JoURBZdXy8B#a&H)sATmYz z{B%U}5{b~-*l@o5L+4hJ^}t)N zKqebneCM@?Y-{zUnq~}xxP507h@8_FPv`oP4)H!=4Z`0|%RYUkCi8QNvy{S(8B`LR zM2`{yjo*ahfNA1ZNf-jYM90dqTxLHC>UEVPg=P?wJv3yy(YctVD!$efU@+7iE^9d8 zG4qi_OXjU#nY?r)lc{_VP+{bfrv8U3e!J*883+-XjesbkduTo@7@zD~Ru(jS|E7zsbJiaGBsN6QsE%2x0)N_knig%4*oGJ~)CsiKlYPq=&e4GMZNIu0#o8h@%cht?AVs&yZ>LAnlx(zNgDHYnjs{KK=C zzelI-5e0Eq@-VdpA@)8uu1p`pZSvzQPHBE$uh=i0TLaCE?6^cfUw^;}-5TwAUf%q} zw+bh8I=-8#OYAw>(d`p9ARh*0Z0DW<9Owg7f$w$((_aKS!C+DARC_2Lmyv=!fE#NA z%e_WNKe72U>RrwpK~Qvn)+)u6J#(Mo*ONF$$9|_;#$&H z?K}_oYbB%$#SysCb^3_#nDkWUMT0?#53OT&UUrSshRUq%xVUeI_vmny2P2pJL`$-0 z7#o`a+p^yroNePXi_;Wu(mYQUe`*<^t@QogNyBLxsu!;pkYr+D%M|xVf@CP1CN2@K@JQo znw&y3PHXf@Y^YAZu%q0?ncDb>ltxUh4W1CIHKczuGQqT~yUOsr&SN@Z1TtD?8mrJ# zV>xrO-ALG#DPSL=@-+NoNKNE5TKWgAy~Qi*lYW$Ow$bo@!+4^CT(18Ra(X~$h-hYC zEhV~ap&I4OxGHXQ(g?vjjCthJy93rJl$_D`(-&V)qHNLQ)ALB1$-khR8iCszCn0~j zx!yi5KL|K(qZOd=-kPumWt*DwgKWL}35bC8fi4qM7@6?&{g4%y2%Y`fTeE z{POV%BdpI~|5iexw>)6{tbb)9HCj=A-{};`G2nQVd{~BN_FjP(E6yZ-7=@~dKM7kI zv(()4%ty>SR&l8+Dq*yG)o}66h=b^sc+spa{Jz7~zPF|#cG)}{j%5#jS21gX@ti65 zJ`}QwMbat|`-)S);B|^@D`Lv=@?}&6sGvhkVIfq=mCxa;syT{|mBBqM;iw=9B%Tt9 zgpAZJg!>TGF~KG`K@h-d&|o2vLz{~$WLBl*I_19IeVuu@uec}=KBATGQN@v^^rp;dPOn>I$J+f- z{3T$bjv9$gHkGWEuI4Y>m=l?iB(u1iHQMbVRH7U$0hH4)T%j@aP7asJV}Pe7=2>{bIFM1BoCc(TsCp%x1zR-q?9IaCG$B zo`eyS5oM)7wnX!q9gJD$FgWd4sN84UFis<+$hq~~!|+meFMTZDIr09*t^?{(elRNb z9AG-qvCxZE-r1_#p@u(tag?zk={QM*NQq{!P(uLiGoVuQv7jB%sMVy>(YxB-tq{1=Fywo{uq}B-?A}6q7A7-{~Emi)JM$eaK#VcMB*%oIFtAe5no>rHhhg%9C@~L3lgTzWC{PVJ8 zw%|t8g1V*7@SeV1HSOHWX@tZ=U%YQRia5w6;dg4hOIfW-da09mb+l6CZ+s?>vzz8S z8HRRmM@yj(=s1^jYyI#eCt?6<|3V_cJB zot2;WfRm?6S?T(WUM8&kI z+z{3`dB(lw)q1B%TA7bsMIUo=leI2m*|vO)sSPWzU~*qN)LPVL#K0ti8B5fia^~5046M;-1gk4ykxRd>q$< zV@i~4Jin8olF(2Nt}1R*lPiz8dfp;I=NtDOOG(v*EO(i{W!UxIHRm?3+kNW9&wCh} z*)ObKj(SpH@?4PBvsh`WZqNR>Y_@scUfyAv$I+dVFzckbU0Pm3RV~fE|7?3UQ0 zH5dAds#_eja;4W9`#ft0#=a5usmpCDdNxyR0iRqfLOI8dZ$BBVa{iJbK&w@J@&|Pj z%d0vg3BRBC^HTBPyl29B+YkI5=3ALDYeT=J|+cmn=1CMeS!v1BDEjQi5h zNOXM2Y$TKymY@^it~a6Zen&rowZKs&<=5uBhXzg;0Yc5*V7Zx&fnlFd=>hfMjY$&O zma@x4r)&OX0aExJ$B+PML^~BOszf<(>M%<$;Jww+GF1sJj=6)Vcgr90mg82(0dt`{ z%osMdMMG;w5Fv3iZ+8P}?qNN_|KYA6I;#R+=8kYuRioV%1K6~%UuRv(`oO`obKT1A z=;f5!E0^rEHoT!p3#7qAUe^dji<<;{rmzmnEHi{i z=_{0rct{f}!-SB4j-RvAB@IhMkKE~hNO#Qj8sA7cYZbWwn-C6kQg%m8l~Q^RhD;&K z4ruDZ(z6Q^H)8l;i0wc%R~nEz(AJ5?U}y2PCVq=HtFs;CXnER@u3oE}!}<<#k{)yL zBa)iS>ySv&-KJArbAxR4}u0#sDW-l%jzzPL>7`Z#Ue!ZcE;9*@K-+>3Xtr22ssDv r??B5sG?Z_Sk&TQl2=LJU_e`J+{byE${C5B7fAMbv|2FVnYv6wX06#(W literal 0 HcmV?d00001 diff --git a/test/images/aufs-docker-v2-manifest/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a b/test/images/aufs-docker-v2-manifest/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a new file mode 100644 index 0000000..902a739 --- /dev/null +++ b/test/images/aufs-docker-v2-manifest/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a @@ -0,0 +1 @@ +{"architecture":"arm64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/hello"],"Image":"sha256:cc0fff24c4ece63ade5d9f549e42c926cf569112c4f5c439a4a57f3f33f5588b","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"b2af51419cbf516f3c99b877a64906b21afedc175bd3cd082eb5798e2f277bb4","container_config":{"Hostname":"b2af51419cbf","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"/hello\"]"],"Image":"sha256:cc0fff24c4ece63ade5d9f549e42c926cf569112c4f5c439a4a57f3f33f5588b","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2022-03-19T16:12:58.923371954Z","docker_version":"20.10.12","history":[{"created":"2022-03-19T16:12:58.834095198Z","created_by":"/bin/sh -c #(nop) COPY file:a79dd5bda1e77203401956a93401d3aef45221fc750295a4291896f3386f4f54 in / "},{"created":"2022-03-19T16:12:58.923371954Z","created_by":"/bin/sh -c #(nop) CMD [\"/hello\"]","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:efb53921da3394806160641b72a2cbd34ca1a9a8345ac670a85a04ad3d0e3507"]},"variant":"v8"} \ No newline at end of file diff --git a/test/images/aufs-docker-v2-manifest/blobs/sha256/6c9c1b8d1adba535a40046b52c051cccf33d85f60827bd269c978fd95f05c3c9 b/test/images/aufs-docker-v2-manifest/blobs/sha256/6c9c1b8d1adba535a40046b52c051cccf33d85f60827bd269c978fd95f05c3c9 new file mode 100644 index 0000000..aa59ff8 --- /dev/null +++ b/test/images/aufs-docker-v2-manifest/blobs/sha256/6c9c1b8d1adba535a40046b52c051cccf33d85f60827bd269c978fd95f05c3c9 @@ -0,0 +1,16 @@ +{ + "schemaVersion": 2, + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "config": { + "mediaType": "application/vnd.docker.container.image.v1+json", + "size": 2, + "digest": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a" + }, + "layers": [ + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 252, + "digest": "sha256:da55812559dec81445c289c3832cee4a2f725b15aeb258791640185c3126b2bf" + } + ] + } \ No newline at end of file diff --git a/test/images/aufs-docker-v2-manifest/blobs/sha256/da55812559dec81445c289c3832cee4a2f725b15aeb258791640185c3126b2bf b/test/images/aufs-docker-v2-manifest/blobs/sha256/da55812559dec81445c289c3832cee4a2f725b15aeb258791640185c3126b2bf new file mode 100644 index 0000000000000000000000000000000000000000..ccadfc71033942813b3dac846de8ff38b8622581 GIT binary patch literal 252 zcmV2-_HexbjW&rKeiwc4;6oBD9%3dJ#xZCp-b`gZi z3(?!>iXbQ`!3EL(fefaw1OLo&m0U3j&c#)NlgIT=iFIOvkz|!=+4b2Uj`N=p1AA*H z^!HMT(#8CvJp23pvHPZO*6Gfe)L&fu|9!CZ2LJ#70000000000_{0Srg##o2C;$N6 CVR8ll literal 0 HcmV?d00001 diff --git a/test/images/aufs-docker-v2-manifest/index.json b/test/images/aufs-docker-v2-manifest/index.json new file mode 100644 index 0000000..fe1cd39 --- /dev/null +++ b/test/images/aufs-docker-v2-manifest/index.json @@ -0,0 +1 @@ +{"schemaVersion":2,"mediaType":"application/vnd.oci.image.index.v1+json","manifests":[{"mediaType":"application/vnd.docker.distribution.manifest.v2+json","size":525,"digest":"sha256:6c9c1b8d1adba535a40046b52c051cccf33d85f60827bd269c978fd95f05c3c9","platform":{"architecture":"arm64","os":"linux","variant":"v8"}}]} \ No newline at end of file diff --git a/test/images/aufs-docker-v2-manifest/oci-layout b/test/images/aufs-docker-v2-manifest/oci-layout new file mode 100644 index 0000000..224a869 --- /dev/null +++ b/test/images/aufs-docker-v2-manifest/oci-layout @@ -0,0 +1,3 @@ +{ + "imageLayoutVersion": "1.0.0" +} \ No newline at end of file