github.com/goreleaser/goreleaser@v1.25.1/pkg/archive/tar/tar.go (about) 1 // Package tar implements the Archive interface providing tar archiving. 2 package tar 3 4 import ( 5 "archive/tar" 6 "fmt" 7 "io" 8 "io/fs" 9 "os" 10 11 "github.com/goreleaser/goreleaser/pkg/config" 12 ) 13 14 // Archive as tar. 15 type Archive struct { 16 tw *tar.Writer 17 files map[string]bool 18 } 19 20 // New tar archive. 21 func New(target io.Writer) Archive { 22 return Archive{ 23 tw: tar.NewWriter(target), 24 files: map[string]bool{}, 25 } 26 } 27 28 // Copying creates a new tar with the contents of the given tar. 29 func Copying(source io.Reader, target io.Writer) (Archive, error) { 30 w := New(target) 31 r := tar.NewReader(source) 32 for { 33 header, err := r.Next() 34 if err == io.EOF || header == nil { 35 break 36 } 37 if err != nil { 38 return Archive{}, err 39 } 40 w.files[header.Name] = true 41 if err := w.tw.WriteHeader(header); err != nil { 42 return w, err 43 } 44 if _, err := io.Copy(w.tw, r); err != nil { 45 return w, err 46 } 47 } 48 return w, nil 49 } 50 51 // Close all closeables. 52 func (a Archive) Close() error { 53 return a.tw.Close() 54 } 55 56 // Add file to the archive. 57 func (a Archive) Add(f config.File) error { 58 if _, ok := a.files[f.Destination]; ok { 59 return &fs.PathError{Err: fs.ErrExist, Path: f.Destination, Op: "add"} 60 } 61 a.files[f.Destination] = true 62 info, err := os.Lstat(f.Source) // #nosec 63 if err != nil { 64 return fmt.Errorf("%s: %w", f.Source, err) 65 } 66 var link string 67 if info.Mode()&os.ModeSymlink != 0 { 68 link, err = os.Readlink(f.Source) // #nosec 69 if err != nil { 70 return fmt.Errorf("%s: %w", f.Source, err) 71 } 72 } 73 header, err := tar.FileInfoHeader(info, link) 74 if err != nil { 75 return fmt.Errorf("%s: %w", f.Source, err) 76 } 77 header.Name = f.Destination 78 if !f.Info.ParsedMTime.IsZero() { 79 header.ModTime = f.Info.ParsedMTime 80 } 81 if f.Info.Mode != 0 { 82 header.Mode = int64(f.Info.Mode) 83 } 84 if f.Info.Owner != "" { 85 header.Uid = 0 86 header.Uname = f.Info.Owner 87 } 88 if f.Info.Group != "" { 89 header.Gid = 0 90 header.Gname = f.Info.Group 91 } 92 if err = a.tw.WriteHeader(header); err != nil { 93 return fmt.Errorf("%s: %w", f.Source, err) 94 } 95 if info.IsDir() || info.Mode()&os.ModeSymlink != 0 { 96 return nil 97 } 98 file, err := os.Open(f.Source) // #nosec 99 if err != nil { 100 return fmt.Errorf("%s: %w", f.Source, err) 101 } 102 defer file.Close() 103 if _, err := io.Copy(a.tw, file); err != nil { 104 return fmt.Errorf("%s: %w", f.Source, err) 105 } 106 return nil 107 }