package os import ( "archive/tar" "fmt" "io" "log" "os" "path/filepath" "strings" ) func TarPack(srcs []string, w io.Writer) error { tw := tar.NewWriter(w) defer tw.Close() // Loop through each source var fwErr error for _, s := range srcs { // ensure the src actually exists before trying to tar it if _, err := os.Stat(s); err != nil { return err } // walk path fwErr = filepath.Walk(s, func(path string, fi os.FileInfo, err error) error { if err != nil { return err } header, err := tar.FileInfoHeader(fi, fi.Name()) if err != nil { return err } var link string if fi.Mode()&os.ModeSymlink == os.ModeSymlink { if link, err = os.Readlink(path); err != nil { return err } log.Println("Symbolic link found at %s to %s", path, link) // Rewrite header for SymLink header, err = tar.FileInfoHeader(fi, link) if err != nil { return err } } header.Name = strings.TrimPrefix(filepath.ToSlash(path), "/") if err = tw.WriteHeader(header); err != nil { return err } if !fi.Mode().IsRegular() { log.Println("Directory found at %s", path) return nil } log.Println("File found at %s", path) file, err := os.Open(path) if err != nil { return err } defer file.Close() _, err = io.Copy(tw, file) return err }) if fwErr != nil { return fwErr } } return fwErr } func TarUnpack(dst string, r io.Reader) error { tr := tar.NewReader(r) for { header, err := tr.Next() switch { // if no more files are found return case err == io.EOF: return nil // return any other error case err != nil: return err // if the header is nil, just skip it (not sure how this happens) case header == nil: continue } // the target location where the dir/file should be created target := filepath.Join(dst, header.Name) // the following switch could also be done using fi.Mode(), not sure if there // a benefit of using one vs. the other. // fi := header.FileInfo() // check the file type switch header.Typeflag { // if its a symlink and it doesn't exist create it case tar.TypeSymlink: log.Println("Symlink found at %s", target) // Check if something already exists _, err := os.Stat(target) if err == nil { return fmt.Errorf("Failed to create symlink because file already exists at %s", target) } // Create the link log.Println("Creating link %s to %s", target, header.Linkname) err = os.Symlink(header.Linkname, target) if err != nil { log.Println("Failed creating link %s to %s", target, header.Linkname) return err } // if its a dir and it doesn't exist create it case tar.TypeDir: log.Println("Directory found at %s", target) if _, err := os.Stat(target); err != nil { if err := os.MkdirAll(target, 0755); err != nil { return err } } // if it's a file create it case tar.TypeReg: log.Println("File found at %s", target) f, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode)) if err != nil { return err } // copy over contents _, err = io.Copy(f, tr) // Explicitly close otherwise too many files remain open f.Close() if err != nil { return err } } } }