libcdio-devel
[Top][All Lists]
Advanced

[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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]