Revert "fs: Drop ext* support"
This reverts commit ba5aca6fd81b40544eaab0a2a3baa6f24fce0947.
diff --git a/GNUmakefile.in b/GNUmakefile.in
index 7349acce..830f18b7 100644
--- a/GNUmakefile.in
+++ b/GNUmakefile.in
@@ -374,6 +374,29 @@ mbrtest.hdd:
dd if=/dev/zero bs=1M count=0 seek=64 of=mbrtest.hdd
echo -e "o\nn\np\n1\n2048\n\nt\n6\na\nw\n" | fdisk mbrtest.hdd -H 16 -S 63
+.PHONY: ext2-test
+ext2-test:
+ $(MAKE) test-clean
+ $(MAKE) test.hdd
+ $(MAKE) limine-bios
+ $(MAKE) limine-deploy
+ $(MAKE) -C test CROSS_FILE='$(call SHESCAPE,$(BUILDDIR))/cross-files/x86_64-toolchain.mk'
+ rm -rf test_image/
+ mkdir test_image
+ sudo losetup -Pf --show test.hdd > loopback_dev
+ sudo partprobe `cat loopback_dev`
+ sudo mkfs.ext2 `cat loopback_dev`p1
+ sudo mount `cat loopback_dev`p1 test_image
+ sudo mkdir test_image/boot
+ sudo cp -rv $(BINDIR)/* test_image/boot/
+ sudo cp -rv test/* test_image/boot/
+ sync
+ sudo umount test_image/
+ sudo losetup -d `cat loopback_dev`
+ rm -rf test_image loopback_dev
+ $(BINDIR)/limine-deploy test.hdd
+ qemu-system-x86_64 -net none -smp 4 -hda test.hdd -debugcon stdio
+
.PHONY: fat12-test
fat12-test:
$(MAKE) test-clean
diff --git a/README.md b/README.md
index 07f88e97..bae80cfa 100644
--- a/README.md
+++ b/README.md
@@ -24,12 +24,13 @@ as the reference implementation for the [Limine boot protocol](/PROTOCOL.md).
* Chainloading
### Supported filesystems
+* ext2/3/4
* FAT12/16/32
* ISO9660 (CDs/DVDs)
-**Even though these are the filesystems Limine directly supports, _any_**
-**filesystem can be utilised by the OS provided that the kernel (and any**
-**needed modules) reside on a directly supported filesystem.**
+Even though these are the filesystems Limine supports directly, *any*
+filesystem can be utilised by the kernel provided that the kernel (and any
+needed modules) are on a directly supported filesystem.
### Supported partitioning schemes
* MBR
diff --git a/common/fs/ext2.h b/common/fs/ext2.h
new file mode 100644
index 00000000..22f787ac
--- /dev/null
+++ b/common/fs/ext2.h
@@ -0,0 +1,13 @@
+#ifndef __FS__EXT2_H__
+#define __FS__EXT2_H__
+
+#include <stdbool.h>
+#include <lib/part.h>
+#include <fs/file.h>
+
+bool ext2_get_guid(struct guid *guid, struct volume *part);
+char *ext2_get_label(struct volume *part);
+
+struct file_handle *ext2_open(struct volume *part, const char *path);
+
+#endif
diff --git a/common/fs/ext2.s2.c b/common/fs/ext2.s2.c
new file mode 100644
index 00000000..9b56d615
--- /dev/null
+++ b/common/fs/ext2.s2.c
@@ -0,0 +1,685 @@
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <fs/ext2.h>
+#include <drivers/disk.h>
+#include <lib/libc.h>
+#include <lib/misc.h>
+#include <lib/print.h>
+#include <mm/pmm.h>
+
+/* Superblock Fields */
+struct ext2_superblock {
+ uint32_t s_inodes_count;
+ uint32_t s_blocks_count;
+ uint32_t s_r_blocks_count;
+ uint32_t s_free_blocks_count;
+ uint32_t s_free_inodes_count;
+ uint32_t s_first_data_block;
+ uint32_t s_log_block_size;
+ uint32_t s_log_frag_size;
+ uint32_t s_blocks_per_group;
+ uint32_t s_frags_per_group;
+ uint32_t s_inodes_per_group;
+ uint32_t s_mtime;
+ uint32_t s_wtime;
+
+ uint16_t s_mnt_count;
+ uint16_t s_max_mnt_count;
+ uint16_t s_magic;
+ uint16_t s_state;
+ uint16_t s_errors;
+ uint16_t s_minor_rev_level;
+
+ uint32_t s_lastcheck;
+ uint32_t s_checkinterval;
+ uint32_t s_creator_os;
+ uint32_t s_rev_level;
+ uint16_t s_def_resuid;
+ uint16_t s_def_gid;
+
+ // if version number >= 1, we have to use the ext2 extended superblock as well
+
+ /* Extended Superblock */
+ uint32_t s_first_ino;
+
+ uint16_t s_inode_size;
+ uint16_t s_block_group_nr;
+
+ uint32_t s_feature_compat;
+ uint32_t s_feature_incompat;
+ uint32_t s_feature_ro_compat;
+
+ uint64_t s_uuid[2];
+ uint8_t s_volume_name[16];
+
+ uint64_t s_last_mounted[8];
+
+ uint32_t compression_info;
+ uint8_t prealloc_blocks;
+ uint8_t prealloc_dir_blocks;
+ uint16_t reserved_gdt_blocks;
+ uint8_t journal_uuid[16];
+ uint32_t journal_inum;
+ uint32_t journal_dev;
+ uint32_t last_orphan;
+ uint32_t hash_seed[4];
+ uint8_t def_hash_version;
+ uint8_t jnl_backup_type;
+ uint16_t group_desc_size;
+ uint32_t default_mount_opts;
+ uint32_t first_meta_bg;
+ uint32_t mkfs_time;
+ uint32_t jnl_blocks[17];
+} __attribute__((packed));
+
+struct ext2_linux {
+ uint8_t frag_num;
+ uint8_t frag_size;
+
+ uint16_t reserved_16;
+ uint16_t user_id_high;
+ uint16_t group_id_high;
+
+ uint32_t reserved_32;
+} __attribute__((packed));
+
+struct ext2_inode {
+ uint16_t i_mode;
+ uint16_t i_uid;
+
+ uint32_t i_size;
+ uint32_t i_atime;
+ uint32_t i_ctime;
+ uint32_t i_mtime;
+ uint32_t i_dtime;
+
+ uint16_t i_gid;
+ uint16_t i_links_count;
+
+ uint32_t i_blocks_count;
+ uint32_t i_flags;
+ uint32_t i_osd1;
+ uint32_t i_blocks[15];
+ uint32_t i_generation;
+
+ /* EXT2 v >= 1.0 */
+ uint32_t i_eab;
+ uint32_t i_maj;
+
+ /* EXT2 vAll */
+ uint32_t i_frag_block;
+
+ struct ext2_linux i_osd2;
+} __attribute__((packed));
+
+struct ext2_file_handle {
+ struct volume *part;
+ struct ext2_superblock sb;
+ int size;
+ struct ext2_inode root_inode;
+ struct ext2_inode inode;
+ uint64_t block_size;
+ uint32_t *alloc_map;
+};
+
+/* Inode types */
+#define S_IFIFO 0x1000
+#define S_IFCHR 0x2000
+#define S_IFDIR 0x4000
+#define S_IFBLK 0x6000
+#define S_IFREG 0x8000
+#define S_IFLNK 0xa000
+#define S_IFSOCK 0xc000
+
+#define FMT_MASK 0xf000
+
+/* EXT2 Filesystem States */
+#define EXT2_FS_UNRECOVERABLE_ERRORS 3
+
+/* Ext2 incompatible features */
+#define EXT2_IF_COMPRESSION 0x01
+#define EXT2_IF_EXTENTS 0x40
+#define EXT2_IF_64BIT 0x80
+#define EXT2_IF_INLINE_DATA 0x8000
+#define EXT2_IF_ENCRYPT 0x10000
+#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010
+
+/* Ext4 flags */
+#define EXT4_EXTENTS_FLAG 0x80000
+
+#define EXT2_S_MAGIC 0xEF53
+
+/* EXT2 Block Group Descriptor */
+struct ext2_bgd {
+ uint32_t bg_block_bitmap;
+ uint32_t bg_inode_bitmap;
+ uint32_t bg_inode_table;
+
+ uint16_t bg_free_blocks_count;
+ uint16_t bg_free_inodes_count;
+ uint16_t bg_dirs_count;
+
+ uint16_t reserved[7];
+} __attribute__((packed));
+
+struct ext4_bgd {
+ uint32_t bg_block_bitmap;
+ uint32_t bg_inode_bitmap;
+ uint32_t bg_inode_table;
+
+ uint16_t bg_free_blocks_count;
+ uint16_t bg_free_inodes_count;
+ uint16_t bg_dirs_count;
+
+ uint16_t pad;
+ uint32_t reserved[3];
+ uint32_t block_id_hi;
+ uint32_t inode_id_hi;
+ uint32_t inode_table_id_hi;
+ uint16_t free_blocks_hi;
+ uint16_t free_inodes_hi;
+ uint16_t used_dirs_hi;
+ uint16_t pad2;
+ uint32_t reserved2[3];
+} __attribute__((packed));
+
+/* EXT2 Inode Types */
+#define EXT2_INO_DIRECTORY 0x4000
+
+/* EXT2 Directory Entry */
+struct ext2_dir_entry {
+ uint32_t inode;
+ uint16_t rec_len;
+ uint8_t name_len;
+ uint8_t type;
+} __attribute__((packed));
+
+struct ext4_extent_header {
+ uint16_t magic;
+ uint16_t entries;
+ uint16_t max;
+ uint16_t depth;
+ uint16_t generation;
+} __attribute__((packed));
+
+struct ext4_extent {
+ uint32_t block;
+ uint16_t len;
+ uint16_t start_hi;
+ uint32_t start;
+} __attribute__((packed));
+
+struct ext4_extent_idx {
+ uint32_t block;
+ uint32_t leaf;
+ uint16_t leaf_hi;
+ uint16_t empty;
+} __attribute__((packed));
+
+static int inode_read(void *buf, uint64_t loc, uint64_t count,
+ struct ext2_inode *inode, struct ext2_file_handle *fd,
+ uint32_t *alloc_map);
+static bool ext2_parse_dirent(struct ext2_dir_entry *dir, struct ext2_file_handle *fd, const char *path);
+
+// parse an inode given the partition base and inode number
+static bool ext2_get_inode(struct ext2_inode *ret,
+ struct ext2_file_handle *fd, uint64_t inode) {
+ if (inode == 0)
+ return false;
+
+ struct ext2_superblock *sb = &fd->sb;
+
+ //determine if we need to use 64 bit inode ids
+ bool bit64 = false;
+ if (sb->s_rev_level != 0
+ && (sb->s_feature_incompat & (EXT2_IF_64BIT))
+ && sb->group_desc_size != 0
+ && ((sb->group_desc_size & (sb->group_desc_size - 1)) == 0)) {
+ if(sb->group_desc_size > 32) {
+ bit64 = true;
+ }
+ }
+
+ const uint64_t ino_blk_grp = (inode - 1) / sb->s_inodes_per_group;
+ const uint64_t ino_tbl_idx = (inode - 1) % sb->s_inodes_per_group;
+
+ const uint64_t block_size = ((uint64_t)1024 << sb->s_log_block_size);
+ uint64_t ino_offset;
+ const uint64_t bgd_start_offset = block_size >= 2048 ? block_size : block_size * 2;
+ const uint64_t ino_size = sb->s_rev_level == 0 ? sizeof(struct ext2_inode) : sb->s_inode_size;
+
+ if (!bit64) {
+ struct ext2_bgd target_descriptor;
+ const uint64_t bgd_offset = bgd_start_offset + (sizeof(struct ext2_bgd) * ino_blk_grp);
+
+ volume_read(fd->part, &target_descriptor, bgd_offset, sizeof(struct ext2_bgd));
+
+ ino_offset = ((target_descriptor.bg_inode_table) * block_size) +
+ (ino_size * ino_tbl_idx);
+ } else {
+ struct ext4_bgd target_descriptor;
+ const uint64_t bgd_offset = bgd_start_offset + (sizeof(struct ext4_bgd) * ino_blk_grp);
+
+ volume_read(fd->part, &target_descriptor, bgd_offset, sizeof(struct ext4_bgd));
+
+ ino_offset = ((target_descriptor.bg_inode_table | (bit64 ? ((uint64_t)target_descriptor.inode_id_hi << 32) : 0)) * block_size) +
+ (ino_size * ino_tbl_idx);
+ }
+
+ volume_read(fd->part, ret, ino_offset, sizeof(struct ext2_inode));
+
+ return true;
+}
+
+static uint32_t *create_alloc_map(struct ext2_file_handle *fd,
+ struct ext2_inode *inode) {
+ if (inode->i_flags & EXT4_EXTENTS_FLAG)
+ return NULL;
+
+ size_t entries_per_block = fd->block_size / sizeof(uint32_t);
+
+ // Cache the map of blocks
+ uint32_t *alloc_map = ext_mem_alloc(inode->i_blocks_count * sizeof(uint32_t));
+ for (uint32_t i = 0; i < inode->i_blocks_count; i++) {
+ uint32_t block = i;
+ if (block < 12) {
+ // Direct block
+ alloc_map[i] = inode->i_blocks[block];
+ } else {
+ // Indirect block
+ block -= 12;
+ if (block >= entries_per_block) {
+ // Double indirect block
+ block -= entries_per_block;
+ uint32_t index = block / entries_per_block;
+ uint32_t indirect_block;
+ if (index >= entries_per_block) {
+ uint32_t first_index = index / entries_per_block;
+ uint32_t first_indirect_block;
+ volume_read(
+ fd->part, &first_indirect_block,
+ inode->i_blocks[14] * fd->block_size + first_index * sizeof(uint32_t),
+ sizeof(uint32_t)
+ );
+ uint32_t second_index = index % entries_per_block;
+ volume_read(
+ fd->part, &indirect_block,
+ first_indirect_block * fd->block_size + second_index * sizeof(uint32_t),
+ sizeof(uint32_t)
+ );
+ } else {
+ volume_read(
+ fd->part, &indirect_block,
+ inode->i_blocks[13] * fd->block_size + index * sizeof(uint32_t),
+ sizeof(uint32_t)
+ );
+ }
+ for (uint32_t j = 0; j < entries_per_block; j++) {
+ if (i + j >= inode->i_blocks_count)
+ return alloc_map;
+ volume_read(
+ fd->part, &alloc_map[i + j],
+ indirect_block * fd->block_size + j * sizeof(uint32_t),
+ sizeof(uint32_t)
+ );
+ }
+ i += entries_per_block - 1;
+ } else {
+ // Single indirect block
+ volume_read(
+ fd->part, &alloc_map[i],
+ inode->i_blocks[12] * fd->block_size + block * sizeof(uint32_t),
+ sizeof(uint32_t)
+ );
+ }
+ }
+ }
+
+ return alloc_map;
+}
+
+static bool symlink_to_inode(struct ext2_inode *inode, struct ext2_file_handle *fd,
+ const char *cwd, size_t cwd_len) {
+ // I cannot find whether this is 0-terminated or not, so I'm gonna take the
+ // safe route here and assume it is not.
+ if (inode->i_size < 59) {
+ struct ext2_dir_entry dir;
+ char *symlink = (char *)inode->i_blocks;
+ symlink[59] = 0;
+
+ char *abs = ext_mem_alloc(4096);
+ char *cwd_copy = ext_mem_alloc(cwd_len + 1);
+ memcpy(cwd_copy, cwd, cwd_len);
+ get_absolute_path(abs, symlink, cwd_copy);
+
+ pmm_free(cwd_copy, cwd_len + 1);
+
+ if (!ext2_parse_dirent(&dir, fd, abs)) {
+ pmm_free(abs, 4096);
+ return false;
+ }
+ pmm_free(abs, 4096);
+
+ ext2_get_inode(inode, fd, dir.inode);
+ return true;
+ } else {
+ print("ext2: Symlinks with destination paths longer than 60 chars unsupported\n");
+ return false;
+ }
+}
+
+static bool ext2_parse_dirent(struct ext2_dir_entry *dir, struct ext2_file_handle *fd, const char *path) {
+ if (*path != '/') {
+ panic(true, "ext2: Path does not start in /");
+ }
+
+ path++;
+
+ struct ext2_inode current_inode = fd->root_inode;
+
+ bool escape = false;
+ static char token[256];
+
+ bool ret;
+
+ const char *cwd = path - 1; // because /
+ size_t cwd_len = 1;
+ size_t next_cwd_len = cwd_len;
+
+next:
+ memset(token, 0, 256);
+
+ for (size_t i = 0; i < 255 && *path != '/' && *path != '\0'; i++, path++, next_cwd_len++)
+ token[i] = *path;
+
+ if (*path == '\0')
+ escape = true;
+ else
+ path++, next_cwd_len++;
+
+ uint32_t *alloc_map = create_alloc_map(fd, ¤t_inode);
+
+ for (uint32_t i = 0; i < current_inode.i_size; ) {
+ // preliminary read
+ inode_read(dir, i, sizeof(struct ext2_dir_entry),
+ ¤t_inode, fd, alloc_map);
+
+ // name read
+ char *name = ext_mem_alloc(dir->name_len + 1);
+
+ memset(name, 0, dir->name_len + 1);
+ inode_read(name, i + sizeof(struct ext2_dir_entry), dir->name_len,
+ ¤t_inode, fd, alloc_map);
+
+ int (*strcmpfn)(const char *, const char *) = case_insensitive_fopen ? strcasecmp : strcmp;
+
+ int test = strcmpfn(token, name);
+ pmm_free(name, dir->name_len + 1);
+
+ if (test == 0) {
+ if (escape) {
+ ret = true;
+ goto out;
+ } else {
+ // update the current inode
+ ext2_get_inode(¤t_inode, fd, dir->inode);
+ while ((current_inode.i_mode & FMT_MASK) != S_IFDIR) {
+ if ((current_inode.i_mode & FMT_MASK) == S_IFLNK) {
+ if (!symlink_to_inode(¤t_inode, fd, cwd, cwd_len)) {
+ ret = false;
+ goto out;
+ }
+ } else {
+ print("ext2: Part of path is not directory nor symlink\n");
+ ret = false;
+ goto out;
+ }
+ }
+ pmm_free(alloc_map, current_inode.i_blocks_count * sizeof(uint32_t));
+ cwd_len = next_cwd_len;
+ goto next;
+ }
+ }
+
+ i += dir->rec_len;
+ }
+
+ ret = false;
+
+out:
+ pmm_free(alloc_map, current_inode.i_blocks_count * sizeof(uint32_t));
+ return ret;
+}
+
+static void ext2_read(struct file_handle *handle, void *buf, uint64_t loc, uint64_t count);
+static void ext2_close(struct file_handle *file);
+
+struct file_handle *ext2_open(struct volume *part, const char *path) {
+ struct ext2_file_handle *ret = ext_mem_alloc(sizeof(struct ext2_file_handle));
+
+ ret->part = part;
+
+ volume_read(ret->part, &ret->sb, 1024, sizeof(struct ext2_superblock));
+
+ struct ext2_superblock *sb = &ret->sb;
+
+ if (sb->s_magic != EXT2_S_MAGIC) {
+ pmm_free(ret, sizeof(struct ext2_file_handle));
+ return NULL;
+ }
+
+ if (sb->s_rev_level != 0 &&
+ (sb->s_feature_incompat & EXT2_IF_COMPRESSION ||
+ sb->s_feature_incompat & EXT2_IF_INLINE_DATA ||
+ sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)) {
+ print("ext2: filesystem has unsupported features %x\n", sb->s_feature_incompat);
+ pmm_free(ret, sizeof(struct ext2_file_handle));
+ return NULL;
+ }
+
+ if (sb->s_rev_level != 0 && sb->s_feature_incompat & EXT2_IF_ENCRYPT) {
+ print("ext2: WARNING: File system has encryption feature on, stuff may misbehave\n");
+ }
+
+ if (sb->s_state == EXT2_FS_UNRECOVERABLE_ERRORS) {
+ print("ext2: unrecoverable errors found\n");
+ pmm_free(ret, sizeof(struct ext2_file_handle));
+ return NULL;
+ }
+
+ ret->block_size = ((uint64_t)1024 << ret->sb.s_log_block_size);
+
+ ext2_get_inode(&ret->root_inode, ret, 2);
+
+ struct ext2_dir_entry entry;
+
+ size_t cwd_len = 0;
+ char *cwd = ext_mem_alloc(4096);
+ for (int i = strlen(path) - 1; i > 0; i--) {
+ if (path[i] == '/' || path[i] == 0) {
+ cwd_len = i;
+ break;
+ }
+ }
+ memcpy(cwd, path, cwd_len);
+
+ if (!ext2_parse_dirent(&entry, ret, path)) {
+ pmm_free(cwd, 4096);
+ pmm_free(ret, sizeof(struct ext2_file_handle));
+ return NULL;
+ }
+
+ ext2_get_inode(&ret->inode, ret, entry.inode);
+
+ while ((ret->inode.i_mode & FMT_MASK) != S_IFREG) {
+ if ((ret->inode.i_mode & FMT_MASK) == S_IFLNK) {
+ if (!symlink_to_inode(&ret->inode, ret, cwd, cwd_len)) {
+ pmm_free(cwd, 4096);
+ pmm_free(ret, sizeof(struct ext2_file_handle));
+ return NULL;
+ }
+ } else {
+ print("ext2: Entity is not regular file nor symlink\n");
+ pmm_free(cwd, 4096);
+ pmm_free(ret, sizeof(struct ext2_file_handle));
+ return NULL;
+ }
+ }
+
+ pmm_free(cwd, 4096);
+
+ ret->size = ret->inode.i_size;
+
+ ret->alloc_map = create_alloc_map(ret, &ret->inode);
+
+ struct file_handle *handle = ext_mem_alloc(sizeof(struct file_handle));
+
+ handle->fd = ret;
+ handle->read = (void *)ext2_read;
+ handle->close = (void *)ext2_close;
+ handle->size = ret->size;
+ handle->vol = part;
+#if defined (UEFI)
+ handle->efi_part_handle = part->efi_part_handle;
+#endif
+
+ return handle;
+}
+
+static void ext2_close(struct file_handle *file) {
+ struct ext2_file_handle *f = file->fd;
+ if (f->alloc_map != NULL) {
+ pmm_free(f->alloc_map, f->inode.i_blocks_count * sizeof(uint32_t));
+ }
+ pmm_free(f, sizeof(struct ext2_file_handle));
+}
+
+static void ext2_read(struct file_handle *file, void *buf, uint64_t loc, uint64_t count) {
+ struct ext2_file_handle *f = file->fd;
+ inode_read(buf, loc, count, &f->inode, f, f->alloc_map);
+}
+
+static struct ext4_extent_header *ext4_find_leaf(struct ext4_extent_header *ext_block, uint32_t read_block, uint64_t block_size, struct volume *part) {
+ struct ext4_extent_idx *index;
+
+ void *buf = ext_mem_alloc(block_size);
+ memcpy(buf, ext_block, block_size);
+ ext_block = buf;
+
+ for (;;) {
+ index = (struct ext4_extent_idx *)((size_t)ext_block + 12);
+
+ #define EXT4_EXT_MAGIC 0xf30a
+ if (ext_block->magic != EXT4_EXT_MAGIC)
+ panic(false, "invalid extent magic");
+
+ if (ext_block->depth == 0) {
+ return ext_block;
+ }
+
+ int i;
+ for (i = 0; i < ext_block->entries; i++) {
+ if (read_block < index[i].block)
+ break;
+ }
+
+ if (--i < 0)
+ panic(false, "extent not found");
+
+ uint64_t block = ((uint64_t)index[i].leaf_hi << 32) | index[i].leaf;
+
+ volume_read(part, buf, (block * block_size), block_size);
+ ext_block = buf;
+ }
+}
+
+static int inode_read(void *buf, uint64_t loc, uint64_t count,
+ struct ext2_inode *inode, struct ext2_file_handle *fd,
+ uint32_t *alloc_map) {
+ for (uint64_t progress = 0; progress < count;) {
+ uint64_t block = (loc + progress) / fd->block_size;
+
+ uint64_t chunk = count - progress;
+ uint64_t offset = (loc + progress) % fd->block_size;
+ if (chunk > fd->block_size - offset)
+ chunk = fd->block_size - offset;
+
+ uint32_t block_index;
+
+ if (inode->i_flags & EXT4_EXTENTS_FLAG) {
+ struct ext4_extent_header *leaf;
+ struct ext4_extent *ext;
+ int i;
+
+ leaf = ext4_find_leaf((struct ext4_extent_header *)inode->i_blocks, block, fd->block_size, fd->part);
+
+ if (!leaf)
+ panic(false, "invalid extent");
+ ext = (struct ext4_extent*)((size_t)leaf + 12);
+
+ for (i = 0; i < leaf->entries; i++) {
+ if (block < ext[i].block) {
+ break;
+ }
+ }
+
+ if (--i >= 0) {
+ block -= ext[i].block;
+ if (block >= ext[i].len) {
+ panic(false, "block longer than extent");
+ } else {
+ uint64_t start = ((uint64_t)ext[i].start_hi << 32) + ext[i].start;
+ block_index = start + block;
+ }
+ } else {
+ panic(false, "extent for block not found");
+ }
+
+ pmm_free(leaf, fd->block_size);
+ } else {
+ block_index = alloc_map[block];
+ }
+
+ volume_read(fd->part, buf + progress, (block_index * fd->block_size) + offset, chunk);
+
+ progress += chunk;
+ }
+
+ return 0;
+}
+
+bool ext2_get_guid(struct guid *guid, struct volume *part) {
+ struct ext2_superblock sb;
+ volume_read(part, &sb, 1024, sizeof(struct ext2_superblock));
+
+ if (sb.s_magic != EXT2_S_MAGIC)
+ return false;
+
+ ((uint64_t *)guid)[0] = sb.s_uuid[0];
+ ((uint64_t *)guid)[1] = sb.s_uuid[1];
+
+ return true;
+}
+
+char *ext2_get_label(struct volume *part) {
+ struct ext2_superblock sb;
+ volume_read(part, &sb, 1024, sizeof(struct ext2_superblock));
+
+ if (sb.s_magic != EXT2_S_MAGIC) {
+ return NULL;
+ }
+
+ if (sb.s_rev_level < 1) {
+ return NULL;
+ }
+
+ size_t label_len = strlen((char *)sb.s_volume_name);
+ if (label_len == 0) {
+ return NULL;
+ }
+ char *ret = ext_mem_alloc(label_len + 1);
+ strcpy(ret, (char *)sb.s_volume_name);
+
+ return ret;
+}
diff --git a/common/fs/file.s2.c b/common/fs/file.s2.c
index 8516ed9d..df413f32 100644
--- a/common/fs/file.s2.c
+++ b/common/fs/file.s2.c
@@ -1,6 +1,7 @@
#include <stddef.h>
#include <stdint.h>
#include <fs/file.h>
+#include <fs/ext2.h>
#include <fs/fat32.h>
#include <fs/iso9660.h>
#include <lib/print.h>
@@ -16,11 +17,18 @@ char *fs_get_label(struct volume *part) {
if ((ret = fat32_get_label(part)) != NULL) {
return ret;
}
+ if ((ret = ext2_get_label(part)) != NULL) {
+ return ret;
+ }
return NULL;
}
bool fs_get_guid(struct guid *guid, struct volume *part) {
+ if (ext2_get_guid(guid, part) == true) {
+ return true;
+ }
+
return false;
}
@@ -50,6 +58,9 @@ struct file_handle *fopen(struct volume *part, const char *filename) {
}
#endif
+ if ((ret = ext2_open(part, filename)) != NULL) {
+ goto success;
+ }
if ((ret = iso9660_open(part, filename)) != NULL) {
goto success;
}
