What is a File System ❔
A file system is a method and data structure that an operating system uses to manage files on a disk or partition. It provides a way to store, organize, retrieve, and manage data. File systems control how data is stored and retrieved. Without a file system, data placed in a storage medium would be one large body of data with no way to tell where one piece of information stops and the next begins.
Key Concepts of File Systems
- Files and Directories: A file system organizes data into files, which are collections of data stored in a single unit, and directories (or folders), which are containers for files and other directories.
- Metadata: This includes information about files, such as their names, sizes, creation dates, and permissions.
- File Allocation: File systems use various methods to allocate space on storage media, such as contiguous, linked, and indexed allocation.
- File Access: Methods to read, write, and modify files. File systems provide APIs and system calls for file operations.
- File System Types: Examples include FAT (File Allocation Table), NTFS (New Technology File System), ext3/ext4 (third and fourth extended file systems), and others.
ISO 9660 File System
The ISO 9660 file system, commonly known as the ISO file system, is a standard file system for optical disc media, particularly CD-ROMs. It was developed by the International Organization for Standardization (ISO) and published in 1988. It defines how data is structured on the disc, allowing it to be read on different operating systems and hardware.
Key Features of ISO 9660
- Standardization: ISO 9660 is a widely recognized international standard (published by the International Organization for Standardization in 1988) ensuring compatibility across different platforms and devices.
- Volume Descriptors: These are structures containing metadata about the disc, such as volume identifier, volume size, logical block size, and the location of the root directory.
- File and Directory Naming: ISO 9660 has several levels:
- Level 1: Limits filenames to 8 characters with a 3-character extension (8.3 format), uppercase only, and directory names to a maximum of 8 levels deep.
- Level 2: Allows filenames up to 31 characters, still uppercase only.
- Level 3: Allows filenames up to 31 characters and permits multi-extent files, meaning files can be non-contiguous.
- Path Tables: These provide a compact way to represent the directory structure for efficient directory traversal.
- Extent and Logical Block Addressing (LBA): Data is organized in extents (contiguous blocks of data), and LBAs are used to reference these blocks. The standard LBA size for ISO 9660 is 2048 bytes.
- Root Directory: The root directory is defined in the primary volume descriptor and contains directory records for files and subdirectories.
Extensions to ISO 9660
To overcome limitations of the original standard, several extensions have been developed:
- Rock Ridge: Adds POSIX file system semantics, such as long filenames, deeper directory structures, symbolic links, and file permissions.
- Joliet: Developed by Microsoft, allows Unicode (UTF-16) filenames and longer filenames.
- El Torito: Defines a method for making bootable CD-ROMs by specifying boot information and an initial boot image.
File Identifier (File Name) rules in ISO 9660 Filesystem
In the ISO 9660 file system, file identifier rules are quite specific to ensure compatibility across different operating systems. Here are the key rules:
- Uppercase Characters: All file names must be in uppercase characters (A-Z).
- Length: The file name can have a maximum of 8 characters, and the file extension can have a maximum of 3 characters, following the 8.3 filename convention. This is often referred to as Level 1 of the ISO 9660 standard. Level 2 allows longer names (up to 31 characters), but they still must be uppercase.
- Characters Allowed: Only the characters A-Z, 0-9, and the underscore (_) are allowed. Spaces and special characters are not permitted.
- File Name and Extension Separation: File names and their extensions are separated by a period (.), for example,
FILENAME.TXT
. - Directory Depth: The maximum directory depth is limited to 8 levels.
- Root Directory: The root directory is always at a predefined location on the disk and has specific properties.
Levels in ISO 9660 Filesystem
In the context of the ISO 9660 standard, "levels" refer to different sets of restrictions and capabilities that determine how file names and directory structures are formatted and stored on an ISO 9660-compliant filesystem. These levels are designed to ensure compatibility with various operating systems and software.
The ISO 9660 standard defines three levels of file system interchange to provide compatibility with various operating systems and applications. Here are the details of the three levels:
Level 1
- File Name Length: File names can have a maximum of 8 characters, and extensions can have up to 3 characters (8.3 format).
- Character Set: Only uppercase A-Z, digits 0-9, and the underscore (_) are allowed.
- Directory Depth: Maximum of 8 directory levels.
- File Size: Each file can be up to 4 GB in size.
- File Identifier: Must be uppercase and follow the 8.3 naming convention.
- Interoperability: Ensures the highest level of compatibility with older systems, such as DOS and early versions of Windows.
Level 2
- File Name Length: File names can be up to 31 characters long.
- Character Set: Only uppercase A-Z, digits 0-9, and the underscore (_) are allowed.
- Directory Depth: Maximum of 8 directory levels.
- File Size: Each file can be up to 4 GB in size.
- File Identifier: More flexible with length but still must be uppercase.
- Interoperability: Maintains good compatibility with most systems, but some very old systems might not support the longer file names.
Level 3
- File Name Length: Same as Level 2, allowing up to 31 characters.
- Character Set: Only uppercase A-Z, digits 0-9, and the underscore (_) are allowed.
- Directory Depth: Maximum of 8 directory levels.
- File Size: Allows files to be split into multiple extents, each up to 4 GB, which can surpass the 4 GB limit per file by using multiple extents.
- File Identifier: More flexible with length but still must be uppercase.
- Interoperability: Provides the least compatibility among the three levels but allows larger files through file extents.
Level 1
+-----------------------------------------+
| File Name Length: 8.3 format |
| Character Set: A-Z, 0-9, _ |
| Directory Depth: 8 levels |
| File Size: 4 GB |
| Interoperability: Highest |
+-----------------------------------------+
Level 2
+-----------------------------------------+
| File Name Length: Up to 31 characters |
| Character Set: A-Z, 0-9, _ |
| Directory Depth: 8 levels |
| File Size: 4 GB |
| Interoperability: Good |
+-----------------------------------------+
Level 3
+-----------------------------------------+
| File Name Length: Up to 31 characters |
| Character Set: A-Z, 0-9, _ |
| Directory Depth: 8 levels |
| File Size: > 4 GB using extents |
| Interoperability: Lowest |
+-----------------------------------------+
Note:
We can explicitly set the level of iso image, by providing parameters in the image creation command. This is done by specifying it explicitly using the -iso-level
option in your xorriso
command.
For example:
- Level 1 (basic standard):
xorriso -as mkisofs -R -J -iso-level 1 -b stage1.bin -no-emul-boot -boot-load-size 4 -o image.iso iso_dir
- Level 2:
xorriso -as mkisofs -R -J -iso-level 2 -b stage1.bin -no-emul-boot -boot-load-size 4 -o image.iso iso_dir
- Level 3
xorriso -as mkisofs -R -J -iso-level 3 -b stage1.bin -no-emul-boot -boot-load-size 4 -o image.iso iso_dir
ISO 9660 Filesystem Structure
+-------------------------+
| System Area | <- Sectors 0-15 (Reserved
| | for boot code)
| +-------------------+ |
| | | | <- Sector 0 (contains
| | Boot Record | | bootloader for bootable
| | | | ISOs)
| +-------------------+ |
| | Reserved Sectors | | <- Sectors 1-15 (reserved
| | | | for additional boot code)
| +-------------------+ |
| |
+-------------------------+
| Volume Descriptors | <- Sector 16
| | (contains volume metadata)
| +---------------------+ | Start of Volume
| | Volume Descriptor 1 | | Descriptors.
| | | | can be of any order.
| +---------------------+ | But mostly, Primary Volume
| | Volume Descriptor 2 | | Descriptor is the first
| | | | one. Followed by remaining
| +---------------------+ |
| | Volume Descriptor 3 | |
| | | |
| +---------------------+ |
| | ... | |
| | | |
| +---------------------+ |
| | Volume Descriptor | | <- Marks the end
| | Set Terminator | | of the volume
| +---------------------+ | descriptor sequence.
| |
+-------------------------+
| Path Tables | <- Path Tables (contains
| +---------------------+ | directory hierarchy
| | Entry 1 (File or | | information)
| | Directory) | |
| +---------------------+ |
| | Entry 2 (File or | |
| | Directory) | |
| +---------------------+ |
| | Entry n (File or | |
| | Directory) | |
| +---------------------+ |
| List directory struct |
| structures for quick |
| traversal |
| |
+-------------------------+
| |
| Root Directory Entries |<- Contains directory
| | entries for the root
| | directory. All the
| | files or subdirectories
| +-------------------+ | of the Root Directory
| | Directory Entry 1 | | listed here.
| +-------------------+ |
| | Directory Entry 2 | |
| +-------------------+ |
| |Directory Entry ...| |
| +-------------------+ |
| |
+-------------------------+
| |
| File Data | <- Contains the actual file
| | data (raw data of files)
+-------------------------+
+-------------------------------------------------+
| ISO 9660 File System at Top level |
+------------------------+------------------------+
| System area (32,768 B) | Unused by ISO 9660 |
| 16 sectors of 2048 | |
| bytes each | |
|-------------------------------------------------|
| | Volume descriptor set |
| Data area | Path tables, |
| | directories and files |
+------------------------+------------------------+
Note: A single sector in ISO file system is of 4KB = 2048 bytes.

1️⃣ System Area (Sectors 0-15):
- Location: The system area occupies the first 16 sectors (sectors 0-15) of the ISO 9660 filesystem.
- Size: 16 Sectors, 1 Sector = 2048 bytes = 2KB
- 16 Sector = 2048 * 16 = 32768 bytes = 32 KB
- Purpose: It's reserved for boot code and system-specific data. For bootable CDs, this area includes the boot record and bootloader.
- Boot Record: If the ISO is bootable, the boot record is typically found in the first sector (sector 0).
- Sector 0: Boot Record (for bootable ISOs)
- This sector contains the boot code that is executed by the BIOS if the CD is bootable.
- Sectors 1-15: Reserved for system use
- These sectors can be used for additional boot code or other system-specific data.
- Sector 0: Boot Record (for bootable ISOs)
2️⃣ Volume Descriptors (Sector 16):
The sector 0x00 - 0x0F
(16 sectors) are reserved for the System Area. The Volume Descriptors can be found starting at sector 0x10 (16)
. The data area begins with the volume descriptor set, a set of one or more volume descriptors terminated with a volume descriptor set terminator.
Volume Descriptor Set
+----------------------+
| Volume Descriptor 1 |
|----------------------|
| Volume Descriptor 2 |
|----------------------|
| .... |
|----------------------|
| Volume Descriptor #N |
|----------------------|
| Volume Descriptor |
| set terminator |
+----------------------+
- Each Volume Descriptor is
2048 bytes
in size, fitting perfectly into a single sector.
The format of the volume descriptor as follows:
OFFSET | LENGTH (bytes) | FIELD NAME | DESCRIPTION |
0 | 1 | Type | Volume Descriptor Type Code. |
1 | 5 | Identifier | Always "CD001" |
6 | 1 | Version | Volume Descriptor Version (0x01) |
7 | 2041 | Data | Depends on the volume descriptor type |
Note: The data field of a volume descriptor may be subdivided into several fields, with the exact content depending on the type. Redundant copies of each volume descriptor can also be included in case the first copy of the descriptor becomes corrupt.
Each Volume Descriptor is one sector (2KB)(2048 bytes) long.
Basic Types of Volume Descriptors:
- Primary Volume Descriptor (PVD)
- Boot Record Volume Descriptor (BRVD)
- Supplementary Volume Descriptor (SVD)
- Volume Partition Descriptor (VPD)
- Volume Descriptor Set Terminator (VDST)
Volume Descriptor Type Codes:
The ISO 9660 file system uses volume descriptor type codes to identify the type of each volume descriptor. Here are the standard type codes:
- Boot Record Volume Descriptor (BRVD): Type code
0
. - Primary Volume Descriptor (PVD): Type code
1
- Supplementary Volume Descriptor (SVD): Type code
2
- Volume Partition Descriptor (VPD): Type code
3
- Volume Descriptor Set Terminator (VDST): Type code
255
Type Code | Descriptor Name |
---|---|
0 | Boot Record Volume Descriptor (BRVD) |
1 | Primary Volume Descriptor (PVD) |
2 | Supplementary Volume Descriptor (SVD) |
3 | Volume Partition Descriptor (VPD) |
255 | Volume Descriptor Set Terminator (VDST) |
An ISO 9660 compliant disc must contain at least one primary volume descriptor
describing the file system and a volume descriptor set terminator
for indicating the end of the descriptor sequence.
Ⅰ Primary Volume Descriptor (PVD)
This is a lengthy descriptor, but it contains some very useful information for reading the rest of the file system. It mostly contains information about the volume, characteristics and metadata, including a root directory records that indicates in which sector (location) the root directory is located. Other fields contain the description or name of the volume, and information about who created it and with which application.
- Type Code: 1
- Purpose: Contains essential information about the volume, such as volume name, volume size, and system identifier. Every ISO 9660 file system must have one PVD.
- Location: Typically it is present at Logical Block Address (LBA) 16 according to ISO 9660 specification.
- Describes the primary volume, including metadata like volume size, volume identifier, and pointers to the root directory.
- Contains metadata about the volume, including the volume identifier, volume size, and pointers to the root directory and path tables.
Structure of the Primary Volume Descriptor (PVD)
The Primary Volume Descriptor is located at Logical Block Address (LBA) 16 and has a specific format, defined by the ISO 9660 standard. Here’s a detailed view of its structure:
Offset | Length (BYTes) | Field Name | Description |
---|---|---|---|
0 | 1 | Volume Descriptor Type | Indicates the type of volume descriptor (0x01 for Primary Volume Descriptor). |
1 | 5 | Standard Identifier | Standard identifier ("CD001"). Always “CD001” for ISO 9660. |
6 | 1 | Volume Descriptor Version | Always 0x01 for the current version of ISO 9660. |
7 | 1 | Unused | Reserved for future use, should be 0x00. |
8 | 32 | System Identifier | System identifier (ASCII string, space-padded). Identifies the system that created the volume. |
40 | 32 | Volume Identifier | Volume identifier (ASCII string, space-padded) |
72 | 8 | Unused | Unused field (set to 0). Reserved for future use, should be 0x00. |
80 | 4 | Volume Space Size (LSB) | Volume space size (total number of logical blocks, least significant byte first) |
84 | 4 | Volume Space Size (MSB) | Volume space size (total number of logical blocks, most significant byte first) |
88 | 32 | Unused | Unused field (set to 0) |
120 | 2 | Volume Set Size (LSB) | Volume set size (number of volumes in the set, least significant byte first) |
122 | 2 | Volume Set Size (MSB) | Volume set size (number of volumes in the set, most significant byte first) |
124 | 2 | Volume Sequence Number (LSB) | Volume sequence number (this volume's number in the set, least significant byte first) |
126 | 2 | Volume Sequence Number (MSB) | Volume sequence number (this volume's number in the set, most significant byte first) |
128 | 2 | Logical Block Size (LSB) | Logical block size in bytes (least significant byte first) |
130 | 2 | Logical Block Size (MSB) | Logical block size in bytes (most significant byte first) |
132 | 4 | Path Table Size (LSB) | Path table size in bytes (least significant byte first) |
136 | 4 | Path Table Size (MSB) | Path table size in bytes (most significant byte first) |
140 | 4 | Type L Path Table Location | Location of the type L path table (least significant byte first) |
144 | 4 | Optional Type L Path Table Location | Optional location of the type L path table (least significant byte first) |
148 | 4 | Type M Path Table Location | Location of the type M path table (most significant byte first) |
152 | 4 | Optional Type M Path Table Location | Optional location of the type M path table (most significant byte first) |
156 | 34 | Directory Record for Root Directory | Directory record for the root directory |
190 | 128 | Volume Set Identifier | Volume set identifier (ASCII string, space-padded) |
318 | 128 | Publisher Identifier | Publisher identifier (ASCII string, space-padded) |
446 | 128 | Data Preparer Identifier | Data preparer identifier (ASCII string, space-padded) |
574 | 128 | Application Identifier | Application identifier (ASCII string, space-padded). Identifier for the application used to create the volume. |
702 | 37 | Copyright File Identifier | File identifier of copyright file (ASCII string) |
739 | 37 | Abstract File Identifier | File identifier of abstract file (ASCII string) |
776 | 37 | Bibliographic File Identifier | File identifier of bibliographic file (ASCII string) |
813 | 17 | Volume Creation Date and Time | Date and time of volume creation (format: YYYYMMDDHHMMSSCC). Date and time when the volume was created. |
830 | 17 | Volume Modification Date and Time | Date and time of volume modification (format: YYYYMMDDHHMMSSCC). Date and time when the volume was last modified. |
847 | 17 | Volume Expiration Date and Time | Date and time of volume expiration (format: YYYYMMDDHHMMSSCC). Date and time when the volume expires. |
864 | 17 | Volume Effective Date and Time | Date and time of volume becoming effective (format: YYYYMMDDHHMMSSCC). Date and time when the volume becomes effective. |
881 | 1 | File Structure Version | File structure version (always 1) |
882 | 1 | Unused | Unused field (set to 0). Reserved for future use, should be 0x00. |
883 | 512 | Application Use | Application use (512 bytes, application-specific). Space for application-specific data. |
1395 | 653 | Reserved | Reserved for future standardization (set to 0). Reserved for future use. |
- Volume Descriptor Type (Offset 0, Length 1 byte):
- Indicates the type of volume descriptor. For PVD, this value must be
0x01
.
- Indicates the type of volume descriptor. For PVD, this value must be
- Standard Identifier (Offset 1, Length 5 bytes):
- Identifies the standard used. For ISO 9660, must be "
CD001
".
- Identifies the standard used. For ISO 9660, must be "
- Volume Descriptor Version (Offset 6, Length 1 byte):
- Specifies the version of the standard. For ISO 9660, must be
0x01
.
- Specifies the version of the standard. For ISO 9660, must be
- Unused Field (Offset 7, Length 1 byte):
- Should be 0.
- System Identifier (Offset 8, Length 32 bytes):
- A string identifying the system that created the volume. Usually padded with spaces.
- Volume Identifier (Offset 40, Length 32 bytes):
- A string identifying the volume. Usually padded with spaces.
- Unused Field (Offset 72, Length 8 bytes):
- Unused should be 0.
- Volume Space Size (Offset 80, Length 8 bytes):
- The total number of logical blocks in the volume, recorded in both little-endian and big-endian formats.
- Unused Field (Offset 88, Length 32 bytes):
- Unused field, should be 0.
- Volume Set Size (Offset 120, Length 4 bytes):
- The number of volumes in the set. For a single volume, this is
1
.
- The number of volumes in the set. For a single volume, this is
- Volume Sequence Number (Offset 124, Length 4 bytes):
- The sequence number of this volume in the set. For a single volume, this is
1
.
- The sequence number of this volume in the set. For a single volume, this is
- Logical Block Size (Offset 128, Length 4 bytes):
- The size of each logical block, typically 2048 bytes.
- Path Table Size (Offset 132, Length 8 bytes):
- The size of the path table in both little and big endian.
- Location of Type L Path Table (Offset 140, Length 4 bytes):
- The starting logical block number of the type L path table (little-endian).
- LBA of path table.
- Location of Optional Type L Path Table (Offset 144, Length 4 bytes):
- LBA location of the optional path table. The path table pointed to contains only little-endian values. Zero means that no optional path table exists.
- Location of Type M Path Table (Offset 148, Length 4 bytes):
- The starting logical block number of the type M path table (big-endian).
- Location of Optional Type M Path Table (Offset 152, Length 4 bytes):
- LBA location of the optional path table. The path table pointed to contains only big-endian values. Zero means that no optional path table exists.
- Directory Entry for Root Directory (Offset 156, Length 34 bytes):
- Describes the root directory, including its location and size.
- Note that this is not an LBA address, it is the actual Directory Record, which contains a single byte Directory Identifier (0x00), hence the fixed 34 byte size.
- Volume Set Identifier (Offset 190, Length 128 bytes):
- A string identifying the volume set.
- Publisher Identifier (Offset 318, Length 128 bytes):
- A string identifying the publisher.
- Data Preparer Identifier (Offset 446, Length 128 bytes):
- A string identifying the entity that prepared the data.
- Application Identifier (Offset 574, Length 128 bytes):
- A string identifying the application used to create the volume.
- Copyright File Identifier (Offset 702, Length 37 bytes):
- A string identifying the copyright file.
- Abstract File Identifier (Offset 739, Length 37 bytes):
- A string identifying the abstract file.
- Bibliographic File Identifier (Offset 776, Length 37 bytes):
- A string identifying the bibliographic file.
- Volume Creation Date and Time (Offset 813, Length 17 bytes):
- The date and time when the volume was created.
- Volume Modification Date and Time (Offset 830, Length 17 bytes):
- The date and time when the volume was last modified.
- Volume Expiration Date and Time (Offset 847, Length 17 bytes):
- The date and time when the volume expires.
- The date and time after which this volume is considered to be obsolete. If not specified, then the volume is never considered to be obsolete.
- Volume Effective Date and Time (Offset 864, Length 17 bytes):
- The date and time when the volume becomes effective.
- The date and time after which the volume may be used. If not specified, the volume may be used immediately.
- File Structure Version (Offset 881, Length 1 byte):
- The version of the file structure, always
0x01
for ISO 9660.
- The version of the file structure, always
- Unused (Offset 882, Length 1 byte):
- Unused, always 0x00.
- Application Use (Offset 883, Length 512 bytes):
- Space reserved for application-specific data.
- Contents not defined by ISO 9660.
- Reserved (Offset 1395, Length 653 bytes):
- Reserved by ISO for future use.
+---------------------------+
| Volume Descriptor Type | <- Offset 0, Length 1 byte
+---------------------------+
| Standard Identifier | <- Offset 1, Length 5 bytes
+---------------------------+
| Volume Descriptor Version | <- Offset 6, Length 1 byte
+---------------------------+
| Unused | <- Offset 7, Length 1 byte
+---------------------------+
| System Identifier | <- Offset 8, Length 32 bytes
+---------------------------+
| Volume Identifier | <- Offset 40, Length 32 bytes
+---------------------------+
| Unused | <- Offset 72, Length 8 bytes
+---------------------------+
| Volume Space Size | <- Offset 80, Length 8 bytes
+---------------------------+
| Escape Sequences | <- Offset 88, Length 32 bytes
+---------------------------+
| Volume Set Size | <- Offset 120, Length 4 bytes
+---------------------------+
| Volume Sequence Number | <- Offset 124, Length 4 bytes
+---------------------------+
| Logical Block Size | <- Offset 128, Length 4 bytes
+---------------------------+
| Path Table Size | <- Offset 132, Length 8 bytes
+---------------------------+
| Location of Type L Path | <- Offset 140, Length 4 bytes
+---------------------------+
| Location of Type M Path | <- Offset 144, Length 4 bytes
+---------------------------+
| Directory Entry for Root | <- Offset 156, Length 34 bytes
+---------------------------+
| Volume Set Identifier | <- Offset 190, Length 128 bytes
+---------------------------+
| Publisher Identifier | <- Offset 318, Length 128 bytes
+---------------------------+
| Data Preparer Identifier | <- Offset 446, Length 128 bytes
+---------------------------+
| Application Identifier | <- Offset 574, Length 128 bytes
+---------------------------+
| Copyright File Identifier | <- Offset 702, Length 37 bytes
+---------------------------+
| Abstract File Identifier | <- Offset 739, Length 37 bytes
+---------------------------+
| Bibliographic File | <- Offset 776, Length 37 bytes
+---------------------------+
| Volume Creation Date | <- Offset 813, Length 17 bytes
+---------------------------+
| Volume Modification Date | <- Offset 830, Length 17 bytes
+---------------------------+
| Volume Expiration Date | <- Offset 847, Length 17 bytes
+---------------------------+
| Volume Effective Date | <- Offset 864, Length 17 bytes
+---------------------------+
| File Structure Version | <- Offset 881, Length 1 byte
+---------------------------+
| Unused | <- Offset 882, Length 1 byte
+---------------------------+
| Application Use | <- Offset 883, Length 512 bytes
+---------------------------+
| Reserved | <- Offset 1395, Length 653 bytes
+---------------------------+
Offset 156: It is the most important location in PVD, with this we can access the entries of the root directories.
- The structure at offset
156
in the Primary Volume Descriptor (PVD) contains the root directory entry. - Its size is
34
bytes, which starts at offset156
and ends at offset189
.
Here is the detailed structure of this entry:
Byte Offset | Length (Bytes) | Field | Description | Format |
---|---|---|---|---|
0 | 1 | Length of Directory Record | Length of this directory record in bytes. | - |
1 | 1 | Extended Attribute Record Length | Length of the extended attribute record (usually 0). | - |
2 | 4 | Location of Extent (LBA) in LSB | Logical Block Address of the extent (little-endian). | LSB |
6 | 4 | Location of Extent (LBA) in MSB | Logical Block Address of the extent (big-endian). | MSB |
10 | 4 | Data Length (LSB) | Size of the extent in bytes (little-endian). | LSB |
14 | 4 | Data Length (MSB) | Size of the extent in bytes (big-endian). | MSB |
18 | 7 | Recording Date and Time | Date and time of recording. | - |
25 | 1 | File Flags | Flags indicating file properties (e.g., directory, hidden, etc.). | - |
26 | 1 | File Unit Size | Used for interleaved files (usually 0). | - |
27 | 1 | Interleave Gap Size | Used for interleaved files (usually 0). | - |
28 | 4 | Volume Sequence Number (SB) | Volume sequence number (little-endian). | LSB |
32 | 4 | Volume Sequence Number (MSB) | Volume sequence number (big-endian). | MSB |
36 | 1 | Length of File Identifier | Length of the file identifier (name). | - |
37 | Variable | File Identifier | File identifier (name). Padded to an even length if necessary. | - |
Root Directory Entry Structure (34 bytes)
+------------------------------------+
| 0 | Length of Directory Record | (1 byte)
+------------------------------------+
| 1 | Extended Attribute Record Len | (1 byte)
+------------------------------------+
| 2 | Location of Extent (LBA) | (4 bytes, little-endian)
+------------------------------------+
| 6 | Location of Extent (LBA) | (4 bytes, big-endian)
+------------------------------------+
| 10 | Data Length | (4 bytes, little-endian)
+------------------------------------+
| 14 | Data Length | (4 bytes, big-endian)
+------------------------------------+
| 18 | Recording Date and Time | (7 bytes)
+------------------------------------+
| 25 | File Flags | (1 byte)
+------------------------------------+
| 26 | File Unit Size | (1 byte)
+------------------------------------+
| 27 | Interleave Gap Size | (1 byte)
+------------------------------------+
| 28 | Volume Sequence Number | (2 bytes, little-endian)
+------------------------------------+
| 30 | Volume Sequence Number | (2 bytes, big-endian)
+------------------------------------+
| 32 | Length of File Identifier | (1 byte)
+------------------------------------+
| 33 | File Identifier | (variable length)
+------------------------------------+
- The Location of Extent (LSB) (LBA) at offset
2
:- The location of the extent (the starting sector of the root directory entries) is a
4-byte little-endian
value starting at byte offset158
within the PVD. - This Location of Extent at offset
2
is for thelittle-endian
system. - This points to the root directory entries, which are array of the root directory entry - contains all the files and directories of the root directory.
- The location of the extent (the starting sector of the root directory entries) is a
Ⅱ Boot Record Volume Descriptor (BRVD) (Optional)
- Type Code:
0
- Location: Sector LBA 17, if present. It is typically placed after the PVD or at a location specified by the creator of the ISO file.
- Boot System Identifier: A 32-byte string that identifies the boot system. For example, "
EL TORITO SPECIFICATION
" for El Torito bootable CDs. - Boot Identifier: A 32-byte string that specifies the identifier for the boot image.
- Boot System Use: Additional data used by the boot system, such as pointers to the actual boot image.
Offset | Length | Field Name | Description |
---|---|---|---|
0 | 1 | Type | Volume Descriptor Type (0 ) |
1 | 5 | Identifier | Standard Identifier (CD001 ) |
6 | 1 | Version | Volume Descriptor Version (1 ) |
7 | 32 | Boot System Identifier | Boot System Identifier |
39 | 32 | Boot Identifier | Boot Identifier |
71 | 1977 | Boot System Use | Boot System Use |
Example Boot Record Volume Descriptor
A bootable ISO might include a Boot Record Volume Descriptor that looks like this:
- Volume Descriptor Type:
0
- Standard Identifier:
CD001
- Volume Descriptor Version:
1
- Boot System Identifier:
EL TORITO SPECIFICATION
- Boot Identifier:
BOOT IMAGE
- Boot System Use: Data specific to the El Torito specification, pointing to the boot image.
Ⅲ Supplementary Volume Descriptor (Optional):
- Type Code: 2
- Location: Follows the Primary Volume Descriptor.
In addition to the primary volume descriptor, supplementary volume descriptors may be present.
The Supplementary Volume Descriptor (SVD) structure is similar to the Primary Volume Descriptor (PVD), but it supports additional features like Unicode file names for the Joliet extension.
Offset | Length | Description |
---|---|---|
0 | 1 | Volume Descriptor Type (2 ) |
1 | 5 | Standard Identifier ("CD001" ) |
6 | 1 | Volume Descriptor Version (1 ) |
7 | 1 | Flags (e.g., UTF-16 support) |
8 | 32 | System Identifier |
40 | 32 | Volume Identifier |
72 | 8 | Unused Field |
80 | 4 | Volume Space Size (LSB first) |
84 | 4 | Volume Space Size (MSB first) |
88 | 32 | Escape Sequences |
120 | 2 | Volume Set Size (LSB first) |
122 | 2 | Volume Set Size (MSB first) |
124 | 2 | Volume Sequence Number (LSB first) |
126 | 2 | Volume Sequence Number (MSB first) |
128 | 2 | Logical Block Size (LSB first) |
130 | 2 | Logical Block Size (MSB first) |
132 | 4 | Path Table Size (LSB first) |
136 | 4 | Path Table Size (MSB first) |
140 | 4 | Location of Occurrence of L Path Table (LSB) |
144 | 4 | Location of Optional Occurrence of L Path Table (LSB) |
148 | 4 | Location of Occurrence of M Path Table (MSB) |
152 | 4 | Location of Optional Occurrence of M Path Table (MSB) |
156 | 34 | Root Directory Record |
190 | 128 | Volume Set Identifier |
318 | 128 | Publisher Identifier |
446 | 128 | Data Preparer Identifier |
574 | 128 | Application Identifier |
702 | 37 | Copyright File Identifier |
739 | 37 | Abstract File Identifier |
776 | 37 | Bibliographic File Identifier |
813 | 17 | Volume Creation Date and Time |
830 | 17 | Volume Modification Date and Time |
847 | 17 | Volume Expiration Date and Time |
864 | 17 | Volume Effective Date and Time |
881 | 1 | File Structure Version (1 ) |
882 | 1 | Unused Field |
883 | 512 | Application Use |
1395 | 653 | Reserved |
Ⅳ Volume Partition Descriptor (Optional):
- Type Code: 3
- Location: Follows the Supplementary Volume Descriptor if present.
Ⅴ Volume Descriptor Set Terminator (Type Code 255):
- It marks the end of the volume descriptors.
Location: Always the last volume descriptor in the sequence.
Offset | Length | Description |
---|---|---|
0 | 1 | Volume Descriptor Type (255 ) |
1 | 5 | Standard Identifier ("CD001" ) |
6 | 1 | Volume Descriptor Version (1 ) |
7 | 2041 | Reserved |
Volume Descriptor Set Layout:
- The Primary Volume Descriptor is always located at Logical Block 16.
- The sequence of volume descriptors is terminated by the Volume Descriptor Set Terminator, ensuring the reader recognizes the end of the descriptor list.
- Supplementary and Partition Descriptors, if present, follow the Primary Volume Descriptor but do not have fixed positions beyond being after Block 16.
-: Volume Descriptor Set Layout :-
+-----------------------------------------+
| Sector LBA | Descriptor Type |
|-------------|---------------------------|
| 16 | Primary Volume Descriptor |
| | (Type 1) (PVD) |
|-----------------------------------------|
| Follows the | Supplementary Volume |
| PVD | Descriptor (SVD) |
| | (Type 2) (Optional) |
|-----------------------------------------|
| Follows the | Volume Partition |
| SVD | Descriptor (VPD) |
| | (Type 3) (Optional) |
|-----------------------------------------|
| No fixed | Boot Volume Descriptor |
| Position | (Type 0) (Optional) |
|-----------------------------------------|
| .... | Other Volume Descriptor |
| | (if present) |
|-----------------------------------------|
| Last Entry | Volume Descriptor Set |
| | Terminator (Type 255) |
+-----------------------------------------+
Typical ISO 9660 ISO:
+-----------------------------------+
| Sector LBA | Descriptor Type |
|-----------------------------------+
| 16 | Primary Volume |
| | (Type 1) |
|-----------------------------------|
| 17 | Supplementary Volume |
| | (Type 2) (Optional)|
|-----------------------------------|
| 18 | Volume Partition |
| | (Type 3) (Optional)|
|-----------------------------------|
| 19 | Volume Descriptor Set|
| | Terminator (Type 255)|
+-----------------------------------+
-: Bootable ISO :-
+-----------------------------------+
| Sector LBA | Descriptor Type |
|------------|----------------------|
| 16 | Boot Record (Type 0) |
| | |
|-----------------------------------+
| 17 | Primary Volume |
| | (Type 1) |
|-----------------------------------|
| 18 | Supplementary Volume |
| | (Type 2) (Optional)|
|-----------------------------------|
| 19 | Volume Partition |
| | (Type 3) (Optional)|
|-----------------------------------|
| 20 | Volume Descriptor Set|
| | Terminator (Type 255)|
+-----------------------------------+
Example Sequence:
A typical ISO 9660 volume might have the following descriptors starting from LBA 16:
- Primary Volume Descriptor (PVD) at LBA 16
- Supplementary Volume Descriptor (SVD) at LBA 17 (if Joliet extension is used)
- Volume Descriptor Set Terminator (VDST) at LBA 18
In a bootable ISO:
- Boot Record Volume Descriptor (BRVD) at LBA 16
- Primary Volume Descriptor (PVD) at LBA 17
- Supplementary Volume Descriptor (SVD) at LBA 18 (if Joliet extension is used)
- Volume Descriptor Set Terminator (VDST) at LBA 19
3️⃣ Path Tables:
Contains information about directory hierarchy. It allows the system to efficiently and quickly locate directories without having to read through the entire directory tree.
- Makes it easier to locate directories. It only consists the Directories.
- Purpose: The path table provides a compact, easily searchable list of all directories in the file system, along with pointers to their location in the directory hierarchy.
- Location: The location of the path table is specified in the
Primary Volume Descriptor (PVD)
.- Little-endian:
- Offset:
140
in PVD. - Length: 4 bytes
- Description: The starting LBA of the little-endian path table.
- Offset:
- Big-Endian:
- Offset: 148 in PVD.
- Length: 4 bytes
- Description: The starting LBA of the big-endian path table.
- Little-endian:
- Structure: The path table consists of a series of path table records, each representing a directory. The Path Table itself is stored in contiguous sectors.
Path Table Location in PVD:
Offset | Length | Field Name | Description |
---|---|---|---|
140 | 4 | Location of Path Table (Little-endian) | Logical block number of the little-endian path table |
144 | 4 | Optional Location of Path Table (Little-endian) | Logical block number of an optional second little-endian path table |
148 | 4 | Location of Path Table (Big-endian) | Logical block number of the big-endian path table |
152 | 4 | Optional Location of Path Table (Big-endian) | Logical block number of an optional second big-endian path table |
In the ISO 9660 filesystem, path tables provide a compact representation of the directory hierarchy, making it easier to locate directories quickly. There are two types of path tables in the ISO 9660 standard: the Type L Path Table (little-endian) and the Type M Path Table (big-endian). Both path tables contain the same information but are encoded in different byte orders to support systems with different endianness.
Structure of a Path Table Entry:
Each entry in a path table describes a directory and includes the following fields:
- Directory Identifier Length (1 byte): The length of the directory identifier (name).
- Extended Attribute Record Length (1 byte): The length of the extended attribute record (if any).
- Location of Extent (4 bytes): The logical block number where the directory's extent begins. This is recorded in little-endian format in the Type L Path Table and in big-endian format in the Type M Path Table.
- Parent Directory Number (2 bytes): The number of the parent directory. Directories are numbered sequentially starting from 1 for the root directory.
- Directory Identifier (variable length): The name of the directory, padded to an even length with a null byte if necessary.
Offset | Length (byte) | Description |
---|---|---|
0 | 1 | Directory Identifier Length (LEN_DI) |
1 | 1 | Extended Attribute Record Length |
2 | 4 | Location of Extent (Logical Block Number) (LSB) |
6 | 2 | Parent Directory Number (LSB) |
8 | LEN_DI | Directory Identifier (padded to even length) |
+-----------------------------+
| Directory Identifier Length | (1 byte)
+-----------------------------+
| Extended Attribute Length | (1 byte)
+-----------------------------+
| Location of Extent | (4 bytes)<- LBA
| | (little-endian)
+-----------------------------+
| Parent Directory Number | (2 bytes)
+-----------------------------+
| Directory Identifier | (variable length,
| | padded if needed)
+-----------------------------+
Length of Directory Identifier: 5
Extended Attribute Record Length: 0
Directory Location: 0x0010 (16 in decimal)
Parent Directory Number: 1
Directory Identifier: "DIR_A"
Padding: 0
- Directory Identifier Length (LEN_DI)
- Length of the directory identifier (name) in bytes.
- This helps in parsing the entries as the directory names are of variable length.
- 1 byte.
- Extended Attribute Record Length
- Length of the extended attribute record associated with the directory.
- Extended attributes may include additional metadata about the directory.
- 1 byte.
- Location of Extent (Logical Block Number): It refers to the Logical Block Address (LBA) where the directory data begins.
- The starting logical block number of the directory extent.
- This helps in locating the directory’s content on the disk. The Type L Path Table uses little-endian format, and the Type M Path Table uses big-endian format.
- This field is 8 bytes long: 4 bytes, stored in Little-Endian (LSB) format.
- The LBA is used by the file system to directly locate the beginning of the file or directory data on the disc.
- Parent Directory Number
- The number of the parent directory in the path table. A reference to the parent directory. This number corresponds to the sequence number of the parent directory in the path table, allowing the construction of the directory hierarchy.
- 2 bytes, stored in Little-Endian (LSB) format.
- Directory Identifier
- The name of the directory. The actual name of the directory. If the length is odd, it is padded with a null byte to maintain even length alignment.
- Variable length (specified by LEN_DI), padded to an even length with a null byte if necessary
/ (root)
├── dir1
├── dir2
│ └── dir3
+------+------+------------+--------+-------------+
| Dir | Ext | Location | Parent | Directory |
| ID | Attr | of Extent | Dir | |
| Len | Len | (LBA) | Num | Identifier |
+------+------+------------+--------+-------------+
| 0x01 | 0x00 | 0x00000010 | 0x0001 | "/" (Root |
| | | | | Directory) |
+------+------+------------+--------+-------------+
| 0x03 | 0x00 | 0x00000020 | 0x0001 | "dir1" |
+------+------+------------+--------+-------------+
| 0x03 | 0x00 | 0x00000030 | 0x0001 | "dir2" |
+------+------+------------+--------+-------------+
| 0x05 | 0x00 | 0x00000040 | 0x0003 | "dir3" |
+------+------+------------+--------+-------------+
In this example:
- The root directory ("
/
") has aLocation of Extent
at block16 (0x10)
. - "dir1" and "dir2" are subdirectories of the root, located at blocks
32 (0x20)
and48 (0x30)
, respectively. - "dir3" is a subdirectory of "
dir2
", located at block64 (0x00000040)
. - Root Directory (
/
)- Directory Identifier Length: 1
- Extended Attribute Record Length: 0
- Location of Extent: Logical Block
16 (0x10)
(LBA) - Parent Directory Number: 1 (itself)
- Directory Identifier: 0x00 (root)
- Directory 1(
dir1
)- Directory Identifier Length: 4
- Extended Attribute Record Length: 0
- Location of Extent: Logical Block
32 (0x20)
- Parent Directory Number: 1 (root)
- Directory Identifier: "dir1" (padded to even length if necessary)
- Directory 2 (
dir2
)- Directory Identifier Length: 4
- Extended Attribute Record Length: 0
- Location of Extent: Logical Block
48 (0x30)
- Parent Directory Number: 1 (root)
- Directory Identifier: "dir2" (padded to even length if necessary)
- Directory 3 (
dir3
)- Directory Identifier Length: 4
- Extended Attribute Record Length: 0
- Location of Extent: Logical Block
48 (0x30)
- Parent Directory Number: 3 (dir2)
- Directory Identifier: "dir3" (padded to even length if necessary)
Logical Block Allocation
The path tables themselves are stored in logical blocks in the ISO 9660 filesystem. The Primary Volume Descriptor contains fields specifying the locations of the Type L and Type M Path Tables.
- Path Table Size: Indicates the size of the path table in bytes.
- Location of Occurrence of Type L Path Table: The logical block number where the Type L Path Table starts.
- Location of Occurrence of Type M Path Table: The logical block number where the Type M Path Table starts.
Example of Path Table Locations
If the Primary Volume Descriptor specifies the following:
- Path Table Size: 1024 bytes
- Location of Type L Path Table: Logical block 24
- Location of Type M Path Table: Logical block 25
+-------------------------------+
| Primary Volume Descriptor |
| |
| Path Table Size: 1024 bytes |
| Location of Type L Path Table| <- LBA 24
| Location of Type M Path Table| <- LBA 25
+-------------------------------+
| Type L Path Table |
| Entry 1: Root ("/") |
| Entry 2: "dir1" |
| Entry 3: "dir2" |
| Entry 4: "dir3" |
+-------------------------------+
| Type M Path Table |
| Entry 1: Root ("/") |
| Entry 2: "dir1" |
| Entry 3: "dir2" |
| Entry 4: "dir3" |
+-------------------------------+
4 Root Directory:
- Contains directory entries that describe files and subdirectories in the root directory.
4️⃣ Directory Entries (Directory Records):
Directory records (also known as directory entries) in the ISO 9660 file system provide information about files and directories within a directory. Each directory record contains metadata about a file or directory, such as its name, size, and location on the disc.
- Each directory entry describes a file or directory.
- Includes metadata like file name, size, and LBA (location of the file on the disk).
Structure of a Directory Record
Each directory record has the following format:
Offset | Length (bytes) | Description |
---|---|---|
0 | 1 | Length of Directory Record |
1 | 1 | Extended Attribute Record Length |
2 | 4 | Location of Extent, LBA (LSB) |
6 | 4 | Location of Extent, LBA (MSB) |
10 | 4 | Data Length (LSB) |
14 | 4 | Data Length (MSB) |
18 | 7 | Recording Date and Time |
25 | 1 | File Flags |
26 | 1 | File Unit Size |
27 | 1 | Interleave Gap Size |
28 | 2 | Volume Sequence Number (LSB) |
30 | 2 | Volume Sequence Number (MSB) |
32 | 1 | Length of File Identifier |
33 | Variable | File Identifier (name) |
N | 1 (optional) | Padding Field (0 if Length of File Identifier is even) |
-: Detailed Field Descriptions :-
1 Length of Directory Record (Offset 0, Size: 1 byte):
- It indicates the length of this directory record in bytes.
- It is 1 byte long field.
- Value =
0x22 = 34
bytes.
2 Extended Attribute Record Length (Offset 1, Size: 1 byte):
- The length of the extended attribute record for this file or directory (usually 0).
- This field is 1 byte long.
3 Location of Extent LSB (Offset 2, Size: 4 bytes): (LBA) for LSB.
- The starting logical block number of the file or directory extent.
- The "
Location of Extent
" field in the directory entry specifies the starting logical block number of the file or directory extent. - This field is 4 bytes long.
4 Location of Extent MSB (Offset: 6, Size: 4 bytes):
- Logical block number where the file/directory data starts (in MSB format).
- This field is also 4 bytes long.
5 Data Length LSB (Offset: 10, Size: 4 bytes):
- The length of the file/directory data in bytes in LSB.
- This field is 4 bytes long.
6 Data Length MSB (Offset: 14, Size: 4 bytes):
- The length of the file/directory data in bytes in MSB.
- This field is also 4 bytes long.
7 Recording Date and Time (Offset: 18, Size: 7 bytes):
- The recording date and time of the file/directory.
- It is 7 bytes in size.
- Format: year (since 1900), month, day, hour, minute, second, and time zone offset from GMT.
8 File Flags (Offset: 25, Size: 1 byte):
- Flags indicating the file type and attributes (e.g., directory, hidden, associated file).
- This field is 1 byte in size.
- Bitfield structure:
- Bit 0: Hidden
- If set, the entry is a hidden file.
- Bit 1: Directory
- If set, the entry is a directory.
- Bit 2: Associated File
- If set, the entry is an associated file.
- Bit 3: Record format specified
- If set, the file's record is formatted according to the extended attribute record.
- Bit 4: Permissions specified
- If set, the file is a protection file.
- Bit 5: Reserved
- Bit 6: Reserved
- Bits 7: Last directory record in the extent.
- Bit 0: Hidden
9 File Unit Size (Offset 16, Size: 1 byte):
- The size of the file unit for interleaved files (usually 0).
- This field is 1 byte in size.
10 Interleave Gap Size (Offset 27, Size: 1 byte):
- The size of the gap between interleaved files (usually 0)
- This field is 1 byte in size.
11 Volume Sequence Number LSB (Offset 28, Size: 2 bytes):
- The volume sequence number for this file/directory in LSB.
- This field is 2 bytes in size.
12 Volume Sequence Number MSB (Offset 30, Size: 2 bytes):
- The volume sequence number for this file/directory in MSB.
- This field is 2 bytes in size.
13 Length of File Identifier (Offset 32, Size: 1 byte):
- The length of the file identifier (name).
- This field is 1 byte long.
14 File Identifier (Offset 33, Size: Variable):
- The name of the file or directory.
- Variable length, padded to an even length with a null byte if necessary.
15 Padding:
- Padding bytes to ensure the directory record is even in length. If the length of the file identifier is even, a padding byte (0) is added to maintain word alignment.
File Flags:
The File Flags
field in an ISO9660 directory record provides important information about the file or directory. These flags are represented as a single byte, where each bit has a specific meaning. Below is a detailed breakdown of the File Flags
field:
File Flags Field (1 byte)
Bit | Value | Description |
---|---|---|
0 | 0x01 | Hidden file |
1 | 0x02 | Directory |
2 | 0x04 | Associated file |
3 | 0x08 | Record format specified |
4 | 0x10 | Permissions specified |
5 | 0x20 | Reserved |
6 | 0x40 | Reserved |
7 | 0x80 | Last directory record in the extent |
👀Bitwise Explanation:
- Hidden file (Bit 0, 0x01): If set, this file is hidden and should not be displayed in a directory listing by default.
- Directory (Bit 1, 0x02): If set, this record points to a directory rather than a file.
- Associated file (Bit 2, 0x04): If set, this file is an associated file.
- Record format specified (Bit 3, 0x08): If set, the record format is specified.
- Permissions specified (Bit 4, 0x10): If set, file permissions are specified.
- Reserved (Bit 5 and Bit 6, 0x20 and 0x40): These bits are reserved for future use and should typically be set to 0.
- Last directory record in the extent (Bit 7, 0x80): If set, this is the last record in the current extent.
Example of File Flags:
- A regular file with no special attributes:
0x00
- A hidden file:
0x01
- A directory:
0x02
- A hidden directory:
0x03
- The last record in the extent:
0x80
- A hidden directory that is also the last record in the extent:
0x83
-: Directory Record for a file :-
Let's illustrate with an example directory record for a file named "README.TXT":
Offset | Length (bytes) | Description | Example Value |
---|---|---|---|
0 | 1 | Length of Directory Record | 34 |
1 | 1 | Extended Attribute Record Length | 0 |
2 | 4 | Location of Extent (LSB) | 0x00000014 (20 in decimal) |
6 | 4 | Location of Extent (MSB) | 0x14000000 |
10 | 4 | Data Length (LSB) | 0x00000200 (512 in decimal) |
14 | 4 | Data Length (MSB) | 0x00020000 |
18 | 7 | Recording Date and Time | 0x7E070718202120 |
25 | 1 | File Flags | 0x00 (Regular file) |
26 | 1 | File Unit Size | 0 |
27 | 1 | Interleave Gap Size | 0 |
28 | 2 | Volume Sequence Number (LSB) | 0x0001 |
30 | 2 | Volume Sequence Number (MSB) | 0x0100 |
32 | 1 | Length of File Identifier | 10 |
33 | Variable | File Identifier (name) | "README.TXT" (ASCII bytes) |
43 | 1 (optional) | Padding Field | 0 |
Directory Record for a Directory:
Let's illustrate with an example directory record for a directory named "DOCUMENTS":
Offset | Length (bytes) | Description | Example Value |
---|---|---|---|
0 | 1 | Length of Directory Record | 34 |
1 | 1 | Extended Attribute Record Length | 0 |
2 | 4 | Location of Extent (LSB) | 0x00000030 (48 in decimal) |
6 | 4 | Location of Extent (MSB) | 0x30000000 |
10 | 4 | Data Length (LSB) | 0x00000400 (1024 in decimal) |
14 | 4 | Data Length (MSB) | 0x00040000 |
18 | 7 | Recording Date and Time | 0x7E070718202120 |
25 | 1 | File Flags | 0x02 (Directory) |
26 | 1 | File Unit Size | 0 |
27 | 1 | Interleave Gap Size | 0 |
28 | 2 | Volume Sequence Number (LSB) | 0x0001 |
30 | 2 | Volume Sequence Number (MSB) | 0x0100 |
32 | 1 | Length of File Identifier | 9 |
33 | Variable | File Identifier (name) | "DOCUMENTS" (ASCII bytes) |
42 | 1 (optional) | Padding Field | 0 (since "DOCUMENTS" has an odd length) |
Example Directory Record:
Consider a directory with a single file named example.txt
:
- Length of Directory Record:
34
- Extended Attribute Record Length:
0
- Location of Extent:
100
(logical block address, LBA) - Data Length:
2048
bytes - Recording Date and Time:
2023-07-08 12:00:00
- File Flags:
0
(regular file) - File Unit Size:
0
- Interleave Gap Size:
0
- Volume Sequence Number:
1
- Length of File Identifier:
11
- File Identifier:
"example.txt"
The byte-level representation might look like this:
+-----------------------------------------------+
| Length of Directory Record (1 byte) |
| Extended Attribute Record Length (1 byte) |
| Location of Extent (8 bytes) |
| Data Length (8 bytes) |
| Recording Date and Time (7 bytes) |
| File Flags (1 byte) |
| File Unit Size (1 byte) |
| Interleave Gap Size (1 byte) |
| Volume Sequence Number (4 bytes) |
| Length of File Identifier (1 byte) |
| File Identifier (11 bytes) |
| Padding (1 byte) |
+-----------------------------------------------+
Directory Entry Fields
- Length of Directory Record:
0x22
(34 in decimal) - Extended Attribute Record Length:
0x00
- Location of Extent:
0x64
(100 in decimal)- LSB (4 byte):
0x64 00 00 00
, because the first byte address is lower. - MSB (4 byte):
0x00 00 00 64
- LSB (4 byte):
- Data Length:
0x00 08 00 00 00 00 00 00
(2048 in decimal) - Recording Date and Time:
0x17 07 08 0C 00 00 00
(2023-07-08 12:00:00) - File Flags:
0x00
(regular file) - File Unit Size:
0x00
- Interleave Gap Size:
0x00
- Volume Sequence Number:
0x01 00 00 00
- Length of File Identifier:
0x0B
(11 in decimal) - File Identifier:
"example.txt"
- Padding:
0x00
+--------------------------------------------+
| Length of Directory Record (1 byte) | 0x22 (34)
| Extended Attribute Record Length (1 byte) | 0x00
| Location of Extent (8 bytes) | 0x64 00 00 00 00 00 00 64
| Data Length (8 bytes) | 0x00 08 00 00 00 00 00 00
| Recording Date and Time (7 bytes) | 0x17 07 08 0C 00 00 00
| File Flags (1 byte) | 0x00
| File Unit Size (1 byte) | 0x00
| Interleave Gap Size (1 byte) | 0x00
| Volume Sequence Number (4 bytes) | 0x01 00 00 00
| Length of File Identifier (1 byte) | 0x0B (11)
| File Identifier (11 bytes) | "example.txt"
| Padding (1 byte) | 0x00
+--------------------------------------------+
5 File Data:
- The actual content of the files is stored here, following the directory entries.
- Location: The file data itself comes after the directory records and is located based on the extent information provided in those directory records.
- The actual content of the files, stored in contiguous extents.

Reading and Identifying Volume Descriptors:
To identify the Volume Descriptors, you would typically scan the volume descriptors starting from LBA 16 and look for their type code.
-: Algorithm :-
1 Initialize:
- Start reading from LBA 16.
- Prepare a buffer to store the data read from each sector. Mostly the memory address.
2 Loop through Volume Descriptors:
- Read the sector at the current LBA.
- Each sector is
2048 bytes
in the ISO. - Identify the type of volume descriptor based on the type code at the beginning of the sector.
- The type code is the very first byte of the volume descriptor.
- If the volume descriptor is identified, process it accordingly.
- If type code is
0
, it's a Boot Volume Descriptor (BVD). - If type code is
1
, it's a Primary Volume Descriptor (PVD). - If type code is
2
, it's a Supplementary Volume Descriptor (SVD). - If type code is
3
, it's a Volume Partition Descriptor (VPD). - If type code is
255
, it's a Volume Descriptor Set Terminator (VDST). - If any other type code, it's an unknown volume descriptor.
- If type code is
- Move to the next LBA.
3 Stop Condition:
- Increment the LBA to move to the next sector.
- Repeat the loop until the Volume Descriptor Set Terminator is found.
- If the Volume Descriptor Set Terminator (type code 255) is encountered, stop the loop.
Pseudocode:
LBA = 16
Buffer = allocate 2048 bytes
while true:
ReadSector(LBA, Buffer)
TypeCode = Buffer[0]
if TypeCode == 0:
print "Boot Volume Descriptor (BVD) found"
// Process BVD
elif TypeCode == 1:
print "Primary Volume Descriptor (PVD) found"
// Process PVD
elif TypeCode == 2:
print "Supplementary Volume Descriptor (SVD) found"
// Process SVD
elif TypeCode == 3:
print "Volume Partition Descriptor (VPD) found"
// Process VPD
elif TypeCode == 255:
print "Volume Descriptor Set Terminator (VDST) found"
break
else:
print "Unknown Volume Descriptor found"
LBA += 1
Disk Read Function:
We will be using extended read function since the traditional int 0x13, ah = 0x02
reads the sector of size 512 bytes
, However in the ISO 9660
the sector size is 2048 bytes (2 KB)
. This function assumes sector to be 512 byte long.
The extended BIOS disk function int 0x13, ah = 42h
do not make any assumption that a sector size is fixed at 512 bytes. On any kind of drive that supports extended disk BIOS services you can query the drive parameters to determine the disk sector size.
; **************************
; BIOS ReadFromDiskUsingExtendedBIOSFunction
; IN:
; - ES:BX: Buffer
; - EAX: Sector start low-dword
; - ESI: Sector start high-dword
; - ECX: Sector count
; - EDX: Sector size in bytes
;
; Registers:
; - Conserves all but ES:BX
; **************************
ReadFromDiskUsingExtendedBIOSFunction:
pushad
; Set initial
mov word [DiskPackage.Segment], es
mov word [DiskPackage.Offset], bx
mov dword [DiskPackage.Sector], eax
mov dword [DiskPackage.Sector + 4], esi
.sLoop:
; Setup Package
mov word [DiskPackage.SectorsToRead], 1
; Setup INT
push edx
mov al, 0
mov ah, 0x42
mov dl, byte [bPhysicalDriveNum]
mov si, DiskPackage
int 0x13
; It's important we check for offset overflow
pop edx
mov ax, word [DiskPackage.Offset]
add ax, dx
mov word [DiskPackage.Offset], ax
test ax, ax
jne .NoOverflow
.Overflow:
; So overflow happened
add word [DiskPackage.Segment], 0x1000
mov word [DiskPackage.Offset], 0x0000
.NoOverflow:
; Loop
inc dword [DiskPackage.Sector]
loop .sLoop
.End:
; Restore registers
popad
; Save position
push eax
xor eax, eax
mov ax, word [DiskPackage.Segment]
mov es, ax
mov bx, word [DiskPackage.Offset]
pop eax
ret
; This is used for the extended read function (int 0x13)
DiskPackage: db 0x10
db 0
.SectorsToRead dw 0
.Offset dw 0
.Segment dw 0
.Sector dq 0
%endif
Read Volume Descriptors:
As we all know that the volume descriptors starts at sector with LBA 16 of the ISO 9660. The sectors 0-15
are reserved for system.
Since Volume Descriptors starts at sector 16
and all the volume descriptors are at consecutive sectors. For example: If at sector 16 is Primary Volume Descriptor then at sector 17 would be the next Volume Descriptor. However there location is not fixed that is there is no guarantee that Primary Volume Descriptor is at sector 16. Descriptors could be in random order. But their starting sector is fixed which is 16 LBA
(Logical Block Address) and Volume Descriptor Terminator marks the end of the list, means it is always located at the end of the list and indicates that list ends here.
- Start from Sector 16
- Read the sector at location
0x8000
. - Verify the ISO disk Identifier
CD001
, which is present at offset 1 of every volume descriptor and is of size 5 bytes. - Do your work the Volume Descriptor like:
- Print the Volume Descriptor Type, which is at offset
0
of the Volume Descriptor.
- Print the Volume Descriptor Type, which is at offset
- Check if it is the Volume Descriptor Terminator, whose Type is
0xFF
.- If it is, then we have parsed all the volume descriptor and we are done.
- If it is not then, read the next sector for the next volume descriptor and read all steps.
- Read the sector at location
%define VOLUME_DESCRIPTOR_READ_LOCATION 0x8000
Read_volume_descriptors:
pusha ; save the state
.iterate_descriptors:
xor eax, eax ; clear out the eax
mov esi, eax ; clear out the esi
mov ax, 0x0000
mov es, ax ; set es to 0x0000
mov bx, VOLUME_DESCRIPTOR_READ_LOCATION
mov eax, [dwVolumeDescriptorStartingSector] ; starting sector low 32 bit (0-indexed LBA)
mov esi, 0 ; starting sector high 32 bit
mov ecx, 1 ; number of sectors to read
mov edx, 2048 ; Sector sizes in bytes (1 sector = 2048 in ISO 9660)
call ReadFromDiskUsingExtendedBIOSFunction
;; Now at location VOLUME_DESCRIPTOR_READ_LOCATION, we have read the sector.
; Verify the PVD identifier 'CD001',
; which would be at offset 1 of Volume Descriptor structure.
mov si, VOLUME_DESCRIPTOR_READ_LOCATION + 1
mov cx, 5 ; 5 characters to check
mov di, iso_id
repe cmpsb
jne .invalid_iso_disk
;; We got the valid iso disk identifier.
mov byte al, [VOLUME_DESCRIPTOR_READ_LOCATION]
; Print the Volume Descriptor Type
mov si, sVolumeDescriptorTypeStatement
call PrintString16BIOS
call PrintWordNumber
call PrintNewline
;; do your logic to read particular descriptor.
jmp .read_next_volume_descriptor
.read_next_volume_descriptor:
cmp al, 0xFF ; Check for the end of volume descriptor list,
; which is Volume Descriptor Set Terminator and whose type (first byte) is 0xFF.
je .volume_descriptor_terminator
;; If not end of the list, load next volume descriptor from the next sector.
add dword [dwVolumeDescriptorStartingSector], 1 ; read next sector i.e next volume descriptor.
jmp .iterate_descriptor
;; Volume Descriptor Set Terminator
.volume_descriptor_terminator:
mov si, volume_descriptor_terminator_encountered
call PrintString16BIOS
.done_reading_volume_terminator:
popa ; restore the state
ret
.invalid_iso_disk:
;; We got the Invalid ISO DISK
; Print the invalid statement and go to infinite loop
mov si, invalid_iso_disk
call PrintString16BIOS
call PrintNewline
jmp $
dwVolumeDescriptorStartingSector dd 16 ;; starting sector where the volume descriptor resides.
invalid_iso_disk: db 'Invalid ISO disk identifier.', 0
volume_descriptor_terminator_encountered: db 'Volume Descriptor Set Terminator Encountered.', 0
sVolumeDescriptorTypeStatement: db 'Volume Descriptor Type = ', 0
Code Explanation:
- Start: The program begins execution.
- Save the state: The
pusha
instruction saves the state of the registers. - Initialize Registers: The code clears the
eax
andesi
registers and setses
to0x0000
. - Set Read Location: Sets
bx
to the location where the volume descriptor will be read. - Read Volume Descriptor Sector: Reads the sector starting from
dwVolumeDescriptorStartingSector
. - Verify PVD Identifier: Compares the identifier at the specified offset with 'CD001'.
- Invalid ISO Disk Identifier: If the identifier does not match, it jumps to print an invalid identifier message.
- Valid ISO Disk Identifier: If the identifier matches, it prints the volume descriptor type.
- Print Volume Descriptor Type: Prints the volume descriptor type.
- Read Next Volume Descriptor: Checks if the current descriptor is the last one (type 0xFF), otherwise reads the next descriptor.
- Volume Descriptor Terminator: If the type is 0xFF, prints the terminator message.
- Restore the state: The
popa
instruction restores the state of the registers. - End: The program terminates.
Accessing Root Directory
🧾 Scanning for a File in Root Directory
In order to scan for the file from the root directory, we need to first read the Primary Volume Descriptor
from the LBA 16 of the ISO image which should have the sector size of 2KB (2048 bytes). We can read this Primary Volume Descriptor
at some memory location/ buffer or in a structure. This PVD contains metadata about the volume, including the location of the root directory record.
At offset 156 in the PVD, we have the Root Directory Record
, This record contains the location of extent (starting LBA of the root directory) and size (data length) of the root directory.
The directory record is a structured data block that includes metadata about files and directories.
With the starting block and size of the root directory known, we can read the entire root directory into a buffer. The root directory is essentially a list of directory records, each representing a file or subdirectory.
Now all we need to do is to iterate through all the directory records from the buffer, examining each one to see if it matches the desired filename.
Each directory record contains the length of the record, the name of the file or directory, and its starting block and size.
In order to move to the next directory record, we add the length of the current record to the current directory record.
Step-by-Step Guide
- Read the Primary Volume Descriptor (PVD) starting at LBA 16.
- Read the PVD from sector 16 of the ISO image (2048 bytes).
- Parse the PVD to retrieve necessary information.
- Locate the Root Directory Record from the PVD.
- From the PVD structure, extract the root directory record, which is at offset
156
. - Retrieve the
extent location
(starting block) anddata length
(size) of the root directory.
- From the PVD structure, extract the root directory record, which is at offset
- Read the Root Directory's extent to find directory entries.
- Read the root directory into a buffer or memory.
- Iterate through the directory entries to find the desired file.
- Initialize a pointer to the start of the buffer.
- Loop through the directory records in the buffer:
- Check if the
length
of the record is 0, indicating the end of the directory. - Extract the
name
from the directory record. - Compare the
name
with the desired filename. - If the file is found, retrieve its
extentLocation
(starting block) anddataLength
(size). - Break the loop if the file is found.
- Move the pointer to the next directory record.
- Check if the
Algorithm ScanForFileISO9660(desiredFileName)
// Step 1: Initialize Variables
Define structure PVD (Primary Volume Descriptor)
Define structure DirectoryRecord
Set SECTOR_SIZE = 2048
// Step 2: Read the Primary Volume Descriptor (PVD)
buffer = ReadSector(16, SECTOR_SIZE)
pvd = ParsePVD(buffer)
// Step 3: Extract Root Directory Record from PVD
rootExtent = pvd.rootDirectoryRecord.extentLocation
rootSize = pvd.rootDirectoryRecord.dataLength
// Step 4: Read the Root Directory
numSectors = Ceil(rootSize / SECTOR_SIZE)
rootBuffer = ReadSectors(rootExtent, numSectors * SECTOR_SIZE)
// Step 5: Scan for the Desired File in Root Directory
pointer = rootBuffer
while (pointer < rootBuffer + rootSize)
record = ParseDirectoryRecord(pointer)
if (record.length == 0)
break // End of directory
name = ExtractName(record)
if (name == desiredFileName)
fileStart = record.extentLocation
fileSize = record.dataLength
Print("File found at block", fileStart, "with size", fileSize)
return (fileStart, fileSize)
// Move to the next record
pointer += record.length
Print("File not found")
return NULL
Diagram 1: ISO9660 Structure with PVD
+-----------------------+ Sector 0
| |
| ISO9660 Boot Code |
| |
+-----------------------+
| | Sector 1
| ... |
| |
+-----------------------+
| | Sector 16
| Primary Volume |
| Descriptor |
| |
+-----------------------+
| |
| ... |
| |
+-----------------------+
| | Sector n
| |
| Other data |
| |
+-----------------------+
Diagram 2: Structure of PVD
+----------------------------+
| Volume Descriptor Type |
+----------------------------+
| Standard Identifier |
+----------------------------+
| Volume Identifier |
+----------------------------+
| Root Directory Record |
| (Extent Location, Size) |
+----------------------------+
| ... |
+----------------------------+
Diagram 3: Extracting Root Directory Record from PVD.
PVD Structure
+---------------------------+
| Type |
| Identifier |
| Version |
| ... |
| Root Directory Record | ----
+---------------------------+ |
|
------------------
|
V
+---------------------------+
| Extent Location (Block 20)|
+---------------------------+
| Data Length (4096 bytes) |
+---------------------------+
| ... |
+---------------------------+
The root directory record indicates the root directory starts at block 20 and is 4096 bytes in size.
Diagram 4: Reading Sectors of Root Directory
+---------------------------+ Block 20
| Root Directory Data |
| |
+---------------------------+ Block 21
| Root Directory Data |
| |
+---------------------------+
Diagram 5: Buffer with Directory Records
+---------------------------+
| Directory Record 1 |
| |
| |
+---------------------------+
| Directory Record 2 |
| |
| |
+---------------------------+
| Directory Record 3 |
| |
| |
+---------------------------+
Directory Record 1, Sample Example
+---------------------------------+
| Length |
| Extent Location |
| Data Length |
| File Name: "README.TXT" |
| ... |
+---------------------------------+
; **************************
; Reads the Root Directory Entry which should be pointed
; by `SI`. It scans all the entries of the root directory
; and prints the identifier(name) of one and all.
; IN:
; - SI Root Directory Entry
; LOCAL VARIABLE:
; VAR1 = [bp - 4] = Address of the Current Directory entry.
; VAR2 = [bp - 8] = Found file LBA.
; **************************
Find_and_Load_File_from_Root:
pusha ; save the state
push bp ; Save old base pointer
mov bp, sp ; Set new base pointer
sub sp, 8 ; Allocate 8 bytes for local variables
; 2 local variables of size 4 bytes each
mov word [bp - 4], si ; store the address of first entry
; of Root Directory Entries.
; Print the function job message
; SI - is needy for the function so push the initial state into the stack
; and pop after printing the string. Thus storing the initial value.
push si
mov si, sFindAndLoadFileStatement
call PrintString16BIOS
call PrintNewline
pop si
.iterate_entry:
xor ax, ax ; Clear out the ax register
mov byte al, es:[si] ; it points to the 0 offset in directory entry.
; At Offset 0 is length of the record.
; It should be non zero, if zero means no valid record.
test al, al
jz .didnt_find ; Reached the end of the directory entry list
; and didnt find the file
;; It is valid directory record.
; Get the File Flags field (offset 25 from the start of the entry).
mov byte al, [si + 25]
; Check if it's a directory
test al, 0x02 ; Check if bit 1 is set
jnz .is_directory
;; It is a file
;; Check it's identifier.
xor cx, cx ; clear out cx
xor dx, dx ; clear out dx
mov byte cl, es:[si + 32] ; get the file identifier length,
; which is at offset 32 of directory_entry_record.
mov byte dx, cx ; Store the identifier length
; into the DX.
;; Check the file identifier
mov di, si ; DI and SI points to the Directory Entry Record
add di, 33 ; add offset 33, which is the start of the file identifier
; DI, now points to the file identifier beginning.
mov cx, 6 ; The length of the file identifier.
lea si, [file_identifier] ; Load SI to the identifier of the required file.
repe cmpsb ; Keep checking the SI and DI for the match.
; if the length of the file identifier in both DI and SI matches,
; then we have the required file, else jump to the next
; directory entry record.
jne .onto_next ; Jump to the next directory record if file identifier of
; particular size mismatch
;; Found the file
mov si, [bp-4] ; Store the current Directory Entry Record in the Local variable.
.got_file:
;; got the file
; Print the message claiming that we found the file, you searched for.
push si
mov si, sFoundTheFile
call PrintString16BIOS
call PrintNewline
pop si
;; Location of the extent of the File
; i.e the LBA of the data of the file.
mov eax, es:[si + 2] ; Logical Block Address of the extent (first 4 bytes)
;; Save LBA
;; Store File LBA in local variable BP - 8
mov dword [bp - 8], eax ; Store the File LBA
; to second local variable.
; Read the file at the FILE_LOADING AREA which is
; 0x0500
mov ax, 0x0000
mov es, ax ; Zero the extra segment.
mov bx, FILE_LOADING_AREA ; 0xB000
mov byte ax, [si+ 10] ; Data length of the file in LSB
; It is present at offset 10 of Directory Entry Record.
; It is of 4 byte in size
; and data length is in bytes.
;; Prints the Data length
; push si
; mov si, sLengthOfFile
; call PrintString16BIOS
; call PrintWordNumber
; call PrintNewline
; pop si
;; Roundoff the size to the 2048 which is the sector size.
; i.e if the data length is less than 2048, then sector to load should be
; 1 which is 2048 bytes
; If the data length is more than 2048, 2049 for instance, one more than 2048
; then the sector to load will be 2 (2048*2)bytes.
; Now eax, consists the rounded off value to sector size
add eax, 2047
mov ecx, 2048
div ecx ; division also affect the edx register.
; so be careful, if you have stored the necessary
; information in edx, it must have got wiped out.
; move the sector count to ecx.
mov ecx, eax
mov eax, [bp - 8] ; starting sector low 32 bit (0-indexed LBA)
; fetch from the second local variable.
mov esi, 0 ; starting sector high 32 bit
; mov ecx, 1 ; Here you can explicitly specify the sector count,
; Otherwise we already calculated the rounded off
; sector count to sector size based on the data length.
mov edx, 2048 ; Sector sizes in bytes (1 sector = 2048 in ISO 9660)
call ReadFromDiskUsingExtendedBIOSFunction
;; Tell user about displaying the content of the file.
mov si, sPrintingTheFileContent
call PrintString16BIOS
call PrintNewline
;; Print the contents of the file.
mov si, FILE_LOADING_AREA ; 0xB000
; Prints the data from the
; file loaded area.
call PrintString16BIOS
call PrintNewline
; We are done here reading the contents of the file.
jmp .reading_done
.is_directory:
; handle the directory, for the time being, just jump to next entry.
; Maybe you can recursively search for the file in every directory.
; Or you can search for particular directory.
;; Update the si to point to next entry.
.onto_next:
; load the base address of current entry back to si
mov word si, [bp - 4]
add byte si, es:[si] ; increment si to point to next entry
; Increment by adding the size of current entry.
; at offset 0 of directory entry, we have the size
; of each directory entry. we can get to the second
; entry by adding the size of current entry to the
; current entry address.
mov word [bp - 4], si ; update the next entry address in the local
; variable as well.
jmp .iterate_entry ; onto the next entry.
;; Sorry but we didnt find your file.
.didnt_find:
mov si, sDidntFindTheFile
call PrintString16BIOS
call PrintNewline
.reading_done:
add sp, 8 ; Clear local variables
mov sp, bp ; Reset Stack pointer
pop bp ; Restore old base pointer
popa ; restore the state
;; End of the Find_and_Load_File_from_Root function
ret
;; Data for the Find_and_Load_File_from_Root function
sLengthOfFileIdentifierStatement: db 'Length of the File Identifier is (in bytes): ', 0
sLengthOfFile: db 'Length of the File (in bytes): ', 0
sUnpaddedSizeStatement: db 'Un-Padded Size of Bootloader (bytes): ', 0
sFindAndLoadFileStatement: db 'Find and Load the file.', 0
sFoundTheFile: db 'Found the file.', 0
sPrintingTheFileContent: db 'Printing the content of the file.', 0
sDidntFindTheFile: db 'Didnt find the file.', 0
file_identifier: db 'AB.TXT', 0
This function works in conjunction with the previous one with some modifications:
This functions expects that we have read the Root Directory Entry
in memory and supply that location in SI
register.
Create a Bootable ISO
To make an ISO bootable, the bootloader must be placed in the system area. Here's a step-by-step guide to creating a bootable ISO with a custom bootloader.
1 Prepare the Bootloader Code:
- Assemble the Stage 1 bootloader to fit within the first 16 sectors (512 bytes).
boot.asm
:
BITS 16
ORG 0x7C00
start:
; Print a message to indicate bootloader is running
mov si, msg
print:
lodsb
test al, al
jz hang
mov ah, 0x0E
int 0x10
jmp print
hang:
cli
hlt
msg db 'Bootloader started...', 0
TIMES 510-($-$$) db 0
DW 0xAA55
- Assemble the Bootloader
Use nasm
to assemble the bootloader.
nasm -f bin -o boot.bin boot.asm
2 Create the ISO Filesystem:
- Use a tool like
mkisofs
to create the ISO image and specify the bootloader.
mkisofs -o bootable.iso -b boot.bin -no-emul-boot -boot-load-size 4 -boot-info-table
Explanation of mkisofs
Options
-o bootable.iso
: Specifies the output file name.-b boot.bin
: Specifies the boot image file to be used for making the CD bootable.-no-emul-boot
: Indicates that the boot image is a no-emulation El Torito boot image.-boot-load-size 4
: Specifies the number of virtual 512-byte sectors to load from the boot image (4 sectors here).-boot-info-table
: Adds a boot information table to the boot image.
3 Testing the Bootable ISO
You can use an emulator like QEMU to test the bootable ISO:
qemu-system-x86_64 -cdrom bootable.iso
Complete Source Code:
The complete source code is committed here: https://github.com/The-Jat/The9660Boot/tree/32759d280dade194d333bca8d7c9f8d8017d4980
Utility
A utility to read the ISO 9660 file system.
#define SECTOR_SIZE 2048
// Primary Volume Descriptor (PVD) structure
typedef struct __attribute__((packed)) {
uint8_t type; // Offset 0
char id[5]; // Offset 1
uint8_t version; // Offset 6
uint8_t unused1; // Offset 7
char system_id[32]; // Offset 8
char volume_id[32]; // Offset 40
uint8_t unused2[8]; // Offset 72
uint32_t volume_space_size_le; // Offset 80 (little-endian)
uint32_t volume_space_size_be; // Offset 84 (big-endian)
uint8_t unused3[32]; // Offset 88
uint16_t volume_set_size_le; // Offset 120 (little-endian)
uint16_t volume_set_size_be; // Offset 122 (big-endian)
uint16_t volume_sequence_number_le; // Offset 124 (little-endian)
uint16_t volume_sequence_number_be; // Offset 126 (big-endian)
uint16_t logical_block_size_le; // Offset 128 (little-endian)
uint16_t logical_block_size_be; // Offset 130 (big-endian)
uint32_t path_table_size_le; // Offset 132 (little-endian)
uint32_t path_table_size_be; // Offset 136 (big-endian)
uint32_t type_l_path_table; // Offset 140
uint32_t opt_type_l_path_table; // Offset 144
uint32_t type_m_path_table; // Offset 148
uint32_t opt_type_m_path_table; // Offset 152
uint8_t root_directory_record[34]; // Offset 156
char volume_set_id[128]; // Offset 190
char publisher_id[128]; // Offset 318
char data_preparer_id[128]; // Offset 446
char application_id[128]; // Offset 574
char copyright_file_id[37]; // Offset 702
char abstract_file_id[37]; // Offset 739
char bibliographic_file_id[37]; // Offset 776
char creation_date[17]; // Offset 813
char modification_date[17]; // Offset 830
char expiration_date[17]; // Offset 847
char effective_date[17]; // Offset 864
uint8_t file_structure_version; // Offset 881
uint8_t unused4; // Offset 882
uint8_t application_data[512]; // Offset 883
uint8_t unused5[653]; // Offset 1395
} PVD;
// Directory Record structure
typedef struct __attribute__((packed)) {
uint8_t length; // Offset 0
uint8_t ext_attr_length; // Offset 1
uint32_t extent_location_le; // Offset 2 (little-endian)
uint32_t extent_location_be; // Offset 6 (big-endian)
uint32_t data_length_le; // Offset 10 (little-endian)
uint32_t data_length_be; // Offset 14 (big-endian)
uint8_t recording_date[7]; // Offset 18
uint8_t file_flags; // Offset 25
uint8_t file_unit_size; // Offset 26
uint8_t interleave_gap_size; // Offset 27
uint16_t volume_sequence_number_le; // Offset 28 (little-endian)
uint16_t volume_sequence_number_be; // Offset 30 (big-endian)
uint8_t file_id_length; // Offset 32
char file_id[]; // Offset 33
} DirectoryRecord;