github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/lib/images/virtualbox/reader.go (about)

     1  package virtualbox
     2  
     3  import (
     4  	"bufio"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"io"
     8  
     9  	"github.com/Cloud-Foundations/Dominator/lib/format"
    10  )
    11  
    12  type headerType struct {
    13  	Header          [0x40]byte
    14  	Signature       uint32
    15  	VersionMajor    uint16
    16  	VersionMinor    uint16
    17  	HeaderSize      uint32
    18  	ImageType       uint32
    19  	ImageFlags      uint32
    20  	Description     [0x100]byte
    21  	OffsetBocks     uint32
    22  	OffsetData      uint32
    23  	Cylinders       uint32
    24  	Heads           uint32
    25  	Sectors         uint32
    26  	SectorSize      uint32
    27  	Unused          [4]byte
    28  	DiscSize        uint64
    29  	BlockSize       uint32
    30  	BlockExtraData  uint32
    31  	BlocksInHDD     uint32
    32  	BlocksAllocated uint32
    33  	UuidImage       uint64
    34  	UuidLastSnap    uint64
    35  	UuidLink        uint64
    36  	UuidParent      uint64
    37  }
    38  
    39  func newReader(rawReader io.Reader) (*Reader, error) {
    40  	r := bufio.NewReaderSize(rawReader, 1<<20)
    41  	var header headerType
    42  	if err := binary.Read(r, binary.LittleEndian, &header); err != nil {
    43  		return nil, fmt.Errorf("error reading VDI header: %s", err)
    44  	}
    45  	if header.Signature != 0xbeda107f {
    46  		return nil, fmt.Errorf("%x not a VDI signature", header.Signature)
    47  	}
    48  	if header.VersionMajor < 1 {
    49  		return nil,
    50  			fmt.Errorf("VDI major version: %d not supported",
    51  				header.VersionMajor)
    52  	}
    53  	if header.ImageType != 1 {
    54  		return nil, fmt.Errorf("VDI image type: %d not supported",
    55  			header.ImageType)
    56  	}
    57  	if header.BlockSize != 1<<20 {
    58  		return nil, fmt.Errorf("VDI block size: 0x%x (%s) not supported",
    59  			header.BlockSize, format.FormatBytes(uint64(header.BlockSize)))
    60  	}
    61  	mul := uint64(header.BlockSize) * uint64(header.BlocksInHDD)
    62  	if mul != header.DiscSize {
    63  		return nil, fmt.Errorf("blockSize*blocksInHdd: %d != discSize: %d",
    64  			mul, header.DiscSize)
    65  	}
    66  	r.Reset(rawReader) // Discard until 1 MiB boundary.
    67  	lastPointer := int32(-1)
    68  	blockMap := make(map[uint32]struct{}, header.BlocksInHDD)
    69  	for index := uint32(0); index < header.BlocksInHDD; index++ {
    70  		var pointer int32
    71  		if err := binary.Read(r, binary.LittleEndian, &pointer); err != nil {
    72  			return nil,
    73  				fmt.Errorf("error reading block pointer: %d: %s", index, err)
    74  		}
    75  		if pointer >= 0 {
    76  			if pointer <= lastPointer {
    77  				return nil,
    78  					fmt.Errorf("VDI pointer: %d not greater than last: %d",
    79  						pointer, lastPointer)
    80  			}
    81  			blockMap[index] = struct{}{}
    82  		}
    83  		lastPointer = pointer
    84  	}
    85  	r.Reset(rawReader) // Discard until 1 MiB boundary.
    86  	return &Reader{
    87  		Description:  string(header.Description[:]),
    88  		Header:       string(header.Header[:]),
    89  		MajorVersion: header.VersionMajor,
    90  		MinorVersion: header.VersionMinor,
    91  		Size:         header.DiscSize,
    92  		blockMap:     blockMap,
    93  		blockSize:    header.BlockSize,
    94  		blocksInHDD:  header.BlocksInHDD,
    95  		reader:       r,
    96  	}, nil
    97  }
    98  
    99  func (r *Reader) read(p []byte) (int, error) {
   100  	if r.blockIndex >= r.blocksInHDD {
   101  		return 0, io.EOF
   102  	}
   103  	blockIndex := r.blockIndex
   104  	if maxLength := r.blockSize - r.blockOffset; uint32(len(p)) >= maxLength {
   105  		p = p[:maxLength]
   106  		r.blockIndex++
   107  		r.blockOffset = 0
   108  	} else {
   109  		r.blockOffset += uint32(len(p))
   110  	}
   111  	if _, ok := r.blockMap[blockIndex]; !ok {
   112  		for index := range p {
   113  			p[index] = 0
   114  		}
   115  		return len(p), nil
   116  	}
   117  	return io.ReadFull(r.reader, p)
   118  }