mirror of https://github.com/containers/podman.git
				
				
				
			
		
			
				
	
	
		
			382 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			382 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Go
		
	
	
	
package dbus
 | 
						|
 | 
						|
import (
 | 
						|
	"encoding/binary"
 | 
						|
	"io"
 | 
						|
	"reflect"
 | 
						|
	"unsafe"
 | 
						|
)
 | 
						|
 | 
						|
type decoder struct {
 | 
						|
	in    io.Reader
 | 
						|
	order binary.ByteOrder
 | 
						|
	pos   int
 | 
						|
	fds   []int
 | 
						|
 | 
						|
	// The following fields are used to reduce memory allocs.
 | 
						|
	conv *stringConverter
 | 
						|
	buf  []byte
 | 
						|
	d    float64
 | 
						|
	y    [1]byte
 | 
						|
}
 | 
						|
 | 
						|
// newDecoder returns a new decoder that reads values from in. The input is
 | 
						|
// expected to be in the given byte order.
 | 
						|
func newDecoder(in io.Reader, order binary.ByteOrder, fds []int) *decoder {
 | 
						|
	dec := new(decoder)
 | 
						|
	dec.in = in
 | 
						|
	dec.order = order
 | 
						|
	dec.fds = fds
 | 
						|
	dec.conv = newStringConverter(stringConverterBufferSize)
 | 
						|
	return dec
 | 
						|
}
 | 
						|
 | 
						|
// Reset resets the decoder to be reading from in.
 | 
						|
func (dec *decoder) Reset(in io.Reader, order binary.ByteOrder, fds []int) {
 | 
						|
	dec.in = in
 | 
						|
	dec.order = order
 | 
						|
	dec.pos = 0
 | 
						|
	dec.fds = fds
 | 
						|
 | 
						|
	if dec.conv == nil {
 | 
						|
		dec.conv = newStringConverter(stringConverterBufferSize)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// align aligns the input to the given boundary and panics on error.
 | 
						|
func (dec *decoder) align(n int) {
 | 
						|
	if dec.pos%n != 0 {
 | 
						|
		newpos := (dec.pos + n - 1) & ^(n - 1)
 | 
						|
		dec.read2buf(newpos - dec.pos)
 | 
						|
		dec.pos = newpos
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Calls binary.Read(dec.in, dec.order, v) and panics on read errors.
 | 
						|
func (dec *decoder) binread(v interface{}) {
 | 
						|
	if err := binary.Read(dec.in, dec.order, v); err != nil {
 | 
						|
		panic(err)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (dec *decoder) Decode(sig Signature) (vs []interface{}, err error) {
 | 
						|
	defer func() {
 | 
						|
		var ok bool
 | 
						|
		v := recover()
 | 
						|
		if err, ok = v.(error); ok {
 | 
						|
			if err == io.EOF || err == io.ErrUnexpectedEOF {
 | 
						|
				err = FormatError("unexpected EOF")
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}()
 | 
						|
	vs = make([]interface{}, 0)
 | 
						|
	s := sig.str
 | 
						|
	for s != "" {
 | 
						|
		err, rem := validSingle(s, &depthCounter{})
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		v := dec.decode(s[:len(s)-len(rem)], 0)
 | 
						|
		vs = append(vs, v)
 | 
						|
		s = rem
 | 
						|
	}
 | 
						|
	return vs, nil
 | 
						|
}
 | 
						|
 | 
						|
// read2buf reads exactly n bytes from the reader dec.in into the buffer dec.buf
 | 
						|
// to reduce memory allocs.
 | 
						|
// The buffer grows automatically.
 | 
						|
func (dec *decoder) read2buf(n int) {
 | 
						|
	if cap(dec.buf) < n {
 | 
						|
		dec.buf = make([]byte, n)
 | 
						|
	} else {
 | 
						|
		dec.buf = dec.buf[:n]
 | 
						|
	}
 | 
						|
	if _, err := io.ReadFull(dec.in, dec.buf); err != nil {
 | 
						|
		panic(err)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// decodeU decodes uint32 obtained from the reader dec.in.
 | 
						|
// The goal is to reduce memory allocs.
 | 
						|
func (dec *decoder) decodeU() uint32 {
 | 
						|
	dec.align(4)
 | 
						|
	dec.read2buf(4)
 | 
						|
	dec.pos += 4
 | 
						|
	return dec.order.Uint32(dec.buf)
 | 
						|
}
 | 
						|
 | 
						|
func (dec *decoder) decode(s string, depth int) interface{} {
 | 
						|
	dec.align(alignment(typeFor(s)))
 | 
						|
	switch s[0] {
 | 
						|
	case 'y':
 | 
						|
		if _, err := dec.in.Read(dec.y[:]); err != nil {
 | 
						|
			panic(err)
 | 
						|
		}
 | 
						|
		dec.pos++
 | 
						|
		return dec.y[0]
 | 
						|
	case 'b':
 | 
						|
		switch dec.decodeU() {
 | 
						|
		case 0:
 | 
						|
			return false
 | 
						|
		case 1:
 | 
						|
			return true
 | 
						|
		default:
 | 
						|
			panic(FormatError("invalid value for boolean"))
 | 
						|
		}
 | 
						|
	case 'n':
 | 
						|
		dec.read2buf(2)
 | 
						|
		dec.pos += 2
 | 
						|
		return int16(dec.order.Uint16(dec.buf))
 | 
						|
	case 'i':
 | 
						|
		dec.read2buf(4)
 | 
						|
		dec.pos += 4
 | 
						|
		return int32(dec.order.Uint32(dec.buf))
 | 
						|
	case 'x':
 | 
						|
		dec.read2buf(8)
 | 
						|
		dec.pos += 8
 | 
						|
		return int64(dec.order.Uint64(dec.buf))
 | 
						|
	case 'q':
 | 
						|
		dec.read2buf(2)
 | 
						|
		dec.pos += 2
 | 
						|
		return dec.order.Uint16(dec.buf)
 | 
						|
	case 'u':
 | 
						|
		return dec.decodeU()
 | 
						|
	case 't':
 | 
						|
		dec.read2buf(8)
 | 
						|
		dec.pos += 8
 | 
						|
		return dec.order.Uint64(dec.buf)
 | 
						|
	case 'd':
 | 
						|
		dec.binread(&dec.d)
 | 
						|
		dec.pos += 8
 | 
						|
		return dec.d
 | 
						|
	case 's':
 | 
						|
		length := dec.decodeU()
 | 
						|
		p := int(length) + 1
 | 
						|
		dec.read2buf(p)
 | 
						|
		dec.pos += p
 | 
						|
		return dec.conv.String(dec.buf[:len(dec.buf)-1])
 | 
						|
	case 'o':
 | 
						|
		return ObjectPath(dec.decode("s", depth).(string))
 | 
						|
	case 'g':
 | 
						|
		length := dec.decode("y", depth).(byte)
 | 
						|
		p := int(length) + 1
 | 
						|
		dec.read2buf(p)
 | 
						|
		dec.pos += p
 | 
						|
		sig, err := ParseSignature(
 | 
						|
			dec.conv.String(dec.buf[:len(dec.buf)-1]),
 | 
						|
		)
 | 
						|
		if err != nil {
 | 
						|
			panic(err)
 | 
						|
		}
 | 
						|
		return sig
 | 
						|
	case 'v':
 | 
						|
		if depth >= 64 {
 | 
						|
			panic(FormatError("input exceeds container depth limit"))
 | 
						|
		}
 | 
						|
		var variant Variant
 | 
						|
		sig := dec.decode("g", depth).(Signature)
 | 
						|
		if len(sig.str) == 0 {
 | 
						|
			panic(FormatError("variant signature is empty"))
 | 
						|
		}
 | 
						|
		err, rem := validSingle(sig.str, &depthCounter{})
 | 
						|
		if err != nil {
 | 
						|
			panic(err)
 | 
						|
		}
 | 
						|
		if rem != "" {
 | 
						|
			panic(FormatError("variant signature has multiple types"))
 | 
						|
		}
 | 
						|
		variant.sig = sig
 | 
						|
		variant.value = dec.decode(sig.str, depth+1)
 | 
						|
		return variant
 | 
						|
	case 'h':
 | 
						|
		idx := dec.decodeU()
 | 
						|
		if int(idx) < len(dec.fds) {
 | 
						|
			return UnixFD(dec.fds[idx])
 | 
						|
		}
 | 
						|
		return UnixFDIndex(idx)
 | 
						|
	case 'a':
 | 
						|
		if len(s) > 1 && s[1] == '{' {
 | 
						|
			ksig := s[2:3]
 | 
						|
			vsig := s[3 : len(s)-1]
 | 
						|
			v := reflect.MakeMap(reflect.MapOf(typeFor(ksig), typeFor(vsig)))
 | 
						|
			if depth >= 63 {
 | 
						|
				panic(FormatError("input exceeds container depth limit"))
 | 
						|
			}
 | 
						|
			length := dec.decodeU()
 | 
						|
			// Even for empty maps, the correct padding must be included
 | 
						|
			dec.align(8)
 | 
						|
			spos := dec.pos
 | 
						|
			for dec.pos < spos+int(length) {
 | 
						|
				dec.align(8)
 | 
						|
				if !isKeyType(v.Type().Key()) {
 | 
						|
					panic(InvalidTypeError{v.Type()})
 | 
						|
				}
 | 
						|
				kv := dec.decode(ksig, depth+2)
 | 
						|
				vv := dec.decode(vsig, depth+2)
 | 
						|
				v.SetMapIndex(reflect.ValueOf(kv), reflect.ValueOf(vv))
 | 
						|
			}
 | 
						|
			return v.Interface()
 | 
						|
		}
 | 
						|
		if depth >= 64 {
 | 
						|
			panic(FormatError("input exceeds container depth limit"))
 | 
						|
		}
 | 
						|
		sig := s[1:]
 | 
						|
		length := dec.decodeU()
 | 
						|
		// capacity can be determined only for fixed-size element types
 | 
						|
		var capacity int
 | 
						|
		if s := sigByteSize(sig); s != 0 {
 | 
						|
			capacity = int(length) / s
 | 
						|
		}
 | 
						|
		v := reflect.MakeSlice(reflect.SliceOf(typeFor(sig)), 0, capacity)
 | 
						|
		// Even for empty arrays, the correct padding must be included
 | 
						|
		align := alignment(typeFor(s[1:]))
 | 
						|
		if len(s) > 1 && s[1] == '(' {
 | 
						|
			// Special case for arrays of structs
 | 
						|
			// structs decode as a slice of interface{} values
 | 
						|
			// but the dbus alignment does not match this
 | 
						|
			align = 8
 | 
						|
		}
 | 
						|
		dec.align(align)
 | 
						|
		spos := dec.pos
 | 
						|
		for dec.pos < spos+int(length) {
 | 
						|
			ev := dec.decode(s[1:], depth+1)
 | 
						|
			v = reflect.Append(v, reflect.ValueOf(ev))
 | 
						|
		}
 | 
						|
		return v.Interface()
 | 
						|
	case '(':
 | 
						|
		if depth >= 64 {
 | 
						|
			panic(FormatError("input exceeds container depth limit"))
 | 
						|
		}
 | 
						|
		dec.align(8)
 | 
						|
		v := make([]interface{}, 0)
 | 
						|
		s = s[1 : len(s)-1]
 | 
						|
		for s != "" {
 | 
						|
			err, rem := validSingle(s, &depthCounter{})
 | 
						|
			if err != nil {
 | 
						|
				panic(err)
 | 
						|
			}
 | 
						|
			ev := dec.decode(s[:len(s)-len(rem)], depth+1)
 | 
						|
			v = append(v, ev)
 | 
						|
			s = rem
 | 
						|
		}
 | 
						|
		return v
 | 
						|
	default:
 | 
						|
		panic(SignatureError{Sig: s})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// sigByteSize tries to calculates size of the given signature in bytes.
 | 
						|
//
 | 
						|
// It returns zero when it can't, for example when it contains non-fixed size
 | 
						|
// types such as strings, maps and arrays that require reading of the transmitted
 | 
						|
// data, for that we would need to implement the unread method for Decoder first.
 | 
						|
func sigByteSize(sig string) int {
 | 
						|
	var total int
 | 
						|
	for offset := 0; offset < len(sig); {
 | 
						|
		switch sig[offset] {
 | 
						|
		case 'y':
 | 
						|
			total += 1
 | 
						|
			offset += 1
 | 
						|
		case 'n', 'q':
 | 
						|
			total += 2
 | 
						|
			offset += 1
 | 
						|
		case 'b', 'i', 'u', 'h':
 | 
						|
			total += 4
 | 
						|
			offset += 1
 | 
						|
		case 'x', 't', 'd':
 | 
						|
			total += 8
 | 
						|
			offset += 1
 | 
						|
		case '(':
 | 
						|
			i := 1
 | 
						|
			depth := 1
 | 
						|
			for i < len(sig[offset:]) && depth != 0 {
 | 
						|
				if sig[offset+i] == '(' {
 | 
						|
					depth++
 | 
						|
				} else if sig[offset+i] == ')' {
 | 
						|
					depth--
 | 
						|
				}
 | 
						|
				i++
 | 
						|
			}
 | 
						|
			s := sigByteSize(sig[offset+1 : offset+i-1])
 | 
						|
			if s == 0 {
 | 
						|
				return 0
 | 
						|
			}
 | 
						|
			total += s
 | 
						|
			offset += i
 | 
						|
		default:
 | 
						|
			return 0
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return total
 | 
						|
}
 | 
						|
 | 
						|
// A FormatError is an error in the wire format.
 | 
						|
type FormatError string
 | 
						|
 | 
						|
func (e FormatError) Error() string {
 | 
						|
	return "dbus: wire format error: " + string(e)
 | 
						|
}
 | 
						|
 | 
						|
// stringConverterBufferSize defines the recommended buffer size of 4KB.
 | 
						|
// It showed good results in a benchmark when decoding 35KB message,
 | 
						|
// see https://github.com/marselester/systemd#testing.
 | 
						|
const stringConverterBufferSize = 4096
 | 
						|
 | 
						|
func newStringConverter(capacity int) *stringConverter {
 | 
						|
	return &stringConverter{
 | 
						|
		buf:    make([]byte, 0, capacity),
 | 
						|
		offset: 0,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// stringConverter converts bytes to strings with less allocs.
 | 
						|
// The idea is to accumulate bytes in a buffer with specified capacity
 | 
						|
// and create strings with unsafe package using bytes from a buffer.
 | 
						|
// For example, 10 "fizz" strings written to a 40-byte buffer
 | 
						|
// will result in 1 alloc instead of 10.
 | 
						|
//
 | 
						|
// Once a buffer is filled, a new one is created with the same capacity.
 | 
						|
// Old buffers will be eventually GC-ed
 | 
						|
// with no side effects to the returned strings.
 | 
						|
type stringConverter struct {
 | 
						|
	// buf is a temporary buffer where decoded strings are batched.
 | 
						|
	buf []byte
 | 
						|
	// offset is a buffer position where the last string was written.
 | 
						|
	offset int
 | 
						|
}
 | 
						|
 | 
						|
// String converts bytes to a string.
 | 
						|
func (c *stringConverter) String(b []byte) string {
 | 
						|
	n := len(b)
 | 
						|
	if n == 0 {
 | 
						|
		return ""
 | 
						|
	}
 | 
						|
	// Must allocate because a string doesn't fit into the buffer.
 | 
						|
	if n > cap(c.buf) {
 | 
						|
		return string(b)
 | 
						|
	}
 | 
						|
 | 
						|
	if len(c.buf)+n > cap(c.buf) {
 | 
						|
		c.buf = make([]byte, 0, cap(c.buf))
 | 
						|
		c.offset = 0
 | 
						|
	}
 | 
						|
	c.buf = append(c.buf, b...)
 | 
						|
 | 
						|
	b = c.buf[c.offset:]
 | 
						|
	s := toString(b)
 | 
						|
	c.offset += n
 | 
						|
	return s
 | 
						|
}
 | 
						|
 | 
						|
// toString converts a byte slice to a string without allocating.
 | 
						|
// Starting from Go 1.20 you should use unsafe.String.
 | 
						|
func toString(b []byte) string {
 | 
						|
	var s string
 | 
						|
	h := (*reflect.StringHeader)(unsafe.Pointer(&s))
 | 
						|
	h.Data = uintptr(unsafe.Pointer(&b[0]))
 | 
						|
	h.Len = len(b)
 | 
						|
 | 
						|
	return s
 | 
						|
}
 |