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 }