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  }