NAME

Sys::Export::ISO9660 - Write ISO9660 filesystems with Joliet filename and El Torrito boot catalog support

SYNOPSIS

my $dst= Sys::Export::ISO9660->new(
  filename => $path,
);
# Basic files and directories
$dst->add([ file => "README.TXT", "Hello World\r\n" ]);

use Sys::Export 'filedata';
$dst->add([ file => 'EFI/BOOT/BOOTIA32.EFI', filedata($loader) ]);

# Point to a file at an absolute offset from the start of the device
# The data is assumed to already exist in $dst->filename
$dst->add([ file => 'initrd', { device_offset => 0x110000 }]);

$dst->finish;

DESCRIPTION

ISO9660 is the original standard for filesystems on CDROM or DVDROM. The original standard is fairly limited in what characters and directory structure it allows. It has been extended in numerous ways since then, including allowing longer filenames, unicode filenames, or Unix permissions. This module is focused on authoring boot media, so it only supports the Joliet filename extension (similar to Microsoft VFAT unicode filenames) and "El Torrito" boot catalog for describing boot loaders for various architectures.

The basic naming rules for the original ISO9660 are that files are limited to [A-Z0-9_], and at most 8 characters before a 3-character extension, and then a version number. Directories may only be 8 characters without extension or version number, and the tree has a max depth of 8. For widest compatibility, you should try to stay within those constraints. Files have very little in the way of metadata - just a modified-time and 'hidden' flag. File data may refer to ANY 2KiB-aligned extent on the entire media.

The Joliet system for long filenames encodes an entire second copy of the directroy structure, and uses Microsoft's 16-bit litte-endian Unicode, like VFAT. Permitted characters are much more relaxed, and filenames can be much longer.

The El Torrito boot catalog is essentially a menu of boot options, grouped per platform. Each can reference an extent of data that should be executed as a boot loader. If there is only one boot option for a platform, it will be used automatically. (But, this is all dependent on BIOS of the host being booted - El Torrito can describe a menu of boot options for the user to choose from, but BIOS needs to support rendering that menu. You're probably better off just using one option that launches GRUB)

CONSTRUCTORS

new

$fat= Sys::Export::ISO9660->new($filename_or_handle);
$fat= Sys::Export::ISO9660->new(%attrs);
$fat= Sys::Export::ISO9660->new(\%attrs);

This takes a list of attributes as a hashref or key/value list. If there is exactly one argument, it is treated as the filename attribute.

ATTRIBUTES

filename

Name of file (or device) to write. If the file exists it will be truncated before writing. If you want to write the filesystem amid existing data (like a partition table), pass a file handle as filehandle.

filehandle

Output filehandle to write. The file will be enlarged if it is not big enough.

root

The root Directory object.

volume_label

Text label for the filesystem image, limited to uppercase letters, digits, and underscore.

volume_set

Name for a multi-volume collection

system

Name of the system for which the filesystem is intended. (i.e. the system what will be interpreting sectors 0..15 which are defined as "system" sectors) Limited to uppercase ASCII.

publisher

Text label (or reference to a File object in the root directory) of the entity publishing this image. Limited to uppercase ASCII.

preparer

Text label (or reference to a File object in the root directroy) of the entity writing this image. Defaults to PERL SYS::EXPORT::ISO9660 $VERSION

application

Text label (or reference to a File object in the root directory) of the application for which this image is used. Limited to uppercase ASCII.

Name of a file (or reference to File object) in the root directory where a copyright is stored.

abstract_file

Name of a file (or reference to File object) in the root directory where a copyright is stored.

bibliographic_file

Name of a file (or reference to File object) in the root directory where bibliographic information is stored.

boot_catalog

The El Torrito catalog of boot options. See "add_boot_catalog_entry" for a convenient way to build this structure. It starts undefined until the first entry is added. If undefined, no boot catalog is written into the ISO image.

Structure:

{
  system_id => $ascii32,
  boot_id   => $ascii32,
  sections  => [
    {
      platform    => # BOOT_X86, BOOT_PPC, BOOT_MAC, BOOT_EFI
      id_string   => $ascii28,
      entries => [
        {
          bootable     => # 0 (false) or 0x88 (true)
          media_type   => # EMU_NONE, EMU_FLOPPY12, EMU_FLOPPY144, EMU_FLOPPY288, EMU_HDD
          load_segment => # 0x0000=no-emul, 0x07C0=floppy
          system_type  => # MBR partition type byte, 0xEF for EFI ESP
          extent       => # Sys::Export::Extent object, holds device_offset and size and data
        },
        ...
      ]
    },
    ...
  ],
  file => $iso9660_file_obj, # lazy-built
}

default_time

This unix timestamp will be used for any date field that wasn't specified elsewhere. If not set when /finish is called, it will default to the current time().

volume_size

Only valid after calling "finish" or "allocate_extents". Total size of volume, including any extent seen in boot catalog entries. This will always be a multiple of the 2048 sector size.

METHODS

add_boot_catalog_entry

my $entry= $iso->add_boot_catalog_entry(
  platform          => # BOOT_X86, BOOT_PPC, BOOT_MAC, BOOT_EFI
  section_id        => # ASCII name of section
  bootable          => # boolean, default true
  media_type        => # EMU_NONE, EMU_FLOPPY12, EMU_FLOPPY144, EMU_FLOPPY288, EMU_HDD
  load_segment      => # address for floppy emulation, default 0x7C0 for floppy types
  system_type       => # MBR partition type code, default 0xEF for EFI
  extent            => # Sys::Export::Extent object
  device_offset     => # initialize extent->device_offset
  size              => # initialize extent->size
  data              => # initialize extent->data
);

This function is a convenient way to build the boot_catalog data structure. platform is required, and triggers creation of a new section if there isn't a section for that platform yet. The extent is also required to be defined before "finish" is called, but you can delay initializing it if you don't know what extent you'll be using, yet.

add

$fat->add(\%file_attrs);
# Attributes:
# {
#   name               => $path_bytes,
#   uname              => $path_unicode_string,
#   ISO9660_shortname  => "8CHARATR.EXT",
#   mode               => $unix_stat_mode,
#   ISO9660_flags      => FLAG_HIDDEN,
#   mtime              => $unix_epoch,
#   size               => $data_size,
#   data               => $scalar_or_scalarref_or_LazyFileData,
#   device_offset      => $desired_byte_offset,
# }

This add method takes the same file objects as used by Sys::Export, but with some optional extras:

ISO9660_shortname

Any file name not conforming to the 8.3 name limitation of FAT will get an auto-generated "short" filename, in addition to its "long" filename. If you want control over what short name is generated, you can specify it with this attribute.

ISO9660_flags

An ORed combination of flags. The FLAG_DIRECTORY is still automatically added based on the mode flags.

device_offset

For integration with ISOHybrid, you may specify device_offset to request the file be placed at an exact location, and as a single un-fragmented extent. This accounts for the "device_offset" of the whole filesystem. If you supply device_offset and do not supply data it is assumed the data already exists at that address.

device_offset must align to 2KiB boundaries of the LBA sectors.

finish

This method performs all the actual work of building the filesystem. This module writes the entire filesystem in one pass after calculating all the metdata that needs encoded and where to place all the files and directories.

You may get exceptions during this call if there isn't a way to write your files as requested.

allocate_extents

my $max_used= $iso->allocate_extents;

Assign device_offset to every file (and data structure represented by a File object) where device_offset is undefined and size > 0. This assumes we have an open-ended number of free sectors. This should only be called after every file and boot entry have been added.

The return value is the total size (bytes) of everything seen. This includes partitions referenced by the "boot_catalog". You can exclude boot_catalog entries by setting start_lba = -1 before the call, then updating to the real value before "finish".

ISO9660Hybrid calls this with all the files owned by VFAT set to start_lba = -1 so that it can determine how many initial sectors are used, and know where to start the VFAT filesystem, after which it revises all the shared files to point to extents within the VFAT filesystem.

EXPORTS

is_valid_shortname

Returns true if string meets the very restricted 8.3 notation of a file, with the added exception of allowing '~' in filenames. (Windows name mangling generates these and writes them into iso9660 images, so it's expected to work everywhere)

is_valid_joliet_name

Returns true as long as string is less than 127 unicode characters and doesn't contain forbidden characters.

remove_invalid_shortname_chars

$str= remove_invalid_shortname_chars($name, '_');

Returns a string with any invalid characters replaced with the supplied replacement character.

VERSION

version 0.005

AUTHOR

Michael Conrad <mike@nrdvana.net>

COPYRIGHT AND LICENSE

This software is copyright (c) 2026 by Michael Conrad.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.