-
Notifications
You must be signed in to change notification settings - Fork 0
/
filedata.go
67 lines (61 loc) · 1.99 KB
/
filedata.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
package cab
import (
"encoding/binary"
"errors"
"io"
)
// openFileData returns an io.ReadCloser that verifies the checksum of the entry, if it exists.
func openFileData(entry *cabinetFileData) (io.ReadCloser, error) {
if entry.UncompressedBytes == 0 {
// continued entry, see https://docs.microsoft.com/en-us/previous-versions//bb267310(v=vs.85)#cfdata
return nil, errors.New("continued entries are not supported")
}
// Open a separate section reader for this file data reader to prevent race conditions on the underlying section reader
reader := io.NewSectionReader(entry.compressedData, 0, entry.compressedData.Size())
return &dataEntryReader{entry, reader, checksumWriter{}}, nil
}
type dataEntryReader struct {
entry *cabinetFileData
reader io.Reader
checksum checksumWriter
}
func (d *dataEntryReader) Read(data []byte) (n int, err error) {
n, err = d.reader.Read(data)
if d.entry.Checksum != 0 {
// Write data for later checksum check
d.checksum.Write(data[:n])
}
return n, err
}
func (d *dataEntryReader) Close() (err error) {
if d.entry.Checksum == 0 {
return nil // No checksum set for this entry
}
if d.checksum.Checksum == 0 { // No data read yet - no reason to verify checksum
return nil
}
// Copy remaining data from underlying reader to ensure we can verify the checksum
_, err = io.Copy(&d.checksum, d.reader)
if err != nil {
return err
}
d.checksum.Flush()
// After calculating the checksum over the data, we must feed the data entry header (minus checksum) to the checksum
binary.Write(&d.checksum, binary.LittleEndian, checksumlessEntry{
d.entry.CompressedBytes,
d.entry.UncompressedBytes,
})
d.checksum.Write(d.entry.reservedData)
d.checksum.Flush()
if d.checksum.Checksum != d.entry.Checksum && d.entry.Checksum != 0 {
return errors.New("checksum mismatch")
} else {
// Set checksum on entry to 0 to avoid checking it again later
d.entry.Checksum = 0
}
return nil
}
type checksumlessEntry struct {
CompressedBytes uint16
UncompressedBytes uint16
}