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() }