package compression import ( "archive/tar" "fmt" "io" "log" "os" "path/filepath" "github.com/xi2/xz" ) // DecompressTarXz originally from // https://github.com/fergusstrange/embedded-postgres/blob/9f87ef1ab3ab1faff3d85dddc9f86e30f3f06dc9/decompression.go#L23 func DecompressTarXz(txzFilePath, outputPath string) error { tarFile, err := os.Open(txzFilePath) if err != nil { return errorUnableToExtract(txzFilePath, outputPath) } defer func() { if err := tarFile.Close(); err != nil { log.Printf("could not close txz tar file: %s, error %v\n", txzFilePath, err) } }() xzReader, err := xz.NewReader(tarFile, 0) if err != nil { return errorUnableToExtract(txzFilePath, outputPath) } readNext, reader := defaultTarReader(xzReader) for { header, err := readNext() if err == io.EOF { return nil } if err != nil { return errorExtractingPostgres(err) } targetPath := filepath.Join(outputPath, header.Name) // nolint:gosec if err := os.MkdirAll(filepath.Dir(targetPath), 0755); err != nil { return errorExtractingPostgres(err) } switch header.Typeflag { case tar.TypeReg: fsMode := os.FileMode(header.Mode) /* #nosec G115 */ outFile, err := os.OpenFile(targetPath, os.O_CREATE|os.O_RDWR, fsMode) if err != nil { return errorExtractingPostgres(err) } if _, err := io.Copy(outFile, reader()); err != nil { return errorExtractingPostgres(err) } if err := outFile.Close(); err != nil { return errorExtractingPostgres(err) } case tar.TypeSymlink: if err := os.RemoveAll(targetPath); err != nil { return errorExtractingPostgres(err) } if err := os.Symlink(header.Linkname, targetPath); err != nil { return errorExtractingPostgres(err) } } } } func defaultTarReader(xzReader *xz.Reader) (func() (*tar.Header, error), func() io.Reader) { tarReader := tar.NewReader(xzReader) return func() (*tar.Header, error) { return tarReader.Next() }, func() io.Reader { return tarReader } } func errorUnableToExtract(cacheLocation, binariesPath string) error { return fmt.Errorf("unable to extract postgres archive %s to %s, if running parallel tests, configure RuntimePath to isolate testing directories", cacheLocation, binariesPath) } func errorExtractingPostgres(err error) error { return fmt.Errorf("unable to extract postgres archive: %s", err) }