-
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
-
153
-
154
-
155
-
156
-
157
-
158
-
159
-
160
-
161
-
162
-
163
-
164
-
165
-
166
-
167
-
168
-
169
-
170
-
171
-
172
-
173
-
174
-
175
-
176
-
177
-
178
-
179
-
180
-
181
-
182
-
183
-
184
-
185
-
186
-
187
-
188
-
189
-
190
-
191
-
192
-
193
-
194
-
195
-
196
-
197
-
198
-
199
-
200
-
201
-
202
-
203
-
204
-
205
-
206
-
207
-
208
-
209
-
210
-
211
-
212
-
213
-
214
-
215
-
216
-
217
-
218
-
219
-
220
-
221
-
222
-
223
-
224
-
225
-
226
-
227
-
228
-
229
-
230
-
231
-
232
-
233
-
234
-
235
-
236
-
237
-
238
-
239
-
240
-
241
-
242
-
243
-
244
-
245
-
246
-
247
-
248
-
249
-
250
-
251
-
252
-
253
-
254
-
255
-
256
-
257
-
258
-
259
-
260
-
261
-
262
-
263
-
264
-
265
-
266
-
267
-
268
-
269
-
270
-
271
-
272
-
273
-
274
-
275
-
276
-
277
-
278
-
279
-
280
-
281
-
282
-
283
-
284
-
285
-
286
-
287
-
288
-
289
-
290
-
291
-
292
-
293
-
294
-
295
-
296
-
297
-
298
-
299
-
300
-
301
-
302
-
303
-
304
-
305
-
306
-
307
-
308
-
309
package rardecode
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
)
const (
maxSfxSize = 0x100000 // maximum number of bytes to read when searching for RAR signature
sigPrefix = "Rar!\x1A\x07"
fileFmt15 = iota + 1 // Version 1.5 archive file format
fileFmt50 // Version 5.0 archive file format
)
var (
errNoSig = errors.New("rardecode: RAR signature not found")
errVerMismatch = errors.New("rardecode: volume version mistmatch")
errCorruptHeader = errors.New("rardecode: corrupt block header")
errCorruptFileHeader = errors.New("rardecode: corrupt file header")
errBadHeaderCrc = errors.New("rardecode: bad header crc")
errUnknownArc = errors.New("rardecode: unknown archive version")
errUnknownDecoder = errors.New("rardecode: unknown decoder version")
errUnsupportedDecoder = errors.New("rardecode: unsupported decoder version")
errArchiveContinues = errors.New("rardecode: archive continues in next volume")
errArchiveEnd = errors.New("rardecode: archive end reached")
errDecoderOutOfData = errors.New("rardecode: decoder expected more data than is in packed file")
reDigits = regexp.MustCompile(`\d+`)
)
type readBuf []byte
func (b *readBuf) byte() byte {
v := (*b)[0]
*b = (*b)[1:]
return v
}
func (b *readBuf) uint16() uint16 {
v := uint16((*b)[0]) | uint16((*b)[1])<<8
*b = (*b)[2:]
return v
}
func (b *readBuf) uint32() uint32 {
v := uint32((*b)[0]) | uint32((*b)[1])<<8 | uint32((*b)[2])<<16 | uint32((*b)[3])<<24
*b = (*b)[4:]
return v
}
func (b *readBuf) bytes(n int) []byte {
v := (*b)[:n]
*b = (*b)[n:]
return v
}
func (b *readBuf) uvarint() uint64 {
var x uint64
var s uint
for i, n := range *b {
if n < 0x80 {
*b = (*b)[i+1:]
return x | uint64(n)<<s
}
x |= uint64(n&0x7f) << s
s += 7
}
// if we run out of bytes, just return 0
*b = (*b)[len(*b):]
return 0
}
// readFull wraps io.ReadFull to return io.ErrUnexpectedEOF instead
// of io.EOF when 0 bytes are read.
func readFull(r io.Reader, buf []byte) error {
_, err := io.ReadFull(r, buf)
if err == io.EOF {
return io.ErrUnexpectedEOF
}
return err
}
// findSig searches for the RAR signature and version at the beginning of a file.
// It searches no more than maxSfxSize bytes.
func findSig(br *bufio.Reader) (int, error) {
for n := 0; n <= maxSfxSize; {
b, err := br.ReadSlice(sigPrefix[0])
n += len(b)
if err == bufio.ErrBufferFull {
continue
} else if err != nil {
if err == io.EOF {
err = errNoSig
}
return 0, err
}
b, err = br.Peek(len(sigPrefix[1:]) + 2)
if err != nil {
if err == io.EOF {
err = errNoSig
}
return 0, err
}
if !bytes.HasPrefix(b, []byte(sigPrefix[1:])) {
continue
}
b = b[len(sigPrefix)-1:]
var ver int
switch {
case b[0] == 0:
ver = fileFmt15
case b[0] == 1 && b[1] == 0:
ver = fileFmt50
default:
continue
}
_, _ = br.ReadSlice('\x00')
return ver, nil
}
return 0, errNoSig
}
// volume extends a fileBlockReader to be used across multiple
// files in a multi-volume archive
type volume struct {
fileBlockReader
f *os.File // current file handle
br *bufio.Reader // buffered reader for current volume file
dir string // volume directory
file string // current volume file (not including directory)
files []string // full path names for current volume files processed
num int // volume number
old bool // uses old naming scheme
}
// nextVolName updates name to the next filename in the archive.
func (v *volume) nextVolName() {
if v.num == 0 {
// check file extensions
i := strings.LastIndex(v.file, ".")
if i < 0 {
// no file extension, add one
i = len(v.file)
v.file += ".rar"
} else {
ext := strings.ToLower(v.file[i+1:])
// replace with .rar for empty extensions & self extracting archives
if ext == "" || ext == "exe" || ext == "sfx" {
v.file = v.file[:i+1] + "rar"
}
}
if a, ok := v.fileBlockReader.(*archive15); ok {
v.old = a.old
}
// new naming scheme must have volume number in filename
if !v.old && reDigits.FindStringIndex(v.file) == nil {
v.old = true
}
// For old style naming if 2nd and 3rd character of file extension is not a digit replace
// with "00" and ignore any trailing characters.
if v.old && (len(v.file) < i+4 || v.file[i+2] < '0' || v.file[i+2] > '9' || v.file[i+3] < '0' || v.file[i+3] > '9') {
v.file = v.file[:i+2] + "00"
return
}
}
// new style volume naming
if !v.old {
// find all numbers in volume name
m := reDigits.FindAllStringIndex(v.file, -1)
if l := len(m); l > 1 {
// More than 1 match so assume name.part###of###.rar style.
// Take the last 2 matches where the first is the volume number.
m = m[l-2 : l]
if strings.Contains(v.file[m[0][1]:m[1][0]], ".") || !strings.Contains(v.file[:m[0][0]], ".") {
// Didn't match above style as volume had '.' between the two numbers or didnt have a '.'
// before the first match. Use the second number as volume number.
m = m[1:]
}
}
// extract and increment volume number
lo, hi := m[0][0], m[0][1]
n, err := strconv.Atoi(v.file[lo:hi])
if err != nil {
n = 0
} else {
n++
}
// volume number must use at least the same number of characters as previous volume
vol := fmt.Sprintf("%0"+fmt.Sprint(hi-lo)+"d", n)
v.file = v.file[:lo] + vol + v.file[hi:]
return
}
// old style volume naming
i := strings.LastIndex(v.file, ".")
// get file extension
b := []byte(v.file[i+1:])
// start incrementing volume number digits from rightmost
for j := 2; j >= 0; j-- {
if b[j] != '9' {
b[j]++
break
}
// digit overflow
if j == 0 {
// last character before '.'
b[j] = 'A'
} else {
// set to '0' and loop to next character
b[j] = '0'
}
}
v.file = v.file[:i+1] + string(b)
}
func (v *volume) next() (*fileBlockHeader, error) {
for {
var atEOF bool
h, err := v.fileBlockReader.next()
switch err {
case errArchiveContinues:
case io.EOF:
// Read all of volume without finding an end block. The only way
// to tell if the archive continues is to try to open the next volume.
atEOF = true
default:
return h, err
}
v.f.Close()
v.nextVolName()
v.f, err = os.Open(v.dir + v.file) // Open next volume file
if err != nil {
if atEOF && os.IsNotExist(err) {
// volume not found so assume that the archive has ended
return nil, io.EOF
}
return nil, err
}
v.num++
v.br.Reset(v.f)
ver, err := findSig(v.br)
if err != nil {
return nil, err
}
if v.version() != ver {
return nil, errVerMismatch
}
v.files = append(v.files, v.dir+v.file)
v.reset() // reset encryption
}
}
func (v *volume) Close() error {
// may be nil if os.Open fails in next()
if v.f == nil {
return nil
}
return v.f.Close()
}
func openVolume(name, password string) (*volume, error) {
var err error
v := new(volume)
v.dir, v.file = filepath.Split(name)
v.f, err = os.Open(name)
if err != nil {
return nil, err
}
v.br = bufio.NewReader(v.f)
v.fileBlockReader, err = newFileBlockReader(v.br, password)
if err != nil {
v.f.Close()
return nil, err
}
v.files = append(v.files, name)
return v, nil
}
func newFileBlockReader(br *bufio.Reader, pass string) (fileBlockReader, error) {
runes := []rune(pass)
if len(runes) > maxPassword {
pass = string(runes[:maxPassword])
}
ver, err := findSig(br)
if err != nil {
return nil, err
}
switch ver {
case fileFmt15:
return newArchive15(br, pass), nil
case fileFmt50:
return newArchive50(br, pass), nil
}
return nil, errUnknownArc
}