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 <ro...@gnu.org> - Copyright (C) 2018, 2020 Pete Batard <p...@akeo.ie> + Copyright (C) 2018, 2020, 2024 Pete Batard <p...@akeo.ie> Copyright (C) 2018 Thomas Schmitt <scdbac...@gmx.net> Copyright (C) 2001 Herbert Valerio Riedel <h...@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