:: commit ce7d6558cdaf48f168af4804e2da21e7ef8eeecd

Mintsuki <mintsuki@protonmail.com> — 2025-12-27 20:27

parents: 4515fa62dc

fs/iso9660: Add support for multi-extent files

diff --git a/common/fs/iso9660.s2.c b/common/fs/iso9660.s2.c
index 35304840..63210580 100644
--- a/common/fs/iso9660.s2.c
+++ b/common/fs/iso9660.s2.c
@@ -13,12 +13,20 @@ struct iso9660_context {
     uint32_t root_size;
 };
 
-struct iso9660_file_handle {
-    struct iso9660_context *context;
+struct iso9660_extent {
     uint32_t LBA;
     uint32_t size;
 };
 
+struct iso9660_file_handle {
+    struct iso9660_context *context;
+    uint64_t total_size;
+    uint32_t extent_count;
+    struct iso9660_extent *extents;
+};
+
+#define ISO9660_FLAG_MULTI_EXTENT 0x80
+
 #define ISO9660_FIRST_VOLUME_DESCRIPTOR 0x10
 #define ISO9660_VOLUME_DESCRIPTOR_SIZE ISO9660_SECTOR_SIZE
 #define ROCK_RIDGE_MAX_FILENAME 255
@@ -246,6 +254,46 @@ use_iso_name:
     return false;
 }
 
+// Advance to the next directory entry in the buffer
+// Returns NULL if no more entries or invalid entry
+static struct iso9660_directory_entry *iso9660_next_entry(void *current, void *buffer_end) {
+    struct iso9660_directory_entry *entry = current;
+
+    if (entry->length == 0) {
+        // Skip to next sector boundary
+        uintptr_t current_addr = (uintptr_t)current;
+        uintptr_t next_sector = ALIGN_UP(current_addr + 1, ISO9660_SECTOR_SIZE);
+        if (next_sector >= (uintptr_t)buffer_end)
+            return NULL;
+        entry = (struct iso9660_directory_entry *)next_sector;
+        if (entry->length == 0)
+            return NULL;
+        return entry;
+    }
+
+    void *next = (uint8_t *)current + entry->length;
+    if (next >= buffer_end)
+        return NULL;
+
+    entry = next;
+
+    // Handle zero-length entries (padding at sector boundaries)
+    if (entry->length == 0) {
+        uintptr_t next_sector = ALIGN_UP((uintptr_t)next + 1, ISO9660_SECTOR_SIZE);
+        if (next_sector >= (uintptr_t)buffer_end)
+            return NULL;
+        entry = (struct iso9660_directory_entry *)next_sector;
+        if (entry->length == 0)
+            return NULL;
+    }
+
+    // Validate minimum entry size
+    if (entry->length < sizeof(struct iso9660_directory_entry))
+        return NULL;
+
+    return entry;
+}
+
 static struct iso9660_directory_entry *iso9660_find(void *buffer, uint32_t size, const char *filename) {
     while (size) {
         struct iso9660_directory_entry *entry = buffer;
@@ -367,8 +415,45 @@ struct file_handle *iso9660_open(struct volume *vol, const char *path) {
         next_sector = entry->extent.little;
         next_size = entry->extent_size.little;
 
-        if (*path == '\0')
-            break;    // Found :)
+        if (*path == '\0') {
+            // Found the file - collect all extents for multi-extent files
+            void *buffer_end = (uint8_t *)current + current_size;
+
+            // First pass: count extents and calculate total size
+            uint32_t extent_count = 1;
+            uint64_t total_size = entry->extent_size.little;
+            struct iso9660_directory_entry *e = entry;
+
+            while (e->flags & ISO9660_FLAG_MULTI_EXTENT) {
+                e = iso9660_next_entry(e, buffer_end);
+                if (e == NULL)
+                    break;
+                extent_count++;
+                total_size += e->extent_size.little;
+            }
+
+            // Allocate extent array
+            ret->extents = ext_mem_alloc(extent_count * sizeof(struct iso9660_extent));
+            ret->extent_count = extent_count;
+            ret->total_size = total_size;
+
+            // Second pass: populate extent array
+            e = entry;
+            for (uint32_t i = 0; i < extent_count; i++) {
+                ret->extents[i].LBA = e->extent.little;
+                ret->extents[i].size = e->extent_size.little;
+                if (i + 1 < extent_count) {
+                    e = iso9660_next_entry(e, buffer_end);
+                }
+            }
+
+            // Free the directory buffer if we allocated one
+            if (!first) {
+                pmm_free(current, current_size);
+            }
+
+            goto setup_handle;
+        }
 
         path++;  // Skip the '/' separator
 
@@ -401,15 +486,20 @@ struct file_handle *iso9660_open(struct volume *vol, const char *path) {
         pmm_free(current, current_size);
     }
 
-    ret->LBA = next_sector;
-    ret->size = next_size;
+    // Fallback path (trailing slash case) - create single extent
+    ret->extents = ext_mem_alloc(sizeof(struct iso9660_extent));
+    ret->extent_count = 1;
+    ret->total_size = next_size;
+    ret->extents[0].LBA = next_sector;
+    ret->extents[0].size = next_size;
 
+setup_handle:;
     struct file_handle *handle = ext_mem_alloc(sizeof(struct file_handle));
 
     handle->fd = ret;
     handle->read = (void *)iso9660_read;
     handle->close = (void *)iso9660_close;
-    handle->size = ret->size;
+    handle->size = ret->total_size;
     handle->vol = vol;
 #if defined (UEFI)
     handle->efi_part_handle = vol->efi_part_handle;
@@ -420,17 +510,41 @@ struct file_handle *iso9660_open(struct volume *vol, const char *path) {
 
 static void iso9660_read(struct file_handle *file, void *buf, uint64_t loc, uint64_t count) {
     struct iso9660_file_handle *f = file->fd;
-    uint64_t base_offset;
-    if (__builtin_mul_overflow((uint64_t)f->LBA, (uint64_t)ISO9660_SECTOR_SIZE, &base_offset)) {
-        panic(false, "iso9660: offset calculation overflow");
-    }
-    uint64_t offset;
-    if (__builtin_add_overflow(base_offset, loc, &offset)) {
-        panic(false, "iso9660: offset calculation overflow");
+
+    // Find which extent 'loc' falls into and read across extents as needed
+    uint64_t extent_start = 0;
+    for (uint32_t i = 0; i < f->extent_count && count > 0; i++) {
+        uint64_t extent_size = f->extents[i].size;
+        uint64_t extent_end = extent_start + extent_size;
+
+        if (loc < extent_end) {
+            // Read starts (or continues) in this extent
+            uint64_t offset_in_extent = (loc > extent_start) ? (loc - extent_start) : 0;
+            uint64_t bytes_available = extent_size - offset_in_extent;
+            uint64_t to_read = (count < bytes_available) ? count : bytes_available;
+
+            uint64_t base_offset;
+            if (__builtin_mul_overflow((uint64_t)f->extents[i].LBA, (uint64_t)ISO9660_SECTOR_SIZE, &base_offset)) {
+                panic(false, "iso9660: offset calculation overflow");
+            }
+            uint64_t disk_offset;
+            if (__builtin_add_overflow(base_offset, offset_in_extent, &disk_offset)) {
+                panic(false, "iso9660: offset calculation overflow");
+            }
+
+            volume_read(f->context->vol, buf, disk_offset, to_read);
+
+            buf = (uint8_t *)buf + to_read;
+            loc += to_read;
+            count -= to_read;
+        }
+
+        extent_start = extent_end;
     }
-    volume_read(f->context->vol, buf, offset, count);
 }
 
 static void iso9660_close(struct file_handle *file) {
-    pmm_free(file->fd, sizeof(struct iso9660_file_handle));
+    struct iso9660_file_handle *f = file->fd;
+    pmm_free(f->extents, f->extent_count * sizeof(struct iso9660_extent));
+    pmm_free(f, sizeof(struct iso9660_file_handle));
 }
tab: 248 wrap: offon