golang.org/x/build@v0.0.0-20240506185731-218518f32b70/tarutil/tarutil.go (about)

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package tarutil contains utilities for working with tar archives.
     6  package tarutil
     7  
     8  import (
     9  	"archive/tar"
    10  	"compress/gzip"
    11  	"errors"
    12  	"io"
    13  )
    14  
    15  // FileList is a list of entries in a tar archive which acts
    16  // as a template to make .tar.gz io.Readers as needed.
    17  //
    18  // The zero value is a valid empty list.
    19  //
    20  // All entries must be added before calling OpenTarGz.
    21  type FileList struct {
    22  	files []headerContent
    23  }
    24  
    25  type headerContent struct {
    26  	header *tar.Header
    27  
    28  	// For regular files:
    29  	size    int64
    30  	content io.ReaderAt
    31  }
    32  
    33  // AddHeader adds a non-regular file to the FileList.
    34  func (fl *FileList) AddHeader(h *tar.Header) {
    35  	fl.files = append(fl.files, headerContent{header: h})
    36  }
    37  
    38  // AddRegular adds a regular file to the FileList.
    39  func (fl *FileList) AddRegular(h *tar.Header, size int64, content io.ReaderAt) {
    40  	fl.files = append(fl.files, headerContent{
    41  		header:  h,
    42  		size:    size,
    43  		content: content,
    44  	})
    45  }
    46  
    47  // TarGz returns an io.ReadCloser of a gzip-compressed tar file
    48  // containing the contents of the FileList.
    49  // All Add calls must happen before OpenTarGz is called.
    50  // Callers must call Close on the returned ReadCloser to release
    51  // resources.
    52  func (fl *FileList) TarGz() io.ReadCloser {
    53  	pr, pw := io.Pipe()
    54  	go func() {
    55  		err := fl.writeTarGz(pw)
    56  		pw.CloseWithError(err)
    57  	}()
    58  	return struct {
    59  		io.Reader
    60  		io.Closer
    61  	}{
    62  		pr,
    63  		funcCloser(func() error {
    64  			pw.CloseWithError(errors.New("tarutil: .tar.gz generation aborted with Close"))
    65  			return nil
    66  		}),
    67  	}
    68  }
    69  
    70  func (fl *FileList) writeTarGz(w *io.PipeWriter) error {
    71  	zw := gzip.NewWriter(w)
    72  	tw := tar.NewWriter(zw)
    73  	for _, f := range fl.files {
    74  		if err := tw.WriteHeader(f.header); err != nil {
    75  			return err
    76  		}
    77  		if f.content != nil {
    78  			if _, err := io.CopyN(tw, io.NewSectionReader(f.content, 0, f.size), f.size); err != nil {
    79  				return err
    80  			}
    81  		}
    82  	}
    83  
    84  	if err := tw.Close(); err != nil {
    85  		return err
    86  	}
    87  	return zw.Close()
    88  }
    89  
    90  // funcCloser implements io.Closer with a function.
    91  type funcCloser func() error
    92  
    93  func (fn funcCloser) Close() error { return fn() }