[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Libcdio-devel] [PATCH v2 2/4] Add El Torito virtual boot image support
From: |
Pete Batard |
Subject: |
[Libcdio-devel] [PATCH v2 2/4] Add El Torito virtual boot image support |
Date: |
Mon, 29 Jan 2024 13:48:06 +0000 |
Enables the El Torito boot images to be listed and extracted from a virtual
"[BOOT]/" root directory. Limited to 8 maximum images and only for No Emulation.
El Torito support can be enabled or disabled through ISO_EXTENSION_EL_TORITO.
The naming convention of the virtual images follows what 7-zip does.
We also try to be compliant with the specs "extension" that is used by UEFI
(Section 13.3.2.1 of UEFI v2.10 specs) that allows for El-Torito images to
occupy more than 0xffff virtual sectors if the size is set to 0 or 1.
---
include/cdio/iso9660.h | 40 ++++++++++++-
lib/iso9660/iso9660_fs.c | 117 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 152 insertions(+), 5 deletions(-)
diff --git a/include/cdio/iso9660.h b/include/cdio/iso9660.h
index 4fa12bda..72d49473 100644
--- a/include/cdio/iso9660.h
+++ b/include/cdio/iso9660.h
@@ -93,8 +93,9 @@ extern enum iso_enum1_s {
preparer id. */
MAX_ISOPATHNAME = 255, /**< Maximum number of characters in the
entire ISO 9660 filename. */
- ISO_BLOCKSIZE = 2048 /**< Number of bytes in an ISO 9660 block. */
-
+ ISO_BLOCKSIZE = 2048, /**< Number of bytes in an ISO 9660 block. */
+ VIRTUAL_SECTORSIZE = 512 /**< Number of bytes in an El Torito virtual
+ image sector */
} iso_enums1;
/*! An enumeration for some of the ISO_* \#defines below. This isn't
@@ -162,6 +163,7 @@ extern enum iso_vd_enum_s {
extern const char ISO_STANDARD_ID[sizeof("CD001")-1];
#define ISO_STANDARD_ID "CD001"
+#define EL_TORITO_ID "EL TORITO SPECIFICATION\0\0\0\0\0\0\0\0\0"
#define CDIO_EXTENT_BLOCKS(size) ((size + (ISO_BLOCKSIZE - 1)) / ISO_BLOCKSIZE)
@@ -506,6 +508,37 @@ struct iso9660_svd_s {
typedef struct iso9660_svd_s iso9660_svd_t;
+/*!
+ \brief ISO-9660 Boot Record Volume Descriptor.
+ */
+struct iso9660_brvd_s {
+ uint8_t type; /**< ISO_VD_BOOT_RECORD - 0
*/
+ char id[5]; /**< ISO_STANDARD_ID
"CD001" */
+ uint8_t version; /**< value 1 for El Torito
*/
+ char system_id[ISO_MAX_SYSTEM_ID]; /**< Boot system ID */
+ uint8_t unused1[32]; /**< unused - value 0 */
+ uint32_t boot_catalog_sector; /**< first sector of boot
catalog */
+ uint8_t unused2[1973]; /**< Unused - value 0 */
+} GNUC_PACKED;
+
+typedef struct iso9660_brvd_s iso9660_brvd_t;
+
+/*!
+ \brief ISO-9660 Section Entry.
+ */
+struct iso9660_br_s {
+ uint8_t boot_id; /**< Boot indicator - 0x88
*/
+ uint8_t media_type; /**< Boot media type - 0
for no emul. */
+ uint16_t load_seg; /**< Load segment for x86 */
+ uint8_t system_type; /**< System type - 0 for
x86 */
+ uint8_t unused1;
+ uint16_t num_sectors; /**< Virtual sectors count
of the image */
+ uint32_t image_lsn; /**< Start address of the
image */
+ uint8_t unused2[20];
+} GNUC_PACKED;
+
+typedef struct iso9660_br_s iso9660_br_t;
+
PRAGMA_END_PACKED
/*! \brief A data type for a list of ISO9660
@@ -582,7 +615,8 @@ extern enum iso_extension_enum_s {
ISO_EXTENSION_JOLIET_LEVEL2 = 0x02,
ISO_EXTENSION_JOLIET_LEVEL3 = 0x04,
ISO_EXTENSION_ROCK_RIDGE = 0x08,
- ISO_EXTENSION_HIGH_SIERRA = 0x10
+ ISO_EXTENSION_HIGH_SIERRA = 0x10,
+ ISO_EXTENSION_EL_TORITO = 0x20
} iso_extension_enums;
diff --git a/lib/iso9660/iso9660_fs.c b/lib/iso9660/iso9660_fs.c
index e5b8fa43..a1604b51 100644
--- a/lib/iso9660/iso9660_fs.c
+++ b/lib/iso9660/iso9660_fs.c
@@ -1,7 +1,7 @@
/*
Copyright (C) 2003-2008, 2011-2015, 2017, 2024
Rocky Bernstein <rocky@gnu.org>
- Copyright (C) 2018, 2020 Pete Batard <pete@akeo.ie>
+ Copyright (C) 2018, 2020, 2024 Pete Batard <pete@akeo.ie>
Copyright (C) 2018 Thomas Schmitt <scdbackup@gmx.net>
Copyright (C) 2001 Herbert Valerio Riedel <hvr@gnu.org>
@@ -60,6 +60,9 @@
#include "_cdio_stdio.h"
#include "cdio_private.h"
+/* Maximum number of El-Torito boot images we keep an index for */
+#define MAX_BOOT_IMAGES 8
+
/** Implementation of iso9660_t type */
struct _iso9660_s {
cdio_header_t header; /**< Internal header - MUST come first. */
@@ -84,6 +87,11 @@ struct _iso9660_s {
be CDIO_CD_FRAMESIZE_RAW (2352) or
M2RAW_SECTOR_SIZE (2336).
*/
+ struct {
+ uint32_t lsn; /**< Start LSN of an El-Torito bootable image */
+ uint32_t num_sectors; /**< Number of virtual sectors occupied by the
+ bootable image */
+ } boot_img[MAX_BOOT_IMAGES];
int i_fuzzy_offset; /**< Adjustment in bytes to make ISO_STANDARD_ID
("CD001") come out as ISO_PVD_SECTOR
(frame 16). Normally this should be 0
@@ -504,7 +512,8 @@ iso9660_ifs_read_superblock (iso9660_t *p_iso,
iso_extension_mask_t iso_extension_mask)
{
iso9660_svd_t p_svd; /* Secondary volume descriptor. */
- int i;
+ iso9660_brvd_t* p_brvd = (iso9660_brvd_t*)&p_svd; /* Boot record volume
descriptor. */
+ int i, j, k;
if (!p_iso || !iso9660_ifs_read_pvd(p_iso, &(p_iso->pvd)))
return false;
@@ -515,6 +524,42 @@ iso9660_ifs_read_superblock (iso9660_t *p_iso,
for (i=1; (0 != iso9660_iso_seek_read (p_iso, &p_svd, ISO_PVD_SECTOR+i, 1));
i++) {
if (ISO_VD_END == from_711(p_svd.type) ) /* Last SVD */
break;
+ if (iso_extension_mask & ISO_EXTENSION_EL_TORITO) {
+ /* Check for an El-Torito boot volume descriptor */
+ if (ISO_VD_BOOT_RECORD == from_711(p_svd.type) &&
+ (memcmp(p_brvd->system_id, EL_TORITO_ID, ISO_MAX_SYSTEM_ID) == 0)) {
+ /* Perform basic parsing of boot entries to fill an image table */
+ iso9660_br_t br[ISO_BLOCKSIZE / sizeof(iso9660_br_t)];
+ uint32_t max_lsn = from_733(p_iso->pvd.volume_space_size);
+ if (iso9660_iso_seek_read(p_iso, &br, p_brvd->boot_catalog_sector, 1)
== ISO_BLOCKSIZE) {
+ for (j = 0, k = 0;
+ j < (ISO_BLOCKSIZE / sizeof(iso9660_br_t)) && k <
MAX_BOOT_IMAGES;
+ j++) {
+ if (br[j].boot_id == 0x88 && br[j].media_type == 0) {
+ p_iso->boot_img[k].lsn = br[j].image_lsn;
+ p_iso->boot_img[k++].num_sectors = br[j].num_sectors;
+ }
+ }
+ /* Special case for non specs-compliant images that do follow the
UEFI */
+ /* specs and that use size 0 or 1 for images larger than 0xffff
virtual */
+ /* sectors, in which case the image runs to the end of the volume.
*/
+ /* First, find the entry with the max LSN. */
+ for (j = 1, k = 0; j < MAX_BOOT_IMAGES; j++) {
+ if (p_iso->boot_img[j].lsn > p_iso->boot_img[k].lsn)
+ k = j;
+ }
+ /* If the entry is valid and has num_sectors set to 0 or 1, expand
its */
+ /* size to the end of the volume, if that size is more than 0xffff.
*/
+ cdio_assert(ISO_BLOCKSIZE / VIRTUAL_SECTORSIZE == 4);
+ if ((p_iso->boot_img[k].lsn != 0 && p_iso->boot_img[k].num_sectors <=
1) &&
+ ((max_lsn - p_iso->boot_img[k].lsn) >= 0x4000)) {
+ p_iso->boot_img[k].num_sectors =
+ (max_lsn - p_iso->boot_img[k].lsn) * 4;
+ cdio_warn("Auto-expanding the size of the last El-Torito image");
+ }
+ }
+ }
+ }
if ( ISO_VD_SUPPLEMENTARY == from_711(p_svd.type) ) {
/* We're only interested in Joliet => make sure the SVD isn't
overwritten */
if (p_iso->u_joliet_level == 0)
@@ -1440,6 +1485,34 @@ iso9660_fs_stat_translate (CdIo_t *p_cdio, const char
psz_path[])
iso9660_stat_t *
iso9660_ifs_stat_translate (iso9660_t *p_iso, const char psz_path[])
{
+ /* Special case for virtual El-Torito boot images
('/[BOOT]/#-Boot-NoEmul.img') */
+ if (psz_path && p_iso && p_iso->boot_img[0].lsn != 0) {
+ /* Work on a path without leading slash */
+ const char* path = (psz_path[0] == '/') ? &psz_path[1] : psz_path;
+ if ((_cdio_strnicmp(path, "[BOOT]/", 7) == 0) &&
+ (_cdio_stricmp(&path[8], "-Boot-NoEmul.img") == 0)) {
+ int index = path[7] - '0';
+ iso9660_stat_t* p_stat;
+ if (strlen(path) < 24)
+ return NULL;
+ cdio_assert(MAX_BOOT_IMAGES <= 10);
+ if ((path[7] < '0') || (path[7] > '0' + MAX_BOOT_IMAGES - 1))
+ return NULL;
+ if (p_iso->boot_img[index].lsn == 0 ||
p_iso->boot_img[index].num_sectors == 0)
+ return NULL;
+ p_stat = calloc(1, sizeof(iso9660_stat_t) + strlen(path));
+ if (!p_stat) {
+ cdio_warn("Couldn't calloc(1, %d)", (int)sizeof(iso9660_stat_t));
+ return NULL;
+ }
+ p_stat->lsn = p_iso->boot_img[index].lsn;
+ p_stat->total_size = p_iso->boot_img[index].num_sectors *
VIRTUAL_SECTORSIZE;
+ p_stat->type = _STAT_FILE;
+ strcpy(p_stat->filename, path);
+ return p_stat;
+ }
+ }
+
return fs_stat_translate(p_iso, (stat_root_t *) _ifs_stat_root,
(stat_traverse_t *) _fs_iso_stat_traverse,
psz_path);
@@ -1575,6 +1648,7 @@ iso9660_fs_readdir (CdIo_t *p_cdio, const char psz_path[])
CdioISO9660FileList_t *
iso9660_ifs_readdir (iso9660_t *p_iso, const char psz_path[])
{
+ int i;
iso9660_dir_t *p_iso9660_dir;
iso9660_stat_t *p_iso9660_stat = NULL;
iso9660_stat_t *p_stat;
@@ -1582,6 +1656,30 @@ iso9660_ifs_readdir (iso9660_t *p_iso, const char
psz_path[])
if (!p_iso) return NULL;
if (!psz_path) return NULL;
+ /* List the virtual El-Torito images */
+ if (p_iso->boot_img[0].lsn != 0) {
+ const char* path = (psz_path[0] == '/') ? &psz_path[1] : psz_path;
+ if (_cdio_strnicmp(path, "[BOOT]", 6) == 0 && (path[6] == '\0' || path[6]
== '/')) {
+ CdioList_t* retval = _cdio_list_new();
+ for (i = 0; i < MAX_BOOT_IMAGES && p_iso->boot_img[i].lsn != 0; i++) {
+ p_iso9660_stat = calloc(1, sizeof(iso9660_stat_t) + 18);
+ if (!p_iso9660_stat) {
+ cdio_warn("Couldn't calloc(1, %d)", (int)sizeof(iso9660_stat_t) + 18);
+ break;
+ }
+ strcpy(p_iso9660_stat->filename, "#-Boot-NoEmul.img");
+ p_iso9660_stat->filename[0] = '0' + i;
+ p_iso9660_stat->type = _STAT_FILE;
+ p_iso9660_stat->lsn = p_iso->boot_img[i].lsn;
+ p_iso9660_stat->total_size = p_iso->boot_img[i].num_sectors *
VIRTUAL_SECTORSIZE;
+ iso9660_get_ltime(&p_iso->pvd.creation_date, &p_iso9660_stat->tm);
+ _cdio_list_append(retval, p_iso9660_stat);
+ p_iso9660_stat = NULL;
+ }
+ return retval;
+ }
+ }
+
p_stat = iso9660_ifs_stat (p_iso, psz_path);
if (!p_stat) return NULL;
@@ -1599,6 +1697,21 @@ iso9660_ifs_readdir (iso9660_t *p_iso, const char
psz_path[])
const size_t dirbuf_len = blocks * ISO_BLOCKSIZE;
bool skip_following_extents = false;
+ /* Add the virtual El-Torito "[BOOT]" directory to root */
+ if (p_iso->boot_img[0].lsn != 0) {
+ if (psz_path[0] == '\0' || (psz_path[0] == '/' && psz_path[1] == '\0')) {
+ p_iso9660_stat = calloc(1, sizeof(iso9660_stat_t) + 7);
+ if (p_iso9660_stat) {
+ strcpy(p_iso9660_stat->filename, "[BOOT]");
+ p_iso9660_stat->type = _STAT_DIR;
+ p_iso9660_stat->lsn = ISO_PVD_SECTOR + 1;
+ iso9660_get_ltime(&p_iso->pvd.creation_date, &p_iso9660_stat->tm);
+ _cdio_list_append(retval, p_iso9660_stat);
+ p_iso9660_stat = NULL;
+ }
+ }
+ }
+
if (!dirbuf_len)
{
cdio_warn("Invalid directory buffer sector size %u", blocks);
--
2.43.0.windows.1