asar/header.go

263 lines
4.9 KiB
Go

package asar // import "b612.me/asar"
import (
"encoding/json"
"errors"
"io"
)
var (
errHeader = errors.New("asar: invalid file header")
)
type jsonReader struct {
ASAR io.ReaderAt
BaseOffset int64
D *json.Decoder
Token json.Token
Options DecodeOptions // 新增字段
}
func (j *jsonReader) Peek() json.Token {
if j.Token != nil {
return j.Token
}
tkn, err := j.D.Token()
if err != nil {
if err == io.EOF {
return nil
}
panic(err)
}
j.Token = tkn
return tkn
}
func (j *jsonReader) HasDelimRune(r rune) bool {
peek := j.Peek()
ru, ok := peek.(json.Delim)
if !ok {
return false
}
if rune(ru) != r {
return false
}
return true
}
func (j *jsonReader) Next() json.Token {
if j.Token != nil {
t := j.Token
j.Token = nil
return t
}
tkn, err := j.D.Token()
if err != nil {
if err == io.EOF {
return nil
}
panic(err)
}
return tkn
}
func (j *jsonReader) NextDelimRune() rune {
tkn := j.Next()
r, ok := tkn.(json.Delim)
if !ok {
panic(errHeader)
}
return rune(r)
}
func (j *jsonReader) ExpectDelim(r rune) {
next := j.NextDelimRune()
if next != r {
panic(errHeader)
}
}
func (j *jsonReader) ExpectBool() bool {
tkn := j.Next()
b, ok := tkn.(bool)
if !ok {
panic(errHeader)
}
return b
}
func (j *jsonReader) ExpectString() string {
next := j.Next()
str, ok := next.(string)
if !ok {
panic(errHeader)
}
return str
}
func (j *jsonReader) ExpectStringVal(val string) {
str := j.ExpectString()
if str != val {
panic(errHeader)
}
}
func (j *jsonReader) ExpectInt64() int64 {
var number json.Number
switch j.Peek().(type) {
case string:
number = json.Number(j.ExpectString())
case json.Number:
number = j.Next().(json.Number)
default:
panic(errHeader)
}
val, err := number.Int64()
if err != nil {
panic(errHeader)
}
return val
}
func parseRoot(r *jsonReader) *Entry {
entry := &Entry{
Flags: FlagDir,
}
r.ExpectDelim('{')
r.ExpectStringVal("files")
parseFiles(r, entry)
r.ExpectDelim('}')
// 关键修改:根据选项决定是否检查剩余内容
if r.Options.StrictValidation {
// 严格模式:不允许有剩余数据
if r.Next() != nil {
panic(errHeader)
}
} else {
// 宽松模式:消费并忽略剩余的 token
// 这些可能是 NUL 填充或其他无关数据
for {
token := r.Next()
if token == nil {
break
}
// 可选:如果需要,可以在这里记录日志
// log.Printf("Ignoring trailing token in ASAR header: %v", token)
}
}
return entry
}
func parseFiles(r *jsonReader, parent *Entry) {
r.ExpectDelim('{')
for !r.HasDelimRune('}') {
parseEntry(r, parent)
}
r.ExpectDelim('}')
}
func parseEntry(r *jsonReader, parent *Entry) {
name := r.ExpectString()
if name == "" {
panic(errHeader)
}
if !validFilename(name) {
panic(errHeader)
}
r.ExpectDelim('{')
child := &Entry{
Name: name,
Parent: parent,
}
for !r.HasDelimRune('}') {
key := r.ExpectString()
switch key {
case "files":
child.Flags |= FlagDir
parseFiles(r, child)
case "size":
child.Size = r.ExpectInt64()
case "offset":
child.Offset = r.ExpectInt64()
case "unpacked":
if r.ExpectBool() {
child.Flags |= FlagUnpacked
}
case "executable":
if r.ExpectBool() {
child.Flags |= FlagExecutable
}
default:
if r.Options.StrictValidation {
// 严格模式:未知字段导致错误
panic(errHeader)
} else {
// 宽松模式:跳过未知字段
// 需要消费这个未知字段的值
if token := r.Next(); token == nil {
panic(errHeader)
}
// 可选:记录日志
// log.Printf("Ignoring unknown field '%s' in ASAR entry", key)
}
}
}
if child.Flags&FlagDir == 0 {
child.r = r.ASAR
child.baseOffset = r.BaseOffset
}
parent.Children = append(parent.Children, child)
r.ExpectDelim('}')
}
// 保留原有的 decodeHeader
func decodeHeader(asar io.ReaderAt, header *io.SectionReader, offset int64) (entry *Entry, err error) {
return decodeHeaderWithOptions(asar, header, offset, DefaultDecodeOptions)
}
// 新增带选项的版本
func decodeHeaderWithOptions(asar io.ReaderAt, header *io.SectionReader, offset int64, opts DecodeOptions) (entry *Entry, err error) {
decoder := json.NewDecoder(header)
decoder.UseNumber()
reader := jsonReader{
ASAR: asar,
BaseOffset: offset,
D: decoder,
Options: opts, // 传递选项
}
defer func() {
if r := recover(); r != nil {
if e := r.(error); e != nil {
err = e
} else {
panic(r)
}
}
}()
entry = parseRoot(&reader)
return
}
// findJSONEnd 找到 JSON 的实际结束位置(最后一个有意义的字符)
func findJSONEnd(data []byte) int {
// 从后往前找,跳过所有的 NUL 和空白字符
for i := len(data) - 1; i >= 0; i-- {
b := data[i]
// 跳过 NUL 和空白字符
if b != 0 && b != ' ' && b != '\n' && b != '\r' && b != '\t' {
// 找到了非空白字符,这应该是 JSON 的结束
return i + 1
}
}
return len(data)
}