func()

in lambda/internal/tarfile/src.go [118:184]


func (s *S3FileSource) prepareLayerData(tarManifest *ManifestItem, parsedConfig *manifest.Schema2Image) (map[digest.Digest]*layerInfo, error) {
	// Collect layer data available in manifest and config.
	if len(tarManifest.Layers) != len(parsedConfig.RootFS.DiffIDs) {
		return nil, errors.Errorf("Inconsistent layer count: %d in manifest, %d in config", len(tarManifest.Layers), len(parsedConfig.RootFS.DiffIDs))
	}
	knownLayers := map[digest.Digest]*layerInfo{}
	unknownLayerSizes := map[string]*layerInfo{} // Points into knownLayers, a "to do list" of items with unknown sizes.
	for i, diffID := range parsedConfig.RootFS.DiffIDs {
		if _, ok := knownLayers[diffID]; ok {
			// Apparently it really can happen that a single image contains the same layer diff more than once.
			// In that case, the diffID validation ensures that both layers truly are the same, and it should not matter
			// which of the tarManifest.Layers paths is used; (docker save) actually makes the duplicates symlinks to the original.
			continue
		}
		layerPath := path.Clean(tarManifest.Layers[i])
		if _, ok := unknownLayerSizes[layerPath]; ok {
			return nil, errors.Errorf("Layer tarfile %s used for two different DiffID values", layerPath)
		}
		li := &layerInfo{ // A new element in each iteration
			path: layerPath,
			size: -1,
		}
		knownLayers[diffID] = li
		unknownLayerSizes[layerPath] = li
	}

	// Scan the tar file to collect layer sizes.
	t := tar.NewReader(s.s3fileReader.s3file)
	for {
		h, err := t.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, err
		}
		layerPath := path.Clean(h.Name)
		// FIXME: Cache this data across images in Reader.
		if li, ok := unknownLayerSizes[layerPath]; ok {
			// Since GetBlob will decompress layers that are compressed we need
			// to do the decompression here as well, otherwise we will
			// incorrectly report the size. Pretty critical, since tools like
			// umoci always compress layer blobs. Obviously we only bother with
			// the slower method of checking if it's compressed.
			uncompressedStream, isCompressed, err := compression.AutoDecompress(t)
			if err != nil {
				return nil, errors.Wrapf(err, "Error auto-decompressing %s to determine its size", layerPath)
			}
			defer uncompressedStream.Close()

			uncompressedSize := h.Size
			if isCompressed {
				uncompressedSize, err = io.Copy(ioutil.Discard, uncompressedStream)
				if err != nil {
					return nil, errors.Wrapf(err, "Error reading %s to find its size", layerPath)
				}
			}
			li.size = uncompressedSize
			delete(unknownLayerSizes, layerPath)
		}
	}
	if len(unknownLayerSizes) != 0 {
		return nil, errors.Errorf("Some layer tarfiles are missing in the tarball") // This could do with a better error reporting, if this ever happened in practice.
	}

	return knownLayers, nil
}