@@ -60,6 +60,22 @@ func isLayerTar(name string) bool {
6060 return slashes == 2 && strings .HasSuffix (name , "/layer.tar" )
6161}
6262
63+ // followSymlinkLayer returns actual layer name of the symlink layer.
64+ // It returns "deadbeeddeadbeef/layer.tar" if the name is like
65+ // "../deadbeeddeadbeef/layer.tar", and returns error if the name
66+ // is not in "../xxxx/layer.tar" format.
67+ func followSymlinkLayer (name string ) (string , error ) {
68+ parts := strings .Split (name , "/" )
69+ if len (parts ) != 3 || parts [0 ] != ".." {
70+ return "" , errors .New ("invalid symlink layer" )
71+ }
72+ name = strings .TrimPrefix (name , "../" )
73+ if ! isLayerTar (name ) {
74+ return "" , errors .New ("invalid layer tar" )
75+ }
76+ return name , nil
77+ }
78+
6379// isDotJSON returns true if name is like "deadbeefdeadbeef.json"
6480func isDotJSON (name string ) bool {
6581 slashes := len (strings .Split (name , "/" ))
@@ -97,9 +113,10 @@ func Import(ctx context.Context, client *containerd.Client, reader io.Reader) (_
97113
98114 tr := tar .NewReader (reader )
99115 var (
100- mfsts []manifestDotJSON
101- layers = make (map [string ]ocispec.Descriptor ) // key: filename (deadbeeddeadbeef/layer.tar)
102- configs = make (map [string ]imageConfig ) // key: filename (deadbeeddeadbeef.json)
116+ mfsts []manifestDotJSON
117+ symlinkLayers = make (map [string ]string ) // key: filename (deadbeeddeadbeef/layer.tar), value: linkname (targetlayerid/layer.tar)
118+ layers = make (map [string ]ocispec.Descriptor ) // key: filename (deadbeeddeadbeef/layer.tar)
119+ configs = make (map [string ]imageConfig ) // key: filename (deadbeeddeadbeef.json)
103120 )
104121 for {
105122 hdr , err := tr .Next ()
@@ -109,6 +126,14 @@ func Import(ctx context.Context, client *containerd.Client, reader io.Reader) (_
109126 if err != nil {
110127 return nil , errors .Wrap (err , "get next file" )
111128 }
129+ if hdr .Typeflag == tar .TypeSymlink && isLayerTar (hdr .Name ) {
130+ linkname , err := followSymlinkLayer (hdr .Linkname )
131+ if err != nil {
132+ return nil , errors .Wrapf (err , "follow symlink layer from %q to %q" , hdr .Name , hdr .Linkname )
133+ }
134+ symlinkLayers [hdr .Name ] = linkname
135+ continue
136+ }
112137 if hdr .Typeflag != tar .TypeReg && hdr .Typeflag != tar .TypeRegA {
113138 continue
114139 }
@@ -136,6 +161,13 @@ func Import(ctx context.Context, client *containerd.Client, reader io.Reader) (_
136161 continue
137162 }
138163 }
164+ for name , linkname := range symlinkLayers {
165+ desc , ok := layers [linkname ]
166+ if ! ok {
167+ return nil , errors .Errorf ("no target for symlink layer from %q to %q" , name , linkname )
168+ }
169+ layers [name ] = desc
170+ }
139171 var refs []string
140172 defer func () {
141173 if retErr == nil {
0 commit comments