-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathphar.go
More file actions
152 lines (135 loc) · 3.68 KB
/
Copy pathphar.go
File metadata and controls
152 lines (135 loc) · 3.68 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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package magic
import (
"bytes"
"encoding/binary"
)
const (
pharHaltCompiler = "__HALT_COMPILER();"
pharManifestFixedLen = 18
pharEntryFixedLen = 28
pharEntryMinLen = pharEntryFixedLen + 1
pharManifestMaxLen = 100 << 20
pharAPIVersionMask = 0xfff0
pharMinimumAPIVersion = 0x1000
pharManifestLengthSize = 4
pharClosingTagSize = 3
)
type pharStatus uint8
const (
pharNotFound pharStatus = iota
pharIncomplete
pharValid
)
func nativePHAR(data []byte) pharStatus {
stubEnd := bytes.Index(data, []byte(pharHaltCompiler))
if stubEnd < 0 {
return pharNotFound
}
stubEnd += len(pharHaltCompiler)
manifestOffset, status := pharManifestOffset(data, stubEnd)
if status != pharValid {
return status
}
if len(data)-manifestOffset < pharManifestLengthSize {
return pharIncomplete
}
manifestLength := binary.LittleEndian.Uint32(data[manifestOffset:])
if manifestLength < pharManifestFixedLen || manifestLength > pharManifestMaxLen {
return pharNotFound
}
manifestStart := manifestOffset + pharManifestLengthSize
manifestEnd64 := uint64(manifestStart) + uint64(manifestLength)
if manifestEnd64 > uint64(len(data)) {
return pharIncomplete
}
manifestEnd := int(manifestEnd64)
manifest := data[manifestStart:manifestEnd]
entryCount := binary.LittleEndian.Uint32(manifest)
if entryCount == 0 {
return pharNotFound
}
apiVersion := binary.BigEndian.Uint16(manifest[4:6])
if apiVersion&pharAPIVersionMask < pharMinimumAPIVersion {
return pharNotFound
}
offset := 10 // entry count, API version, and global flags
aliasLength, ok := pharUint32(manifest, &offset)
if !ok || !pharSkip(manifest, &offset, aliasLength) {
return pharNotFound
}
metadataLength, ok := pharUint32(manifest, &offset)
if !ok || !pharSkip(manifest, &offset, metadataLength) {
return pharNotFound
}
if uint64(entryCount)*pharEntryMinLen > uint64(len(manifest)-offset) {
return pharNotFound
}
var payloadLength uint64
for range entryCount {
filenameLength, ok := pharUint32(manifest, &offset)
if !ok || filenameLength == 0 || !pharSkip(manifest, &offset, filenameLength) {
return pharNotFound
}
if len(manifest)-offset < pharEntryFixedLen-pharManifestLengthSize {
return pharNotFound
}
compressedSize := binary.LittleEndian.Uint32(manifest[offset+8:])
metadataLength := binary.LittleEndian.Uint32(manifest[offset+20:])
offset += pharEntryFixedLen - pharManifestLengthSize
if !pharSkip(manifest, &offset, metadataLength) {
return pharNotFound
}
payloadLength += uint64(compressedSize)
}
if uint64(manifestEnd)+payloadLength > uint64(len(data)) {
return pharIncomplete
}
return pharValid
}
func pharManifestOffset(data []byte, offset int) (int, pharStatus) {
if offset >= len(data) {
return 0, pharIncomplete
}
if data[offset] != ' ' && data[offset] != '\n' {
return offset, pharValid
}
if len(data)-offset < pharClosingTagSize {
return 0, pharIncomplete
}
if data[offset+1] != '?' || data[offset+2] != '>' {
return offset, pharValid
}
offset += pharClosingTagSize
if offset >= len(data) {
return 0, pharIncomplete
}
switch data[offset] {
case '\n':
offset++
case '\r':
if offset+1 >= len(data) {
return 0, pharIncomplete
}
if data[offset+1] != '\n' {
return 0, pharNotFound
}
offset += 2
}
return offset, pharValid
}
func pharUint32(data []byte, offset *int) (uint32, bool) {
if len(data)-*offset < pharManifestLengthSize {
return 0, false
}
value := binary.LittleEndian.Uint32(data[*offset:])
*offset += 4
return value, true
}
func pharSkip(data []byte, offset *int, length uint32) bool {
end := uint64(*offset) + uint64(length)
if end > uint64(len(data)) {
return false
}
*offset = int(end)
return true
}