func FromReader()

in pkg/sdk/fsutil/targzfs/builders.go [46:142]


func FromReader(r io.Reader) (fs.FS, error) {
	gz, err := gzip.NewReader(r)
	if err != nil {
		return nil, fmt.Errorf("unable to open .tar.gz file: %w", err)
	}

	// Retrieve TAR content from GZIP
	var (
		tarContents      bytes.Buffer
		tarContentLength = int64(0)
	)

	// Chunked read with hard limit to prevent/reduce zipbomb vulnerability
	// exploitation.
	for {
		written, err := io.CopyN(&tarContents, gz, 1024)
		if err != nil {
			if errors.Is(err, io.EOF) {
				break
			}
			return nil, err
		}

		// Add to length
		tarContentLength += written

		// Check max size
		if tarContentLength > maxDecompressedSize {
			return nil, errors.New("the archive contains a too large content (>25MB)")
		}
	}

	// Close the gzip decompressor
	if err := gz.Close(); err != nil {
		return nil, fmt.Errorf("unable to close gzip reader: %w", err)
	}

	// TAR format reader
	tarReader := tar.NewReader(bytes.NewBuffer(tarContents.Bytes()))

	// Prepare in-memory filesystem.
	ret := &tarGzFs{
		files:       make(map[string]*tarEntry),
		rootEntries: make([]fs.DirEntry, 0, 10),
		rootEntry:   nil,
	}

	for {
		// Iterate on each file entry
		hdr, err := tarReader.Next()
		if errors.Is(err, io.EOF) {
			break
		} else if err != nil {
			return nil, fmt.Errorf("unable to read .tar.gz entry: %w", err)
		}

		// Clean file path.
		name := path.Clean(hdr.Name)
		if name == "." {
			continue
		}

		// Load content in memory
		var fileContents bytes.Buffer
		if _, err := io.CopyN(&fileContents, tarReader, maxDecompressedSize); err != nil {
			return nil, fmt.Errorf("unable to read .tar.gz entry: %w", err)
		}

		// Register file
		e := &tarEntry{
			h:       hdr,
			b:       fileContents.Bytes(),
			entries: nil,
		}

		// Add as file entry
		ret.files[name] = e

		// Create directories
		dir := path.Dir(name)
		if dir == "." {
			ret.rootEntries = append(ret.rootEntries, e)
		} else {
			if parent, ok := ret.files[dir]; ok {
				parent.entries = append(parent.entries, e)
			}
		}

		// Check file count limit
		if len(ret.files) > maxFileCount {
			return nil, errors.New("interrupted extraction, too many files in the archive")
		}
	}

	// No error
	return ret, nil
}