/
tar.go
86 lines (82 loc) · 2.22 KB
/
tar.go
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
package tar
import (
"archive/tar"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"syscall"
)
type insecureLinkError error
// ExtractTar extracts a tarball (from a tar.Reader) into the given directory
func ExtractTar(tr *tar.Reader, dir string) error {
um := syscall.Umask(0)
defer syscall.Umask(um)
for {
hdr, err := tr.Next()
switch err {
case io.EOF:
return nil
case nil:
p := filepath.Join(dir, hdr.Name)
fi := hdr.FileInfo()
typ := hdr.Typeflag
switch {
case typ == tar.TypeReg || typ == tar.TypeRegA:
f, err := os.OpenFile(p, os.O_CREATE|os.O_RDWR, fi.Mode())
if err != nil {
f.Close()
return err
}
_, err = io.Copy(f, tr)
if err != nil {
f.Close()
return err
}
f.Close()
case typ == tar.TypeDir:
if err := os.MkdirAll(p, fi.Mode()); err != nil {
return err
}
case typ == tar.TypeLink:
dest := filepath.Join(filepath.Dir(p), hdr.Linkname)
if !strings.HasPrefix(dest, dir) {
return insecureLinkError(fmt.Errorf("insecure link %q -> %q", p, hdr.Linkname))
}
if err := os.Link(hdr.Linkname, p); err != nil {
return err
}
case typ == tar.TypeSymlink:
dest := filepath.Join(filepath.Dir(p), hdr.Linkname)
if !strings.HasPrefix(dest, dir) {
return insecureLinkError(fmt.Errorf("insecure symlink %q -> %q", p, hdr.Linkname))
}
if err := os.Symlink(hdr.Linkname, p); err != nil {
return err
}
case typ == tar.TypeChar:
dev := makedev(int(hdr.Devmajor), int(hdr.Devminor))
mode := uint32(fi.Mode()) | syscall.S_IFCHR
if err := syscall.Mknod(p, mode, dev); err != nil {
return err
}
case typ == tar.TypeBlock:
dev := makedev(int(hdr.Devmajor), int(hdr.Devminor))
mode := uint32(fi.Mode()) | syscall.S_IFBLK
if err := syscall.Mknod(p, mode, dev); err != nil {
return err
}
// TODO(jonboulle): implement other modes
default:
return fmt.Errorf("unsupported type: %v", typ)
}
default:
return fmt.Errorf("error extracting tarball: %v", err)
}
}
}
// makedev mimics glib's gnu_dev_makedev
func makedev(major, minor int) int {
return (minor & 0xff) | (major & 0xfff << 8) | int((uint64(minor & ^0xff) << 12)) | int(uint64(major & ^0xfff)<<32)
}