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