This page hosts a formal specification of vfat using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.
# 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;
use DosDatetime;
########################################################################
package Vfat;
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->{boot_sector} = Vfat::BootSector->new($self->{_io}, $self, $self->{_root});
}
sub fats {
my ($self) = @_;
return $self->{fats} if ($self->{fats});
my $_pos = $self->{_io}->pos();
$self->{_io}->seek($self->boot_sector()->pos_fats());
$self->{fats} = ();
my $n_fats = $self->boot_sector()->bpb()->num_fats();
for (my $i = 0; $i < $n_fats; $i++) {
push @{$self->{fats}}, $self->{_io}->read_bytes($self->boot_sector()->size_fat());
}
$self->{_io}->seek($_pos);
return $self->{fats};
}
sub root_dir {
my ($self) = @_;
return $self->{root_dir} if ($self->{root_dir});
my $_pos = $self->{_io}->pos();
$self->{_io}->seek($self->boot_sector()->pos_root_dir());
$self->{_raw_root_dir} = $self->{_io}->read_bytes($self->boot_sector()->size_root_dir());
my $io__raw_root_dir = IO::KaitaiStruct::Stream->new($self->{_raw_root_dir});
$self->{root_dir} = Vfat::RootDirectory->new($io__raw_root_dir, $self, $self->{_root});
$self->{_io}->seek($_pos);
return $self->{root_dir};
}
sub boot_sector {
my ($self) = @_;
return $self->{boot_sector};
}
sub _raw_root_dir {
my ($self) = @_;
return $self->{_raw_root_dir};
}
########################################################################
package Vfat::ExtBiosParamBlockFat32;
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->{ls_per_fat} = $self->{_io}->read_u4le();
$self->{has_active_fat} = $self->{_io}->read_bits_int_le(1);
$self->{reserved1} = $self->{_io}->read_bits_int_le(3);
$self->{active_fat_id} = $self->{_io}->read_bits_int_le(4);
$self->{_io}->align_to_byte();
$self->{reserved2} = $self->{_io}->read_bytes(1);
$self->{fat_version} = $self->{_io}->read_u2le();
$self->{root_dir_start_clus} = $self->{_io}->read_u4le();
$self->{ls_fs_info} = $self->{_io}->read_u2le();
$self->{boot_sectors_copy_start_ls} = $self->{_io}->read_u2le();
$self->{reserved3} = $self->{_io}->read_bytes(12);
$self->{phys_drive_num} = $self->{_io}->read_u1();
$self->{reserved4} = $self->{_io}->read_u1();
$self->{ext_boot_sign} = $self->{_io}->read_u1();
$self->{volume_id} = $self->{_io}->read_bytes(4);
$self->{partition_volume_label} = Encode::decode("ASCII", IO::KaitaiStruct::Stream::bytes_strip_right($self->{_io}->read_bytes(11), 32));
$self->{fs_type_str} = Encode::decode("ASCII", IO::KaitaiStruct::Stream::bytes_strip_right($self->{_io}->read_bytes(8), 32));
}
sub ls_per_fat {
my ($self) = @_;
return $self->{ls_per_fat};
}
sub has_active_fat {
my ($self) = @_;
return $self->{has_active_fat};
}
sub reserved1 {
my ($self) = @_;
return $self->{reserved1};
}
sub active_fat_id {
my ($self) = @_;
return $self->{active_fat_id};
}
sub reserved2 {
my ($self) = @_;
return $self->{reserved2};
}
sub fat_version {
my ($self) = @_;
return $self->{fat_version};
}
sub root_dir_start_clus {
my ($self) = @_;
return $self->{root_dir_start_clus};
}
sub ls_fs_info {
my ($self) = @_;
return $self->{ls_fs_info};
}
sub boot_sectors_copy_start_ls {
my ($self) = @_;
return $self->{boot_sectors_copy_start_ls};
}
sub reserved3 {
my ($self) = @_;
return $self->{reserved3};
}
sub phys_drive_num {
my ($self) = @_;
return $self->{phys_drive_num};
}
sub reserved4 {
my ($self) = @_;
return $self->{reserved4};
}
sub ext_boot_sign {
my ($self) = @_;
return $self->{ext_boot_sign};
}
sub volume_id {
my ($self) = @_;
return $self->{volume_id};
}
sub partition_volume_label {
my ($self) = @_;
return $self->{partition_volume_label};
}
sub fs_type_str {
my ($self) = @_;
return $self->{fs_type_str};
}
########################################################################
package Vfat::BootSector;
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->{jmp_instruction} = $self->{_io}->read_bytes(3);
$self->{oem_name} = Encode::decode("ASCII", IO::KaitaiStruct::Stream::bytes_strip_right($self->{_io}->read_bytes(8), 32));
$self->{bpb} = Vfat::BiosParamBlock->new($self->{_io}, $self, $self->{_root});
if (!($self->is_fat32())) {
$self->{ebpb_fat16} = Vfat::ExtBiosParamBlockFat16->new($self->{_io}, $self, $self->{_root});
}
if ($self->is_fat32()) {
$self->{ebpb_fat32} = Vfat::ExtBiosParamBlockFat32->new($self->{_io}, $self, $self->{_root});
}
}
sub pos_fats {
my ($self) = @_;
return $self->{pos_fats} if ($self->{pos_fats});
$self->{pos_fats} = ($self->bpb()->bytes_per_ls() * $self->bpb()->num_reserved_ls());
return $self->{pos_fats};
}
sub ls_per_fat {
my ($self) = @_;
return $self->{ls_per_fat} if ($self->{ls_per_fat});
$self->{ls_per_fat} = ($self->is_fat32() ? $self->ebpb_fat32()->ls_per_fat() : $self->bpb()->ls_per_fat());
return $self->{ls_per_fat};
}
sub ls_per_root_dir {
my ($self) = @_;
return $self->{ls_per_root_dir} if ($self->{ls_per_root_dir});
$self->{ls_per_root_dir} = int(((($self->bpb()->max_root_dir_rec() * 32) + $self->bpb()->bytes_per_ls()) - 1) / $self->bpb()->bytes_per_ls());
return $self->{ls_per_root_dir};
}
sub is_fat32 {
my ($self) = @_;
return $self->{is_fat32} if ($self->{is_fat32});
$self->{is_fat32} = $self->bpb()->max_root_dir_rec() == 0;
return $self->{is_fat32};
}
sub size_fat {
my ($self) = @_;
return $self->{size_fat} if ($self->{size_fat});
$self->{size_fat} = ($self->bpb()->bytes_per_ls() * $self->ls_per_fat());
return $self->{size_fat};
}
sub pos_root_dir {
my ($self) = @_;
return $self->{pos_root_dir} if ($self->{pos_root_dir});
$self->{pos_root_dir} = ($self->bpb()->bytes_per_ls() * ($self->bpb()->num_reserved_ls() + ($self->ls_per_fat() * $self->bpb()->num_fats())));
return $self->{pos_root_dir};
}
sub size_root_dir {
my ($self) = @_;
return $self->{size_root_dir} if ($self->{size_root_dir});
$self->{size_root_dir} = ($self->ls_per_root_dir() * $self->bpb()->bytes_per_ls());
return $self->{size_root_dir};
}
sub jmp_instruction {
my ($self) = @_;
return $self->{jmp_instruction};
}
sub oem_name {
my ($self) = @_;
return $self->{oem_name};
}
sub bpb {
my ($self) = @_;
return $self->{bpb};
}
sub ebpb_fat16 {
my ($self) = @_;
return $self->{ebpb_fat16};
}
sub ebpb_fat32 {
my ($self) = @_;
return $self->{ebpb_fat32};
}
########################################################################
package Vfat::BiosParamBlock;
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->{bytes_per_ls} = $self->{_io}->read_u2le();
$self->{ls_per_clus} = $self->{_io}->read_u1();
$self->{num_reserved_ls} = $self->{_io}->read_u2le();
$self->{num_fats} = $self->{_io}->read_u1();
$self->{max_root_dir_rec} = $self->{_io}->read_u2le();
$self->{total_ls_2} = $self->{_io}->read_u2le();
$self->{media_code} = $self->{_io}->read_u1();
$self->{ls_per_fat} = $self->{_io}->read_u2le();
$self->{ps_per_track} = $self->{_io}->read_u2le();
$self->{num_heads} = $self->{_io}->read_u2le();
$self->{num_hidden_sectors} = $self->{_io}->read_u4le();
$self->{total_ls_4} = $self->{_io}->read_u4le();
}
sub bytes_per_ls {
my ($self) = @_;
return $self->{bytes_per_ls};
}
sub ls_per_clus {
my ($self) = @_;
return $self->{ls_per_clus};
}
sub num_reserved_ls {
my ($self) = @_;
return $self->{num_reserved_ls};
}
sub num_fats {
my ($self) = @_;
return $self->{num_fats};
}
sub max_root_dir_rec {
my ($self) = @_;
return $self->{max_root_dir_rec};
}
sub total_ls_2 {
my ($self) = @_;
return $self->{total_ls_2};
}
sub media_code {
my ($self) = @_;
return $self->{media_code};
}
sub ls_per_fat {
my ($self) = @_;
return $self->{ls_per_fat};
}
sub ps_per_track {
my ($self) = @_;
return $self->{ps_per_track};
}
sub num_heads {
my ($self) = @_;
return $self->{num_heads};
}
sub num_hidden_sectors {
my ($self) = @_;
return $self->{num_hidden_sectors};
}
sub total_ls_4 {
my ($self) = @_;
return $self->{total_ls_4};
}
########################################################################
package Vfat::RootDirectoryRec;
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->{file_name} = $self->{_io}->read_bytes(11);
$self->{_raw_attrs} = $self->{_io}->read_bytes(1);
my $io__raw_attrs = IO::KaitaiStruct::Stream->new($self->{_raw_attrs});
$self->{attrs} = Vfat::RootDirectoryRec::AttrFlags->new($io__raw_attrs, $self, $self->{_root});
$self->{reserved} = $self->{_io}->read_bytes(10);
$self->{_raw_last_write_time} = $self->{_io}->read_bytes(4);
my $io__raw_last_write_time = IO::KaitaiStruct::Stream->new($self->{_raw_last_write_time});
$self->{last_write_time} = DosDatetime->new($io__raw_last_write_time);
$self->{start_clus} = $self->{_io}->read_u2le();
$self->{file_size} = $self->{_io}->read_u4le();
}
sub file_name {
my ($self) = @_;
return $self->{file_name};
}
sub attrs {
my ($self) = @_;
return $self->{attrs};
}
sub reserved {
my ($self) = @_;
return $self->{reserved};
}
sub last_write_time {
my ($self) = @_;
return $self->{last_write_time};
}
sub start_clus {
my ($self) = @_;
return $self->{start_clus};
}
sub file_size {
my ($self) = @_;
return $self->{file_size};
}
sub _raw_attrs {
my ($self) = @_;
return $self->{_raw_attrs};
}
sub _raw_last_write_time {
my ($self) = @_;
return $self->{_raw_last_write_time};
}
########################################################################
package Vfat::RootDirectoryRec::AttrFlags;
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->{read_only} = $self->{_io}->read_bits_int_le(1);
$self->{hidden} = $self->{_io}->read_bits_int_le(1);
$self->{system} = $self->{_io}->read_bits_int_le(1);
$self->{volume_id} = $self->{_io}->read_bits_int_le(1);
$self->{is_directory} = $self->{_io}->read_bits_int_le(1);
$self->{archive} = $self->{_io}->read_bits_int_le(1);
$self->{reserved} = $self->{_io}->read_bits_int_le(2);
}
sub long_name {
my ($self) = @_;
return $self->{long_name} if ($self->{long_name});
$self->{long_name} = (($self->read_only()) && ($self->hidden()) && ($self->system()) && ($self->volume_id())) ;
return $self->{long_name};
}
sub read_only {
my ($self) = @_;
return $self->{read_only};
}
sub hidden {
my ($self) = @_;
return $self->{hidden};
}
sub system {
my ($self) = @_;
return $self->{system};
}
sub volume_id {
my ($self) = @_;
return $self->{volume_id};
}
sub is_directory {
my ($self) = @_;
return $self->{is_directory};
}
sub archive {
my ($self) = @_;
return $self->{archive};
}
sub reserved {
my ($self) = @_;
return $self->{reserved};
}
########################################################################
package Vfat::RootDirectory;
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->{records} = ();
my $n_records = $self->_root()->boot_sector()->bpb()->max_root_dir_rec();
for (my $i = 0; $i < $n_records; $i++) {
push @{$self->{records}}, Vfat::RootDirectoryRec->new($self->{_io}, $self, $self->{_root});
}
}
sub records {
my ($self) = @_;
return $self->{records};
}
########################################################################
package Vfat::ExtBiosParamBlockFat16;
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->{phys_drive_num} = $self->{_io}->read_u1();
$self->{reserved1} = $self->{_io}->read_u1();
$self->{ext_boot_sign} = $self->{_io}->read_u1();
$self->{volume_id} = $self->{_io}->read_bytes(4);
$self->{partition_volume_label} = Encode::decode("ASCII", IO::KaitaiStruct::Stream::bytes_strip_right($self->{_io}->read_bytes(11), 32));
$self->{fs_type_str} = Encode::decode("ASCII", IO::KaitaiStruct::Stream::bytes_strip_right($self->{_io}->read_bytes(8), 32));
}
sub phys_drive_num {
my ($self) = @_;
return $self->{phys_drive_num};
}
sub reserved1 {
my ($self) = @_;
return $self->{reserved1};
}
sub ext_boot_sign {
my ($self) = @_;
return $self->{ext_boot_sign};
}
sub volume_id {
my ($self) = @_;
return $self->{volume_id};
}
sub partition_volume_label {
my ($self) = @_;
return $self->{partition_volume_label};
}
sub fs_type_str {
my ($self) = @_;
return $self->{fs_type_str};
}
1;