github.com/tiagovtristao/plz@v13.4.0+incompatible/tools/jarcat/tar/tar.go (about)

     1  // Package tar implements a tarball writer for Please.
     2  // This is not really dissimilar to the standard command-line tar utility,
     3  // but we would like some of the GNU tar flags which we can't rely on for all
     4  // platforms that we support, plus we'd like finer control over timestamps
     5  // and directories.
     6  package tar
     7  
     8  import (
     9  	"archive/tar"
    10  	"compress/gzip"
    11  	"io"
    12  	"os"
    13  	"path/filepath"
    14  	"strings"
    15  	"time"
    16  
    17  	"github.com/ulikunitz/xz"
    18  )
    19  
    20  // mtime is the time we attach for the modification time of all files.
    21  var mtime = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)
    22  
    23  // nobody is the usual uid / gid of the 'nobody' user.
    24  const nobody = 65534
    25  
    26  // Write writes a tarball to output with all the files found in inputDir.
    27  // If prefix is given the files are all placed into a single directory with that name.
    28  // If compress is true the output will be gzip-compressed.
    29  func Write(output string, srcs []string, prefix string, gzcompress, xzcompress bool) error {
    30  	f, err := os.Create(output)
    31  	if err != nil {
    32  		return err
    33  	}
    34  	defer f.Close()
    35  	if xzcompress {
    36  		w, err := xz.NewWriter(f)
    37  		if err != nil {
    38  			return err
    39  		}
    40  		defer w.Close()
    41  		return write(w, output, srcs, prefix)
    42  	} else if gzcompress {
    43  		w := gzip.NewWriter(f)
    44  		defer w.Close()
    45  		return write(w, output, srcs, prefix)
    46  	}
    47  	return write(f, output, srcs, prefix)
    48  }
    49  
    50  // write writes a tarball to the given writer with all the files found in inputDir.
    51  // If prefix is given the files are all placed into a single directory with that name.
    52  func write(w io.Writer, output string, srcs []string, prefix string) error {
    53  	tw := tar.NewWriter(w)
    54  	defer tw.Close()
    55  
    56  	for _, src := range srcs {
    57  		strip := filepath.Dir(src)
    58  		if err := filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
    59  			if err != nil {
    60  				return err
    61  			} else if info.IsDir() {
    62  				return nil // ignore directories
    63  			} else if abs, _ := filepath.Abs(path); abs == output {
    64  				return nil // don't write the output tarball into itself :)
    65  			}
    66  			hdr, err := tar.FileInfoHeader(info, "") // We don't write symlinks into plz-out/tmp, so the argument doesn't matter.
    67  			if err != nil {
    68  				return err
    69  			}
    70  			// Set name appropriately (recall that FileInfoHeader does not set the full path).
    71  			hdr.Name = strings.TrimLeft(strings.TrimPrefix(path, strip), "/")
    72  			if prefix != "" {
    73  				hdr.Name = filepath.Join(prefix, hdr.Name)
    74  			}
    75  			// Zero out all timestamps.
    76  			hdr.ModTime = mtime
    77  			hdr.AccessTime = mtime
    78  			hdr.ChangeTime = mtime
    79  			// Strip user/group ids.
    80  			hdr.Uid = nobody
    81  			hdr.Gid = nobody
    82  			hdr.Uname = "nobody"
    83  			hdr.Gname = "nobody"
    84  			// Setting the user/group write bits helps consistency of output.
    85  			hdr.Mode |= 0220
    86  			if err := tw.WriteHeader(hdr); err != nil {
    87  				return err
    88  			}
    89  			f, err := os.Open(path)
    90  			if err != nil {
    91  				return err
    92  			}
    93  			defer f.Close()
    94  			_, err = io.Copy(tw, f)
    95  			return err
    96  		}); err != nil {
    97  			return err
    98  		}
    99  	}
   100  	return nil
   101  }