-
Notifications
You must be signed in to change notification settings - Fork 19
Expand file tree
/
Copy pathcpio.go
More file actions
119 lines (95 loc) · 3.18 KB
/
Copy pathcpio.go
File metadata and controls
119 lines (95 loc) · 3.18 KB
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
package xtractr
import (
"compress/gzip"
"errors"
"fmt"
"io"
"path/filepath"
"github.com/cavaliergopher/cpio"
)
// ExtractCPIOGzip extracts a gzip-compressed cpio archive (cpgz).
func ExtractCPIOGzip(xFile *XFile) (size uint64, filesList []string, err error) {
compressedFile, stat, err := openStatFile(xFile.FilePath)
if err != nil {
return 0, nil, err
}
defer compressedFile.Close()
defer xFile.newProgress(0, uint64(stat.Size()), 0).done()
zipStream, err := gzip.NewReader(xFile.prog.reader(compressedFile))
if err != nil {
return 0, nil, fmt.Errorf("gzip.NewReader: %w", err)
}
files, err := xFile.uncpio(zipStream)
closeNamed(zipStream, &err)
return xFile.prog.Wrote, files, err
}
// ExtractCPIO extracts a .cpio file.
func ExtractCPIO(xFile *XFile) (size uint64, filesList []string, err error) {
fileReader, stat, err := openStatFile(xFile.FilePath)
if err != nil {
return 0, nil, err
}
defer fileReader.Close()
defer xFile.newProgress(uint64(stat.Size()), uint64(stat.Size()), 0).done()
files, err := xFile.uncpio(xFile.prog.reader(fileReader))
return xFile.prog.Wrote, files, err
}
func (x *XFile) uncpio(reader io.Reader) ([]string, error) {
zipReader := cpio.NewReader(reader)
files := []string{}
for {
zipFile, err := zipReader.Next()
if errors.Is(err, io.EOF) {
return files, nil
} else if err != nil {
return nil, fmt.Errorf("cpio Next() failed: %w", err)
}
fSize, err := x.uncpioFile(zipFile, zipReader)
if err != nil {
return files, fmt.Errorf("%s: %w", x.FilePath, err)
}
files = append(files, filepath.Join(x.OutputDir, zipFile.Name))
x.Debugf("Wrote archived file: %s (%d bytes), total: %d files and %d bytes",
zipFile.Name, fSize, x.prog.Files, x.prog.Wrote)
}
}
func (x *XFile) uncpioFile(cpioFile *cpio.Header, cpioReader *cpio.Reader) (uint64, error) {
file := &file{
Path: x.clean(cpioFile.Name),
Data: cpioReader,
FileMode: cpioFile.FileInfo().Mode(),
DirMode: x.DirMode,
Mtime: cpioFile.ModTime,
}
if !x.pathWithinOutput(file.Path) {
// The file being written is trying to write outside of the base path. Malicious archive?
return 0, fmt.Errorf("%s: %w: %s (from: %s)", cpioFile.FileInfo().Name(), ErrInvalidPath, file.Path, cpioFile.Name)
}
if cpioFile.Mode.IsDir() || cpioFile.FileInfo().IsDir() {
err := x.mkDir(file.Path, cpioFile.FileInfo().Mode(), cpioFile.ModTime)
if err != nil {
return 0, fmt.Errorf("making cpio dir: %w", err)
}
return 0, nil
}
// This turns hard links into symlinks.
if cpioFile.Linkname != "" {
// The link's parent folder may not have its own entry in the archive.
err := x.mkDir(filepath.Dir(file.Path), x.DirMode, cpioFile.ModTime)
if err != nil {
return 0, fmt.Errorf("making cpio link parent dir: %w", err)
}
err = x.createSymlink(file.Path, cpioFile.Linkname)
if err != nil {
return 0, fmt.Errorf("%s: %w", cpioFile.FileInfo().Name(), err)
}
return 0, nil
}
// This should turn non-regular files into empty files.
// ie. sockets, block, character and fifo devices.
s, err := x.write(file)
if err != nil {
return s, fmt.Errorf("%s: %w: %s (from: %s)", cpioFile.FileInfo().Name(), err, file.Path, cpioFile.Name)
}
return s, nil
}