btrfs_stream: Perl parsing library

Btrfs is a copy on write file system based on B-trees focusing on fault tolerance, repair and easy administration. Btrfs is intended to address the lack of pooling, snapshots, checksums, and integral multi-device spanning in Linux file systems. Given any pair of subvolumes (or snapshots), Btrfs can generate a binary diff between them by using the btrfs send command that can be replayed later by using btrfs receive, possibly on a different Btrfs file system. The btrfs send command creates a set of data modifications required for converting one subvolume into another. This spec can be used to disassemble the binary diff created by the btrfs send command. If you want a text representation you may want to checkout btrfs receive --dump instead.

Application

Btrfs

KS implementation details

License: CC0-1.0

References

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

Perl source code to parse btrfs_stream

BtrfsStream.pm

# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

use strict;
use warnings;
use IO::KaitaiStruct 0.009_000;
use Encode;

########################################################################
package BtrfsStream;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

our $COMMAND_UNSPEC = 0;
our $COMMAND_SUBVOL = 1;
our $COMMAND_SNAPSHOT = 2;
our $COMMAND_MKFILE = 3;
our $COMMAND_MKDIR = 4;
our $COMMAND_MKNOD = 5;
our $COMMAND_MKFIFO = 6;
our $COMMAND_MKSOCK = 7;
our $COMMAND_SYMLINK = 8;
our $COMMAND_RENAME = 9;
our $COMMAND_LINK = 10;
our $COMMAND_UNLINK = 11;
our $COMMAND_RMDIR = 12;
our $COMMAND_SET_XATTR = 13;
our $COMMAND_REMOVE_XATTR = 14;
our $COMMAND_WRITE = 15;
our $COMMAND_CLONE = 16;
our $COMMAND_TRUNCATE = 17;
our $COMMAND_CHMOD = 18;
our $COMMAND_CHOWN = 19;
our $COMMAND_UTIMES = 20;
our $COMMAND_END = 21;
our $COMMAND_UPDATE_EXTENT = 22;

our $ATTRIBUTE_UNSPEC = 0;
our $ATTRIBUTE_UUID = 1;
our $ATTRIBUTE_CTRANSID = 2;
our $ATTRIBUTE_INO = 3;
our $ATTRIBUTE_SIZE = 4;
our $ATTRIBUTE_MODE = 5;
our $ATTRIBUTE_UID = 6;
our $ATTRIBUTE_GID = 7;
our $ATTRIBUTE_RDEV = 8;
our $ATTRIBUTE_CTIME = 9;
our $ATTRIBUTE_MTIME = 10;
our $ATTRIBUTE_ATIME = 11;
our $ATTRIBUTE_OTIME = 12;
our $ATTRIBUTE_XATTR_NAME = 13;
our $ATTRIBUTE_XATTR_DATA = 14;
our $ATTRIBUTE_PATH = 15;
our $ATTRIBUTE_PATH_TO = 16;
our $ATTRIBUTE_PATH_LINK = 17;
our $ATTRIBUTE_FILE_OFFSET = 18;
our $ATTRIBUTE_DATA = 19;
our $ATTRIBUTE_CLONE_UUID = 20;
our $ATTRIBUTE_CLONE_CTRANSID = 21;
our $ATTRIBUTE_CLONE_PATH = 22;
our $ATTRIBUTE_CLONE_OFFSET = 23;
our $ATTRIBUTE_CLONE_LEN = 24;

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{header} = BtrfsStream::SendStreamHeader->new($self->{_io}, $self, $self->{_root});
    $self->{commands} = ();
    while (!$self->{_io}->is_eof()) {
        push @{$self->{commands}}, BtrfsStream::SendCommand->new($self->{_io}, $self, $self->{_root});
    }
}

sub header {
    my ($self) = @_;
    return $self->{header};
}

sub commands {
    my ($self) = @_;
    return $self->{commands};
}

########################################################################
package BtrfsStream::SendStreamHeader;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{magic} = $self->{_io}->read_bytes(13);
    $self->{version} = $self->{_io}->read_u4le();
}

sub magic {
    my ($self) = @_;
    return $self->{magic};
}

sub version {
    my ($self) = @_;
    return $self->{version};
}

########################################################################
package BtrfsStream::SendCommand;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{len_data} = $self->{_io}->read_u4le();
    $self->{type} = $self->{_io}->read_u2le();
    $self->{checksum} = $self->{_io}->read_bytes(4);
    $self->{_raw_data} = $self->{_io}->read_bytes($self->len_data());
    my $io__raw_data = IO::KaitaiStruct::Stream->new($self->{_raw_data});
    $self->{data} = BtrfsStream::SendCommand::Tlvs->new($io__raw_data, $self, $self->{_root});
}

sub len_data {
    my ($self) = @_;
    return $self->{len_data};
}

sub type {
    my ($self) = @_;
    return $self->{type};
}

sub checksum {
    my ($self) = @_;
    return $self->{checksum};
}

sub data {
    my ($self) = @_;
    return $self->{data};
}

sub _raw_data {
    my ($self) = @_;
    return $self->{_raw_data};
}

########################################################################
package BtrfsStream::SendCommand::Tlv;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{type} = $self->{_io}->read_u2le();
    $self->{length} = $self->{_io}->read_u2le();
    my $_on = $self->type();
    if ($_on == $BtrfsStream::ATTRIBUTE_CTRANSID) {
        $self->{value} = $self->{_io}->read_u8le();
    }
    elsif ($_on == $BtrfsStream::ATTRIBUTE_SIZE) {
        $self->{value} = $self->{_io}->read_u8le();
    }
    elsif ($_on == $BtrfsStream::ATTRIBUTE_CLONE_UUID) {
        $self->{_raw_value} = $self->{_io}->read_bytes($self->length());
        my $io__raw_value = IO::KaitaiStruct::Stream->new($self->{_raw_value});
        $self->{value} = BtrfsStream::SendCommand::Uuid->new($io__raw_value, $self, $self->{_root});
    }
    elsif ($_on == $BtrfsStream::ATTRIBUTE_FILE_OFFSET) {
        $self->{value} = $self->{_io}->read_u8le();
    }
    elsif ($_on == $BtrfsStream::ATTRIBUTE_OTIME) {
        $self->{_raw_value} = $self->{_io}->read_bytes($self->length());
        my $io__raw_value = IO::KaitaiStruct::Stream->new($self->{_raw_value});
        $self->{value} = BtrfsStream::SendCommand::Timespec->new($io__raw_value, $self, $self->{_root});
    }
    elsif ($_on == $BtrfsStream::ATTRIBUTE_UID) {
        $self->{value} = $self->{_io}->read_u8le();
    }
    elsif ($_on == $BtrfsStream::ATTRIBUTE_ATIME) {
        $self->{_raw_value} = $self->{_io}->read_bytes($self->length());
        my $io__raw_value = IO::KaitaiStruct::Stream->new($self->{_raw_value});
        $self->{value} = BtrfsStream::SendCommand::Timespec->new($io__raw_value, $self, $self->{_root});
    }
    elsif ($_on == $BtrfsStream::ATTRIBUTE_CTIME) {
        $self->{_raw_value} = $self->{_io}->read_bytes($self->length());
        my $io__raw_value = IO::KaitaiStruct::Stream->new($self->{_raw_value});
        $self->{value} = BtrfsStream::SendCommand::Timespec->new($io__raw_value, $self, $self->{_root});
    }
    elsif ($_on == $BtrfsStream::ATTRIBUTE_UUID) {
        $self->{_raw_value} = $self->{_io}->read_bytes($self->length());
        my $io__raw_value = IO::KaitaiStruct::Stream->new($self->{_raw_value});
        $self->{value} = BtrfsStream::SendCommand::Uuid->new($io__raw_value, $self, $self->{_root});
    }
    elsif ($_on == $BtrfsStream::ATTRIBUTE_CLONE_LEN) {
        $self->{value} = $self->{_io}->read_u8le();
    }
    elsif ($_on == $BtrfsStream::ATTRIBUTE_XATTR_NAME) {
        $self->{_raw_value} = $self->{_io}->read_bytes($self->length());
        my $io__raw_value = IO::KaitaiStruct::Stream->new($self->{_raw_value});
        $self->{value} = BtrfsStream::SendCommand::String->new($io__raw_value, $self, $self->{_root});
    }
    elsif ($_on == $BtrfsStream::ATTRIBUTE_CLONE_CTRANSID) {
        $self->{value} = $self->{_io}->read_u8le();
    }
    elsif ($_on == $BtrfsStream::ATTRIBUTE_MODE) {
        $self->{value} = $self->{_io}->read_u8le();
    }
    elsif ($_on == $BtrfsStream::ATTRIBUTE_MTIME) {
        $self->{_raw_value} = $self->{_io}->read_bytes($self->length());
        my $io__raw_value = IO::KaitaiStruct::Stream->new($self->{_raw_value});
        $self->{value} = BtrfsStream::SendCommand::Timespec->new($io__raw_value, $self, $self->{_root});
    }
    elsif ($_on == $BtrfsStream::ATTRIBUTE_PATH_LINK) {
        $self->{_raw_value} = $self->{_io}->read_bytes($self->length());
        my $io__raw_value = IO::KaitaiStruct::Stream->new($self->{_raw_value});
        $self->{value} = BtrfsStream::SendCommand::String->new($io__raw_value, $self, $self->{_root});
    }
    elsif ($_on == $BtrfsStream::ATTRIBUTE_RDEV) {
        $self->{value} = $self->{_io}->read_u8le();
    }
    elsif ($_on == $BtrfsStream::ATTRIBUTE_PATH_TO) {
        $self->{_raw_value} = $self->{_io}->read_bytes($self->length());
        my $io__raw_value = IO::KaitaiStruct::Stream->new($self->{_raw_value});
        $self->{value} = BtrfsStream::SendCommand::String->new($io__raw_value, $self, $self->{_root});
    }
    elsif ($_on == $BtrfsStream::ATTRIBUTE_PATH) {
        $self->{_raw_value} = $self->{_io}->read_bytes($self->length());
        my $io__raw_value = IO::KaitaiStruct::Stream->new($self->{_raw_value});
        $self->{value} = BtrfsStream::SendCommand::String->new($io__raw_value, $self, $self->{_root});
    }
    elsif ($_on == $BtrfsStream::ATTRIBUTE_CLONE_OFFSET) {
        $self->{value} = $self->{_io}->read_u8le();
    }
    elsif ($_on == $BtrfsStream::ATTRIBUTE_GID) {
        $self->{value} = $self->{_io}->read_u8le();
    }
    elsif ($_on == $BtrfsStream::ATTRIBUTE_CLONE_PATH) {
        $self->{_raw_value} = $self->{_io}->read_bytes($self->length());
        my $io__raw_value = IO::KaitaiStruct::Stream->new($self->{_raw_value});
        $self->{value} = BtrfsStream::SendCommand::String->new($io__raw_value, $self, $self->{_root});
    }
    else {
        $self->{value} = $self->{_io}->read_bytes($self->length());
    }
}

sub type {
    my ($self) = @_;
    return $self->{type};
}

sub length {
    my ($self) = @_;
    return $self->{length};
}

sub value {
    my ($self) = @_;
    return $self->{value};
}

sub _raw_value {
    my ($self) = @_;
    return $self->{_raw_value};
}

########################################################################
package BtrfsStream::SendCommand::Uuid;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{uuid} = $self->{_io}->read_bytes(16);
}

sub uuid {
    my ($self) = @_;
    return $self->{uuid};
}

########################################################################
package BtrfsStream::SendCommand::Tlvs;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{tlv} = ();
    while (!$self->{_io}->is_eof()) {
        push @{$self->{tlv}}, BtrfsStream::SendCommand::Tlv->new($self->{_io}, $self, $self->{_root});
    }
}

sub tlv {
    my ($self) = @_;
    return $self->{tlv};
}

########################################################################
package BtrfsStream::SendCommand::String;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{string} = Encode::decode("UTF-8", $self->{_io}->read_bytes_full());
}

sub string {
    my ($self) = @_;
    return $self->{string};
}

########################################################################
package BtrfsStream::SendCommand::Timespec;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{ts_sec} = $self->{_io}->read_s8le();
    $self->{ts_nsec} = $self->{_io}->read_s4le();
}

sub ts_sec {
    my ($self) = @_;
    return $self->{ts_sec};
}

sub ts_nsec {
    my ($self) = @_;
    return $self->{ts_nsec};
}

1;