File: tgz.go

package info (click to toggle)
golang-github-go-git-go-git-fixtures 4.3.1~git20240304.46037e5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 47,916 kB
  • sloc: makefile: 9
file content (133 lines) | stat: -rw-r--r-- 2,530 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package tgz

import (
	"archive/tar"
	"compress/gzip"
	"fmt"
	"io"
	"os"

	"github.com/go-git/go-billy/v5"
	"github.com/go-git/go-billy/v5/util"
)

const (
	useDefaultTempDir = ""
	tmpPrefix         = "tmp-tgz-"
)

// Extract decompress a gziped tarball into a new temporal directory
// created just for this purpose.
//
// On success, the path of the newly created directory and a nil error
// is returned.
//
// A non-nil error is returned if the method fails to complete. The
// returned path will be an empty string if no information was extracted
// before the error and the temporal directory has not been created.
// Otherwise, a non-empty string with the temporal directory holding
// whatever information was extracted before the error is returned.
func Extract(fs billy.Filesystem, tgz string) (d billy.Filesystem, err error, cleanup func()) {
	dirName := ""
	cleanup = func() {
		if dirName != "" {
			_ = os.RemoveAll(dirName)
		}
	}

	f, err := fs.Open(tgz)
	if err != nil {
		return
	}

	defer func() {
		errClose := f.Close()
		if err == nil {
			err = errClose
		}
	}()

	dirName, err = util.TempDir(fs, useDefaultTempDir, tmpPrefix)
	if err != nil {
		return
	}

	tar, err := zipTarReader(f)
	if err != nil {
		return
	}

	if err = unTar(fs, tar, dirName); err != nil {
		return
	}

	d, err = fs.Chroot(dirName)
	return
}

func zipTarReader(r io.Reader) (*tar.Reader, error) {
	zip, err := gzip.NewReader(r)
	if err != nil {
		return nil, err
	}

	return tar.NewReader(zip), nil
}

func unTar(fs billy.Filesystem, src *tar.Reader, dstPath string) error {
	for {
		header, err := src.Next()
		if err != nil {
			if err == io.EOF {
				break
			}
			return err
		}

		dst := dstPath + "/" + header.Name
		mode := os.FileMode(header.Mode)
		switch header.Typeflag {
		case tar.TypeDir:
			err := fs.MkdirAll(dst, mode)
			if err != nil {
				return err
			}
		case tar.TypeReg:
			err := makeFile(fs, dst, mode, src)
			if err != nil {
				return err
			}
		default:
			return fmt.Errorf("Unable to untar type : %c in file %s",
				header.Typeflag, header.Name)
		}
	}

	return nil
}

func makeFile(fs billy.Filesystem, path string, mode os.FileMode, contents io.Reader) (err error) {
	w, err := fs.Create(path)
	if err != nil {
		return err
	}
	defer func() {
		errClose := w.Close()
		if err == nil {
			err = errClose
		}
	}()

	_, err = io.Copy(w, contents)
	if err != nil {
		return err
	}

	if fs, ok := fs.(billy.Change); ok {
		if err = fs.Chmod(path, mode); err != nil {
			return err
		}
	}

	return nil
}