mirror of
https://github.com/go-gitea/gitea.git
synced 2024-09-19 01:35:59 -04:00
684b7a999f
* Dump: Use mholt/archive/v3 to support tar including many compressions Signed-off-by: Philipp Homann <homann.philipp@googlemail.com> * Dump: Allow dump output to stdout Signed-off-by: Philipp Homann <homann.philipp@googlemail.com> * Dump: Fixed bug present since #6677 where SessionConfig.Provider is never "file" Signed-off-by: Philipp Homann <homann.philipp@googlemail.com> * Dump: never pack RepoRootPath, LFS.ContentPath and LogRootPath when they are below AppDataPath Signed-off-by: Philipp Homann <homann.philipp@googlemail.com> * Dump: also dump LFS (fixes #10058) Signed-off-by: Philipp Homann <homann.philipp@googlemail.com> * Dump: never dump CustomPath if CustomPath is a subdir of or equal to AppDataPath (fixes #10365) Signed-off-by: Philipp Homann <homann.philipp@googlemail.com> * Use log.Info instead of fmt.Fprintf Signed-off-by: Philipp Homann <homann.philipp@googlemail.com> * import ordering * make fmt Co-authored-by: zeripath <art27@cantab.net> Co-authored-by: techknowlogick <techknowlogick@gitea.io> Co-authored-by: Matti R <matti@mdranta.net>
417 lines
9.1 KiB
Go
Vendored
417 lines
9.1 KiB
Go
Vendored
package rardecode
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"hash/crc32"
|
|
"io"
|
|
)
|
|
|
|
const (
|
|
fileSize = 0x1000000
|
|
|
|
vmGlobalAddr = 0x3C000
|
|
vmGlobalSize = 0x02000
|
|
vmFixedGlobalSize = 0x40
|
|
|
|
maxUint32 = 1<<32 - 1
|
|
)
|
|
|
|
// v3Filter is the interface type for RAR V3 filters.
|
|
// v3Filter performs the same function as the filter type, except that it also takes
|
|
// the initial register values r, and global data as input for the RAR V3 VM.
|
|
type v3Filter func(r map[int]uint32, global, buf []byte, offset int64) ([]byte, error)
|
|
|
|
var (
|
|
// standardV3Filters is a list of known filters. We can replace the use of a vm
|
|
// filter with a custom filter function.
|
|
standardV3Filters = []struct {
|
|
crc uint32 // crc of code byte slice for filter
|
|
len int // length of code byte slice for filter
|
|
f v3Filter // replacement filter function
|
|
}{
|
|
{0xad576887, 53, e8FilterV3},
|
|
{0x3cd7e57e, 57, e8e9FilterV3},
|
|
{0x3769893f, 120, itaniumFilterV3},
|
|
{0x0e06077d, 29, deltaFilterV3},
|
|
{0x1c2c5dc8, 149, filterRGBV3},
|
|
{0xbc85e701, 216, filterAudioV3},
|
|
}
|
|
|
|
// itanium filter byte masks
|
|
byteMask = []int{4, 4, 6, 6, 0, 0, 7, 7, 4, 4, 0, 0, 4, 4, 0, 0}
|
|
)
|
|
|
|
func filterE8(c byte, v5 bool, buf []byte, offset int64) ([]byte, error) {
|
|
off := int32(offset)
|
|
for b := buf; len(b) >= 5; {
|
|
ch := b[0]
|
|
b = b[1:]
|
|
off++
|
|
if ch != 0xe8 && ch != c {
|
|
continue
|
|
}
|
|
if v5 {
|
|
off %= fileSize
|
|
}
|
|
addr := int32(binary.LittleEndian.Uint32(b))
|
|
if addr < 0 {
|
|
if addr+off >= 0 {
|
|
binary.LittleEndian.PutUint32(b, uint32(addr+fileSize))
|
|
}
|
|
} else if addr < fileSize {
|
|
binary.LittleEndian.PutUint32(b, uint32(addr-off))
|
|
}
|
|
off += 4
|
|
b = b[4:]
|
|
}
|
|
return buf, nil
|
|
}
|
|
|
|
func e8FilterV3(r map[int]uint32, global, buf []byte, offset int64) ([]byte, error) {
|
|
return filterE8(0xe8, false, buf, offset)
|
|
}
|
|
|
|
func e8e9FilterV3(r map[int]uint32, global, buf []byte, offset int64) ([]byte, error) {
|
|
return filterE8(0xe9, false, buf, offset)
|
|
}
|
|
|
|
func getBits(buf []byte, pos, count uint) uint32 {
|
|
n := binary.LittleEndian.Uint32(buf[pos/8:])
|
|
n >>= pos & 7
|
|
mask := uint32(maxUint32) >> (32 - count)
|
|
return n & mask
|
|
}
|
|
|
|
func setBits(buf []byte, pos, count uint, bits uint32) {
|
|
mask := uint32(maxUint32) >> (32 - count)
|
|
mask <<= pos & 7
|
|
bits <<= pos & 7
|
|
n := binary.LittleEndian.Uint32(buf[pos/8:])
|
|
n = (n & ^mask) | (bits & mask)
|
|
binary.LittleEndian.PutUint32(buf[pos/8:], n)
|
|
}
|
|
|
|
func itaniumFilterV3(r map[int]uint32, global, buf []byte, offset int64) ([]byte, error) {
|
|
fileOffset := uint32(offset) >> 4
|
|
|
|
for b := buf; len(b) > 21; b = b[16:] {
|
|
c := int(b[0]&0x1f) - 0x10
|
|
if c >= 0 {
|
|
mask := byteMask[c]
|
|
if mask != 0 {
|
|
for i := uint(0); i <= 2; i++ {
|
|
if mask&(1<<i) == 0 {
|
|
continue
|
|
}
|
|
pos := i*41 + 18
|
|
if getBits(b, pos+24, 4) == 5 {
|
|
n := getBits(b, pos, 20)
|
|
n -= fileOffset
|
|
setBits(b, pos, 20, n)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
fileOffset++
|
|
}
|
|
return buf, nil
|
|
}
|
|
|
|
func filterDelta(n int, buf []byte) ([]byte, error) {
|
|
var res []byte
|
|
l := len(buf)
|
|
if cap(buf) >= 2*l {
|
|
res = buf[l : 2*l] // use unused capacity
|
|
} else {
|
|
res = make([]byte, l, 2*l)
|
|
}
|
|
|
|
i := 0
|
|
for j := 0; j < n; j++ {
|
|
var c byte
|
|
for k := j; k < len(res); k += n {
|
|
c -= buf[i]
|
|
i++
|
|
res[k] = c
|
|
}
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func deltaFilterV3(r map[int]uint32, global, buf []byte, offset int64) ([]byte, error) {
|
|
return filterDelta(int(r[0]), buf)
|
|
}
|
|
|
|
func abs(n int) int {
|
|
if n < 0 {
|
|
n = -n
|
|
}
|
|
return n
|
|
}
|
|
|
|
func filterRGBV3(r map[int]uint32, global, buf []byte, offset int64) ([]byte, error) {
|
|
width := int(r[0] - 3)
|
|
posR := int(r[1])
|
|
if posR < 0 || width < 0 {
|
|
return buf, nil
|
|
}
|
|
|
|
var res []byte
|
|
l := len(buf)
|
|
if cap(buf) >= 2*l {
|
|
res = buf[l : 2*l] // use unused capacity
|
|
} else {
|
|
res = make([]byte, l, 2*l)
|
|
}
|
|
|
|
for c := 0; c < 3; c++ {
|
|
var prevByte int
|
|
for i := c; i < len(res); i += 3 {
|
|
var predicted int
|
|
upperPos := i - width
|
|
if upperPos >= 3 {
|
|
upperByte := int(res[upperPos])
|
|
upperLeftByte := int(res[upperPos-3])
|
|
predicted = prevByte + upperByte - upperLeftByte
|
|
pa := abs(predicted - prevByte)
|
|
pb := abs(predicted - upperByte)
|
|
pc := abs(predicted - upperLeftByte)
|
|
if pa <= pb && pa <= pc {
|
|
predicted = prevByte
|
|
} else if pb <= pc {
|
|
predicted = upperByte
|
|
} else {
|
|
predicted = upperLeftByte
|
|
}
|
|
} else {
|
|
predicted = prevByte
|
|
}
|
|
prevByte = (predicted - int(buf[0])) & 0xFF
|
|
res[i] = uint8(prevByte)
|
|
buf = buf[1:]
|
|
}
|
|
|
|
}
|
|
for i := posR; i < len(res)-2; i += 3 {
|
|
c := res[i+1]
|
|
res[i] += c
|
|
res[i+2] += c
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func filterAudioV3(r map[int]uint32, global, buf []byte, offset int64) ([]byte, error) {
|
|
var res []byte
|
|
l := len(buf)
|
|
if cap(buf) >= 2*l {
|
|
res = buf[l : 2*l] // use unused capacity
|
|
} else {
|
|
res = make([]byte, l, 2*l)
|
|
}
|
|
|
|
chans := int(r[0])
|
|
for c := 0; c < chans; c++ {
|
|
var prevByte, byteCount int
|
|
var diff [7]int
|
|
var d, k [3]int
|
|
|
|
for i := c; i < len(res); i += chans {
|
|
predicted := prevByte<<3 + k[0]*d[0] + k[1]*d[1] + k[2]*d[2]
|
|
predicted = int(int8(predicted >> 3))
|
|
|
|
curByte := int(int8(buf[0]))
|
|
buf = buf[1:]
|
|
predicted -= curByte
|
|
res[i] = uint8(predicted)
|
|
|
|
dd := curByte << 3
|
|
diff[0] += abs(dd)
|
|
diff[1] += abs(dd - d[0])
|
|
diff[2] += abs(dd + d[0])
|
|
diff[3] += abs(dd - d[1])
|
|
diff[4] += abs(dd + d[1])
|
|
diff[5] += abs(dd - d[2])
|
|
diff[6] += abs(dd + d[2])
|
|
|
|
prevDelta := int(int8(predicted - prevByte))
|
|
prevByte = predicted
|
|
d[2] = d[1]
|
|
d[1] = prevDelta - d[0]
|
|
d[0] = prevDelta
|
|
|
|
if byteCount&0x1f == 0 {
|
|
min := diff[0]
|
|
diff[0] = 0
|
|
n := 0
|
|
for j := 1; j < len(diff); j++ {
|
|
if diff[j] < min {
|
|
min = diff[j]
|
|
n = j
|
|
}
|
|
diff[j] = 0
|
|
}
|
|
n--
|
|
if n >= 0 {
|
|
m := n / 2
|
|
if n%2 == 0 {
|
|
if k[m] >= -16 {
|
|
k[m]--
|
|
}
|
|
} else {
|
|
if k[m] < 16 {
|
|
k[m]++
|
|
}
|
|
}
|
|
}
|
|
}
|
|
byteCount++
|
|
}
|
|
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func filterArm(buf []byte, offset int64) ([]byte, error) {
|
|
for i := 0; len(buf)-i > 3; i += 4 {
|
|
if buf[i+3] == 0xeb {
|
|
n := uint(buf[i])
|
|
n += uint(buf[i+1]) * 0x100
|
|
n += uint(buf[i+2]) * 0x10000
|
|
n -= (uint(offset) + uint(i)) / 4
|
|
buf[i] = byte(n)
|
|
buf[i+1] = byte(n >> 8)
|
|
buf[i+2] = byte(n >> 16)
|
|
}
|
|
}
|
|
return buf, nil
|
|
}
|
|
|
|
type vmFilter struct {
|
|
execCount uint32
|
|
global []byte
|
|
static []byte
|
|
code []command
|
|
}
|
|
|
|
// execute implements v3filter type for VM based RAR 3 filters.
|
|
func (f *vmFilter) execute(r map[int]uint32, global, buf []byte, offset int64) ([]byte, error) {
|
|
if len(buf) > vmGlobalAddr {
|
|
return buf, errInvalidFilter
|
|
}
|
|
v := newVM(buf)
|
|
|
|
// register setup
|
|
v.r[3] = vmGlobalAddr
|
|
v.r[4] = uint32(len(buf))
|
|
v.r[5] = f.execCount
|
|
for i, n := range r {
|
|
v.r[i] = n
|
|
}
|
|
|
|
// vm global data memory block
|
|
vg := v.m[vmGlobalAddr : vmGlobalAddr+vmGlobalSize]
|
|
|
|
// initialize fixed global memory
|
|
for i, n := range v.r[:vmRegs-1] {
|
|
binary.LittleEndian.PutUint32(vg[i*4:], n)
|
|
}
|
|
binary.LittleEndian.PutUint32(vg[0x1c:], uint32(len(buf)))
|
|
binary.LittleEndian.PutUint64(vg[0x24:], uint64(offset))
|
|
binary.LittleEndian.PutUint32(vg[0x2c:], f.execCount)
|
|
|
|
// registers
|
|
v.r[6] = uint32(offset)
|
|
|
|
// copy program global memory
|
|
var n int
|
|
if len(f.global) > 0 {
|
|
n = copy(vg[vmFixedGlobalSize:], f.global) // use saved global instead
|
|
} else {
|
|
n = copy(vg[vmFixedGlobalSize:], global)
|
|
}
|
|
copy(vg[vmFixedGlobalSize+n:], f.static)
|
|
|
|
v.execute(f.code)
|
|
|
|
f.execCount++
|
|
|
|
// keep largest global buffer
|
|
if cap(global) > cap(f.global) {
|
|
f.global = global[:0]
|
|
} else if len(f.global) > 0 {
|
|
f.global = f.global[:0]
|
|
}
|
|
|
|
// check for global data to be saved for next program execution
|
|
globalSize := binary.LittleEndian.Uint32(vg[0x30:])
|
|
if globalSize > 0 {
|
|
if globalSize > vmGlobalSize-vmFixedGlobalSize {
|
|
globalSize = vmGlobalSize - vmFixedGlobalSize
|
|
}
|
|
if cap(f.global) < int(globalSize) {
|
|
f.global = make([]byte, globalSize)
|
|
} else {
|
|
f.global = f.global[:globalSize]
|
|
}
|
|
copy(f.global, vg[vmFixedGlobalSize:])
|
|
}
|
|
|
|
// find program output
|
|
length := binary.LittleEndian.Uint32(vg[0x1c:]) & vmMask
|
|
start := binary.LittleEndian.Uint32(vg[0x20:]) & vmMask
|
|
if start+length > vmSize {
|
|
// TODO: error
|
|
start = 0
|
|
length = 0
|
|
}
|
|
if start != 0 && cap(v.m) > cap(buf) {
|
|
// Initial buffer was to small for vm.
|
|
// Copy output to beginning of vm memory so that decodeReader
|
|
// will re-use the newly allocated vm memory and we will not
|
|
// have to reallocate again next time.
|
|
copy(v.m, v.m[start:start+length])
|
|
start = 0
|
|
}
|
|
return v.m[start : start+length], nil
|
|
}
|
|
|
|
// getV3Filter returns a V3 filter function from a code byte slice.
|
|
func getV3Filter(code []byte) (v3Filter, error) {
|
|
// check if filter is a known standard filter
|
|
c := crc32.ChecksumIEEE(code)
|
|
for _, f := range standardV3Filters {
|
|
if f.crc == c && f.len == len(code) {
|
|
return f.f, nil
|
|
}
|
|
}
|
|
|
|
// create new vm filter
|
|
f := new(vmFilter)
|
|
r := newRarBitReader(bytes.NewReader(code[1:])) // skip first xor byte check
|
|
|
|
// read static data
|
|
n, err := r.readBits(1)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if n > 0 {
|
|
m, err := r.readUint32()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
f.static = make([]byte, m+1)
|
|
err = r.readFull(f.static)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
f.code, err = readCommands(r)
|
|
if err == io.EOF {
|
|
err = nil
|
|
}
|
|
|
|
return f.execute, err
|
|
}
|