zisofs: Go parsing library

zisofs is a compression format for files on ISO9660 file system. It has limited support across operating systems, mainly Linux kernel. Typically a directory tree is first preprocessed by mkzftree (from the zisofs-tools package before being turned into an ISO9660 image by mkisofs, genisoimage or similar tool. The data is zlib compressed.

The specification here describes the structure of a file that has been preprocessed by mkzftree, not of a full ISO9660 ziso. Data is not decompressed, as blocks with length 0 have a special meaning. Decompression and deconstruction of this data should be done outside of Kaitai Struct.

KS implementation details

License: CC0-1.0

References

This page hosts a formal specification of zisofs using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Go source code to parse zisofs

zisofs.go

// Code generated by kaitai-struct-compiler from a .ksy source file. DO NOT EDIT.

import (
	"github.com/kaitai-io/kaitai_struct_go_runtime/kaitai"
	"bytes"
	"io"
)


/**
 * zisofs is a compression format for files on ISO9660 file system. It has
 * limited support across operating systems, mainly Linux kernel. Typically a
 * directory tree is first preprocessed by mkzftree (from the zisofs-tools
 * package before being turned into an ISO9660 image by mkisofs, genisoimage
 * or similar tool. The data is zlib compressed.
 * 
 * The specification here describes the structure of a file that has been
 * preprocessed by mkzftree, not of a full ISO9660 ziso. Data is not
 * decompressed, as blocks with length 0 have a special meaning. Decompression
 * and deconstruction of this data should be done outside of Kaitai Struct.
 * @see <a href="https://web.archive.org/web/20200612093441/https://dev.lovelyhq.com/libburnia/web/-/wikis/zisofs">Source</a>
 */
type Zisofs struct {
	Header *Zisofs_Header
	BlockPointers []uint32
	_io *kaitai.Stream
	_root *Zisofs
	_parent interface{}
	_raw_Header []byte
	_f_blocks bool
	blocks []*Zisofs_Block
}
func NewZisofs() *Zisofs {
	return &Zisofs{
	}
}

func (this *Zisofs) Read(io *kaitai.Stream, parent interface{}, root *Zisofs) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp1, err := this._io.ReadBytes(int(16))
	if err != nil {
		return err
	}
	tmp1 = tmp1
	this._raw_Header = tmp1
	_io__raw_Header := kaitai.NewStream(bytes.NewReader(this._raw_Header))
	tmp2 := NewZisofs_Header()
	err = tmp2.Read(_io__raw_Header, this, this._root)
	if err != nil {
		return err
	}
	this.Header = tmp2
	tmp3, err := this.Header.NumBlocks()
	if err != nil {
		return err
	}
	for i := 0; i < int((tmp3 + 1)); i++ {
		_ = i
		tmp4, err := this._io.ReadU4le()
		if err != nil {
			return err
		}
		this.BlockPointers = append(this.BlockPointers, tmp4)
	}
	return err
}
func (this *Zisofs) Blocks() (v []*Zisofs_Block, err error) {
	if (this._f_blocks) {
		return this.blocks, nil
	}
	tmp5, err := this.Header.NumBlocks()
	if err != nil {
		return nil, err
	}
	for i := 0; i < int(tmp5); i++ {
		_ = i
		tmp6 := NewZisofs_Block(this.BlockPointers[i], this.BlockPointers[(i + 1)])
		err = tmp6.Read(this._io, this, this._root)
		if err != nil {
			return nil, err
		}
		this.blocks = append(this.blocks, tmp6)
	}
	this._f_blocks = true
	this._f_blocks = true
	return this.blocks, nil
}

/**
 * The final pointer (`block_pointers[header.num_blocks]`) indicates the end
 * of the last block. Typically this is also the end of the file data.
 */
type Zisofs_Header struct {
	Magic []byte
	UncompressedSize uint32
	LenHeader uint8
	BlockSizeLog2 uint8
	Reserved []byte
	_io *kaitai.Stream
	_root *Zisofs
	_parent *Zisofs
	_f_blockSize bool
	blockSize int
	_f_numBlocks bool
	numBlocks int
}
func NewZisofs_Header() *Zisofs_Header {
	return &Zisofs_Header{
	}
}

func (this *Zisofs_Header) Read(io *kaitai.Stream, parent *Zisofs, root *Zisofs) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp7, err := this._io.ReadBytes(int(8))
	if err != nil {
		return err
	}
	tmp7 = tmp7
	this.Magic = tmp7
	if !(bytes.Equal(this.Magic, []uint8{55, 228, 83, 150, 201, 219, 214, 7})) {
		return kaitai.NewValidationNotEqualError([]uint8{55, 228, 83, 150, 201, 219, 214, 7}, this.Magic, this._io, "/types/header/seq/0")
	}
	tmp8, err := this._io.ReadU4le()
	if err != nil {
		return err
	}
	this.UncompressedSize = uint32(tmp8)
	tmp9, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.LenHeader = tmp9
	if !(this.LenHeader == 4) {
		return kaitai.NewValidationNotEqualError(4, this.LenHeader, this._io, "/types/header/seq/2")
	}
	tmp10, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.BlockSizeLog2 = tmp10
	if !( ((this.BlockSizeLog2 == 15) || (this.BlockSizeLog2 == 16) || (this.BlockSizeLog2 == 17)) ) {
		return kaitai.NewValidationNotAnyOfError(this.BlockSizeLog2, this._io, "/types/header/seq/3")
	}
	tmp11, err := this._io.ReadBytes(int(2))
	if err != nil {
		return err
	}
	tmp11 = tmp11
	this.Reserved = tmp11
	if !(bytes.Equal(this.Reserved, []uint8{0, 0})) {
		return kaitai.NewValidationNotEqualError([]uint8{0, 0}, this.Reserved, this._io, "/types/header/seq/4")
	}
	return err
}
func (this *Zisofs_Header) BlockSize() (v int, err error) {
	if (this._f_blockSize) {
		return this.blockSize, nil
	}
	this.blockSize = int((1 << this.BlockSizeLog2))
	this._f_blockSize = true
	return this.blockSize, nil
}

/**
 * ceil(uncompressed_size / block_size)
 */
func (this *Zisofs_Header) NumBlocks() (v int, err error) {
	if (this._f_numBlocks) {
		return this.numBlocks, nil
	}
	tmp12, err := this.BlockSize()
	if err != nil {
		return 0, err
	}
	var tmp13 int8;
	tmp15, err := this.BlockSize()
	if err != nil {
		return 0, err
	}
	tmp14 := this.UncompressedSize % tmp15
	if tmp14 < 0 {
		tmp16, err := this.BlockSize()
		if err != nil {
			return 0, err
		}
		tmp14 += tmp16
	}
	if (tmp14 != 0) {
		tmp13 = 1
	} else {
		tmp13 = 0
	}
	this.numBlocks = int(((this.UncompressedSize / tmp12) + tmp13))
	this._f_numBlocks = true
	return this.numBlocks, nil
}

/**
 * Size of the original uncompressed file
 */

/**
 * header_size >> 2 (currently 4)
 */
type Zisofs_Block struct {
	OfsStart uint32
	OfsEnd uint32
	_io *kaitai.Stream
	_root *Zisofs
	_parent *Zisofs
	_f_lenData bool
	lenData int
	_f_data bool
	data []byte
}
func NewZisofs_Block(ofsStart uint32, ofsEnd uint32) *Zisofs_Block {
	return &Zisofs_Block{
		OfsStart: ofsStart,
		OfsEnd: ofsEnd,
	}
}

func (this *Zisofs_Block) Read(io *kaitai.Stream, parent *Zisofs, root *Zisofs) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	return err
}
func (this *Zisofs_Block) LenData() (v int, err error) {
	if (this._f_lenData) {
		return this.lenData, nil
	}
	this.lenData = int((this.OfsEnd - this.OfsStart))
	this._f_lenData = true
	return this.lenData, nil
}
func (this *Zisofs_Block) Data() (v []byte, err error) {
	if (this._f_data) {
		return this.data, nil
	}
	thisIo := this._root._io
	_pos, err := thisIo.Pos()
	if err != nil {
		return nil, err
	}
	_, err = thisIo.Seek(int64(this.OfsStart), io.SeekStart)
	if err != nil {
		return nil, err
	}
	tmp17, err := this.LenData()
	if err != nil {
		return nil, err
	}
	tmp18, err := thisIo.ReadBytes(int(tmp17))
	if err != nil {
		return nil, err
	}
	tmp18 = tmp18
	this.data = tmp18
	_, err = thisIo.Seek(_pos, io.SeekStart)
	if err != nil {
		return nil, err
	}
	this._f_data = true
	this._f_data = true
	return this.data, nil
}