From 8ae16342b6f268cc8eb6280f534fa51a292eaf39 Mon Sep 17 00:00:00 2001 From: kamkow1 Date: Mon, 6 Apr 2026 22:45:54 +0200 Subject: [PATCH] ISO9660 filesystem driver --- aux/limine_img_amd64.sh | 2 +- ce/interp.c | 4 +- include/fs_types.h | 7 +- kernel/Makefile | 4 +- kernel/device/usb/xhci.c | 14 +- kernel/fs/fatfs.c | 4 - kernel/fs/iso9660fs.c | 316 ++++++++++++++++++++++++++++++++++++++ kernel/fs/iso9660fs.h | 32 ++++ kernel/fs/lib9660.c | 317 +++++++++++++++++++++++++++++++++++++++ kernel/fs/lib9660.h | 210 ++++++++++++++++++++++++++ kernel/fs/src.mk | 8 +- kernel/fs/vfs.c | 12 ++ 12 files changed, 911 insertions(+), 19 deletions(-) create mode 100644 kernel/fs/iso9660fs.c create mode 100644 kernel/fs/iso9660fs.h create mode 100644 kernel/fs/lib9660.c create mode 100644 kernel/fs/lib9660.h diff --git a/aux/limine_img_amd64.sh b/aux/limine_img_amd64.sh index 2f40312..2aaef75 100755 --- a/aux/limine_img_amd64.sh +++ b/aux/limine_img_amd64.sh @@ -6,7 +6,7 @@ mkdir -p img_root/boot/limine mkdir -p img_root/EFI/BOOT cp -v kernel/build/kernel.elf img_root/boot/ -cp -v boot/limine/limine-bios.sys boot/limine/limine.conf img_root/boot/limine/ +cp -v boot/limine/limine-bios.sys boot/limine.conf img_root/boot/limine/ cp -v boot/limine/limine-bios-cd.bin boot/limine/limine-uefi-cd.bin img_root/boot/limine/ cp -v boot/limine/BOOTX64.EFI boot/limine/BOOTIA32.EFI img_root/EFI/BOOT/ cp -v mop3dist.tar.lz4 img_root/boot/ diff --git a/ce/interp.c b/ce/interp.c index 15d916e..b3b01d3 100644 --- a/ce/interp.c +++ b/ce/interp.c @@ -241,7 +241,7 @@ static void ls (struct context* context, const char* path_string) { char type = (desc.type == FS_DIR ? 'D' : 'F'); - cprintf (context, "%c %-40s %-40s\n", type, entry.path, size_buf); + cprintf (context, "%c %-50s %-10s\n", type, path_basename (entry.path), size_buf); } volume_close (); @@ -306,6 +306,8 @@ static void mkvol (struct context* context, const char* volume, const char* str_ fs_type = FS_FAT16; } else if (strcmp (str_fs_type, "fat32") == 0) { fs_type = FS_FAT32; + } else if (strcmp (str_fs_type, "iso9660") == 0) { + fs_type = FS_ISO9660; } else { cprintf (context, "ERROR Unknown filesystem '%s'\n", str_fs_type); return; diff --git a/include/fs_types.h b/include/fs_types.h index 1009000..297215e 100644 --- a/include/fs_types.h +++ b/include/fs_types.h @@ -1,8 +1,9 @@ #ifndef _FS_TYPES_H #define _FS_TYPES_H -#define FS_TARFS 0 -#define FS_FAT16 1 -#define FS_FAT32 2 +#define FS_TARFS 0 +#define FS_FAT16 1 +#define FS_FAT32 2 +#define FS_ISO9660 3 #endif // _FS_TYPES_H diff --git a/kernel/Makefile b/kernel/Makefile index 2a0b900..cb6432a 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -34,6 +34,8 @@ format: ':!libk/printf*' \ ':!lz4/lz4*' \ ':!device/pci/pci_defs.c' \ - ':!mm/malloc*') + ':!mm/malloc*' \ + ':!fs/lib9660.c' \ + ':!fs/lib9660.h') .PHONY: all clean format diff --git a/kernel/device/usb/xhci.c b/kernel/device/usb/xhci.c index 9f5a361..313d633 100644 --- a/kernel/device/usb/xhci.c +++ b/kernel/device/usb/xhci.c @@ -110,7 +110,7 @@ static void xhci_bios_handover (struct xhci* xhci) { if (!(val & (1 << XHCI_USBLEGSUP_BIOS_SEMA)) && (val & (1 << XHCI_USBLEGSUP_OS_SEMA))) break; - stall_ms (3); + stall_ms (2); } DEBUG ("XHCI Handover OK\n"); @@ -257,8 +257,8 @@ static void xhci_event_dispatch (struct xhci* xhci, struct xhci_trb* event, uint uint8_t slot_id = (event->ctrl >> XHCI_TETRB_CTRL_SLOT_ID) & 0xFF; uint8_t endpoint_id = (event->ctrl >> XHCI_TETRB_CTRL_ENDPOINT) & 0x1F; - DEBUG ("transfer completion: code=%u,slot=%u,endpoint_id=%u\n", cmpl_code, slot_id, - endpoint_id); + /* DEBUG ("transfer completion: code=%u,slot=%u,endpoint_id=%u\n", cmpl_code, slot_id, */ + /* endpoint_id); */ xhci->last_slot_id = slot_id; xhci->last_cmpl_code = cmpl_code; @@ -406,7 +406,7 @@ static bool xhci_endpoint0_ctrl_in (struct xhci* xhci, struct xhci_usb_device* u xhci_write32 (xhci->xhci_doorbell_base, usb_device->slot_id * 4, 1); while (atomic_load (&xhci->pending) && --timeout > 0) - stall_ms (10); + stall_ms (1); spin_lock (&xhci->device->lock, lockflags); @@ -482,7 +482,7 @@ static bool xhci_endpoint0_ctrl_out (struct xhci* xhci, struct xhci_usb_device* xhci_write32 (xhci->xhci_doorbell_base, usb_device->slot_id * 4, 1); while (atomic_load (&xhci->pending) && --timeout > 0) - stall_ms (10); + stall_ms (1); spin_lock (&xhci->device->lock, lockflags); @@ -530,7 +530,7 @@ static void xhci_send_cmd (struct xhci* xhci, uint64_t param, uint32_t status, u xhci_write32 (xhci->xhci_doorbell_base, 0, 0); while (atomic_load (&xhci->pending) && --timeout > 0) - stall_ms (10); + stall_ms (1); spin_lock (&xhci->device->lock, lockflags); @@ -1046,7 +1046,7 @@ int xhci_bulk_transfer (struct xhci* xhci, struct xhci_usb_device* usb_device, xhci_write32 (xhci->xhci_doorbell_base, usb_device->slot_id * 4, dci); while (atomic_load (&xhci->pending) && --timeout > 0) - stall_ms (10); + stall_ms (1); spin_lock (&xhci->device->lock, lockflags); diff --git a/kernel/fs/fatfs.c b/kernel/fs/fatfs.c index 88443b0..596e2c7 100644 --- a/kernel/fs/fatfs.c +++ b/kernel/fs/fatfs.c @@ -52,8 +52,6 @@ static int fat1_diskio_read (struct fatfs_ctx* ctx, uint32_t sector, uint8_t* bu spin_unlock (&back_device->lock, fd); - if (ret < 0) - return 0; return 1; } @@ -91,8 +89,6 @@ static int fat1_diskio_write (struct fatfs_ctx* ctx, uint32_t sector, uint8_t* b spin_unlock (&back_device->lock, fd); - if (ret < 0) - return 0; return 1; } diff --git a/kernel/fs/iso9660fs.c b/kernel/fs/iso9660fs.c new file mode 100644 index 0000000..7cb4cab --- /dev/null +++ b/kernel/fs/iso9660fs.c @@ -0,0 +1,316 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ISO9660_SECTOR_SIZE 2048 + +static bool iso9660fs_read_sector (l9660_fs* fs, void* buf, uint32_t sector) { + uint64_t fd; + struct vfs_volume* volume = fs->udata; + struct device* back_device = volume->back_device; + + size_t sector_size; + size_t phys_sector, phys_sector_count; + int ret; + + spin_lock (&back_device->lock, &fd); + + ret = device_op (back_device, XDRV_GET_SECTOR_SIZE, fs->proc, fs->rctx, &fd, §or_size); + if (ret < 0) { + spin_unlock (&back_device->lock, fd); + return false; + } + + vfs_translate (sector, 1, ISO9660_SECTOR_SIZE, sector_size, &phys_sector, &phys_sector_count); + + ret = device_op (back_device, XDRV_READ, fs->proc, fs->rctx, &fd, &phys_sector, + &phys_sector_count, buf); + + if (ret < 0) { + spin_unlock (&back_device->lock, fd); + return false; + } + + spin_unlock (&back_device->lock, fd); + + return true; +} + +static bool iso9660fs_is_cat (l9660_dirent* dirent) { + if (dirent->name_len < 4) + return false; + + size_t end = dirent->name_len; + + for (size_t i = 0; i < dirent->name_len; i++) { + if (dirent->name[i] == ';') { + end = i; + break; + } + } + + if (end < 4) + return false; + + const char* n = dirent->name; + + return (n[end - 4] == '.' && + (n[end - 3] == 'c' || n[end - 3] == 'C') && + (n[end - 2] == 'a' || n[end - 2] == 'A') && + (n[end - 1] == 't' || n[end - 1] == 'T')); +} + +static bool iso9660fs_is_special_entry (l9660_dirent* dirent) { + return (dirent->name_len == 1 && (dirent->name[0] == '\0' || dirent->name[0] == '\1')) || + iso9660fs_is_cat (dirent); +} + +static void iso9660fs_copy_visible_name (char* dst, size_t dst_size, l9660_dirent* dirent) { + size_t i = 0; + + if (dst_size == 0) + return; + + while (i + 1 < dst_size && i < dirent->name_len) { + if (dirent->name[i] == ';') + break; + + dst[i] = dirent->name[i]; + i++; + } + + dst[i] = '\0'; +} + +static l9660_status iso9660fs_count_dir_entries (l9660_dir* dir, size_t* count) { + l9660_status status; + l9660_dirent* dirent = NULL; + + *count = 0; + status = l9660_seekdir (dir, 0); + + if (status != L9660_OK) + return status; + + for (;;) { + status = l9660_readdir (dir, &dirent); + + if (status != L9660_OK) + return status; + + if (dirent == NULL) + break; + + if (iso9660fs_is_special_entry (dirent)) + continue; + + (*count)++; + } + + return L9660_OK; +} + +DEFINE_VFS_MOUNT (iso9660fs_mount) { + l9660_fs* fs_ctx = malloc (sizeof (*fs_ctx)); + + if (fs_ctx == NULL) + return -ST_OOM_ERROR; + + memset (fs_ctx, 0, sizeof (*fs_ctx)); + fs_ctx->udata = volume; + volume->udata = fs_ctx; + + fs_ctx->proc = proc; + fs_ctx->rctx = rctx; + + l9660_status status = l9660_openfs (fs_ctx, &iso9660fs_read_sector); + + if (status != L9660_OK) { + free (fs_ctx); + return -ST_MOUNT_ERROR; + } + + return ST_OK; +} + +DEFINE_VFS_FORMAT (iso9660fs_format) { return -ST_FORMAT_ERROR; } + +DEFINE_VFS_DESCRIBE (iso9660fs_describe) { + l9660_fs* fs = volume->udata; + + fs->proc = proc; + fs->rctx = rctx; + + l9660_status status; + l9660_dir root_dir; + l9660_file file; + l9660_dir dir; + + const char* relpath = path; + if (relpath[0] == '/') + relpath++; + + status = l9660_fs_open_root (&root_dir, fs); + + if (status != L9660_OK) + return -ST_NOT_FOUND; + + if (relpath[0] == '\0') { + desc->type = FS_DIR; + status = iso9660fs_count_dir_entries (&root_dir, &desc->size); + + if (status != L9660_OK) + return -ST_NOT_FOUND; + + return ST_OK; + } + + status = l9660_openat (&file, &root_dir, relpath); + + if (status == L9660_OK) { + desc->type = FS_FILE; + desc->size = file.length; + } else { + status = l9660_opendirat (&dir, &root_dir, relpath); + + if (status != L9660_OK) + return -ST_NOT_FOUND; + + desc->type = FS_DIR; + status = iso9660fs_count_dir_entries (&dir, &desc->size); + + if (status != L9660_OK) + return -ST_NOT_FOUND; + } + + return ST_OK; +} + +DEFINE_VFS_READ_FILE (iso9660fs_read_file) { + l9660_fs* fs = volume->udata; + + fs->proc = proc; + fs->rctx = rctx; + + l9660_status status; + l9660_dir root_dir; + l9660_file file; + + status = l9660_fs_open_root (&root_dir, fs); + + if (status != L9660_OK) + return -ST_NOT_FOUND; + + const char* relpath = path; + if (relpath[0] == '/') + relpath++; + + status = l9660_openat (&file, &root_dir, relpath); + + if (status != L9660_OK) + return -ST_NOT_FOUND; + + status = l9660_seek (&file, L9660_SEEK_SET, off); + + if (status != L9660_OK) + return -ST_OOB_ERROR; + + size_t n; + status = l9660_read (&file, buffer, size, &n); + + if (status != L9660_OK) + return -ST_XDRV_READ_ERROR; + + return ST_OK; +} + +DEFINE_VFS_WRITE_FILE (iso9660fs_write_file) { return -ST_XDRV_WRITE_ERROR; } + +DEFINE_VFS_READ_DIR_ENTRY (iso9660fs_read_dir_entry) { + l9660_fs* fs = volume->udata; + + fs->proc = proc; + fs->rctx = rctx; + + l9660_status status; + l9660_dir root_dir; + l9660_dir dir; + l9660_dirent* dirent; + + const char* relpath = path; + if (relpath[0] == '/') + relpath++; + + status = l9660_fs_open_root (&root_dir, fs); + + if (status != L9660_OK) + return -ST_NOT_FOUND; + + if (relpath[0] == '\0') { + dir = root_dir; + } else { + status = l9660_opendirat (&dir, &root_dir, relpath); + + if (status != L9660_OK) + return -ST_NOT_FOUND; + } + + status = l9660_seekdir (&dir, 0); + + if (status != L9660_OK) + return status; + + size_t ent = 0; + + for (;;) { + status = l9660_readdir (&dir, &dirent); + + if (status != L9660_OK) + return status; + + if (dirent == NULL) + break; + + if (iso9660fs_is_special_entry (dirent)) + continue; + + if (ent == entry_num) { + char namebuf[256]; + + iso9660fs_copy_visible_name (namebuf, sizeof (namebuf), dirent); + + entry->path[0] = '\0'; + + strncat (entry->path, path, PATH_MAX - 1); + + if (strcmp (path, "/") != 0) + strncat (entry->path, "/", PATH_MAX - strlen (entry->path) - 1); + + strncat (entry->path, namebuf, PATH_MAX - strlen (entry->path) - 1); + + break; + } + + ent++; + } + + return ST_OK; +} + +DEFINE_VFS_CREATE_FILE (iso9660fs_create_file) { return -ST_XDRV_WRITE_ERROR; } + +DEFINE_VFS_CREATE_DIR (iso9660fs_create_dir) { return -ST_XDRV_WRITE_ERROR; } + +DEFINE_VFS_REMOVE (iso9660fs_remove) { return -ST_REMOVE_ERROR; } diff --git a/kernel/fs/iso9660fs.h b/kernel/fs/iso9660fs.h new file mode 100644 index 0000000..69f17b6 --- /dev/null +++ b/kernel/fs/iso9660fs.h @@ -0,0 +1,32 @@ +#ifndef _KERNEL_FS_ISO9660FS_H +#define _KERNEL_FS_ISO9660FS_H + +#include +#include +#include +#include +#include +#include +#include + +struct vfs_volume; + +DEFINE_VFS_MOUNT (iso9660fs_mount); + +DEFINE_VFS_FORMAT (iso9660fs_format); + +DEFINE_VFS_DESCRIBE (iso9660fs_describe); + +DEFINE_VFS_READ_FILE (iso9660fs_read_file); + +DEFINE_VFS_WRITE_FILE (iso9660fs_write_file); + +DEFINE_VFS_READ_DIR_ENTRY (iso9660fs_read_dir_entry); + +DEFINE_VFS_CREATE_FILE (iso9660fs_create_file); + +DEFINE_VFS_CREATE_DIR (iso9660fs_create_dir); + +DEFINE_VFS_REMOVE (iso9660fs_remove); + +#endif // _KERNEL_FS_ISO9660FS_H diff --git a/kernel/fs/lib9660.c b/kernel/fs/lib9660.c new file mode 100644 index 0000000..af61dab --- /dev/null +++ b/kernel/fs/lib9660.c @@ -0,0 +1,317 @@ +/* lib9660: a simple ISO9660 reader library especially suited to embedded + * systems + * + * SPDX-License-Identifier: LicenseRef-ISC1 + * SPDX-FileCopyrightText: © 2014 Erin Shepherd + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice appears in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#ifdef L9660_HAVE_STDIO +#include +#else +#define SEEK_END L9660_SEEK_END +#define SEEK_SET L9660_SEEK_SET +#define SEEK_CUR L9660_SEEK_CUR +#endif + +#define DENT_EXISTS (1 << 0) +#define DENT_ISDIR (1 << 1) +#define DENT_ASSOCIATED (1 << 2) +#define DENT_RECORD (1 << 3) +#define DENT_PROTECTION (1 << 4) +#define DENT_MULTIEXTENT (1 << 5) + +#define PVD(vdesc) ((l9660_vdesc_primary*)(vdesc)) + +#ifdef L9660_BIG_ENDIAN +#define READ16(v) (((v).be[1]) | ((v).be[0] << 8)) +#define READ32(v) (((v).be[3]) | ((v).be[2] << 8) | ((v).be[1]) << 16 | ((v).be[0] << 24)) +#else +#define READ16(v) (((v).le[0]) | ((v).le[1] << 8)) +#define READ32(v) (((v).le[0]) | ((v).le[1] << 8) | ((v).le[2]) << 16 | ((v).le[3] << 24)) +#endif + +#ifndef L9660_SINGLEBUFFER +#define HAVEBUFFER(f) (true) +#define BUF(f) ((f)->buf) +#else +#define HAVEBUFFER(f) ((f) == last_file) +#define BUF(f) (gbuf) + +static l9660_file *last_file; +static char gbuf[2048]; + +#endif + +static char *strchrnul(const char *s, int c) +{ + while (*s && *s != c) + s++; + return (char *) s; +} + +static inline uint16_t fsectoff(l9660_file *f) +{ + return f->position % 2048; +} + +static inline uint32_t fsector(l9660_file *f) +{ + return f->position / 2048; +} + +static inline uint32_t fnextsectpos(l9660_file *f) +{ + return (f->position + 2047) & ~2047; +} + +l9660_status l9660_openfs( + l9660_fs *fs, + bool (*read_sector)(l9660_fs *fs, void *buf, uint32_t sector)) +{ + fs->read_sector = read_sector; + +#ifndef L9660_SINGLEBUFFER + l9660_vdesc_primary *pvd = PVD(&fs->pvd); +#else + last_file = NULL; + l9660_vdesc_primary *pvd = PVD(gbuf); +#endif + uint32_t idx = 0x10; + for (;;) { + // Read next sector + if (!read_sector(fs, pvd, idx)) + return L9660_EIO; + + // Validate magic + if (memcmp(pvd->hdr.magic, "CD001", 5) != 0) + return L9660_EBADFS; + + if (pvd->hdr.type == 1) + break; // Found PVD + else if(pvd->hdr.type == 255) + return L9660_EBADFS; + } + +#ifdef L9660_SINGLEBUFFER + memcpy(&fs->root_dir_ent, &pvd->root_dir_ent, pvd->root_dir_ent.length); +#endif + + return L9660_OK; +} + +l9660_status l9660_fs_open_root(l9660_dir *dir, l9660_fs *fs) +{ + l9660_file *f = &dir->file; +#ifndef L9660_SINGLEBUFFER + l9660_dirent *dirent = &PVD(&fs->pvd)->rde.root_dir_ent; +#else + l9660_dirent *dirent = &fs->root_dir_ent; +#endif + + f->fs = fs; + f->first_sector = READ32(dirent->sector); + f->length = READ32(dirent->size); + f->position = 0; + + return L9660_OK; +} + +static l9660_status buffer(l9660_file *f) +{ +#ifdef L9660_SINGLEBUFFER + last_file = f; +#endif + if (!f->fs->read_sector(f->fs, BUF(f), f->first_sector + f->position / 2048)) + return L9660_EIO; + else + return L9660_OK; +} + +static l9660_status prebuffer(l9660_file *f) +{ + if (!HAVEBUFFER(f) || (f->position % 2048) == 0) + return buffer(f); + else return L9660_OK; +} + +static l9660_status openat_raw(l9660_file *child, l9660_dir *parent, const char *name, bool isdir) +{ + l9660_status rv; + l9660_dirent *dent = NULL; + if ((rv = l9660_seekdir(parent, 0))) return rv; + + do { + const char *seg = name; + name = strchrnul(name, '/'); + size_t seglen = name - seg; + + /* ISO9660 stores '.' as '\0' */ + if (seglen == 1 && *seg == '.') + seg = "\0"; + + /* ISO9660 stores ".." as '\1' */ + if (seglen == 2 && seg[0] == '.' && seg[1] == '.') { + seg = "\1"; + seglen = 1; + } + + for(;;) { + if ((rv = l9660_readdir(parent, &dent))) + return rv; + + /* EOD */ + if (!dent) + return L9660_ENOENT; + + /* wrong length */ + if (seglen > dent->name_len) + continue; + + /* check name */ + if (memcmp(seg, dent->name, seglen) != 0) + continue; + + /* check for a revision tag */ + if (dent->name_len > seglen && dent->name[seglen] != ';') + continue; + + /* all tests pass */ + break; + } + + child->fs = parent->file.fs; + child->first_sector = READ32(dent->sector) + dent->xattr_length; + child->length = READ32(dent->size); + child->position = 0; + + if (*name && (dent->flags & DENT_ISDIR) == 0) + return L9660_ENOTDIR; + + parent = (l9660_dir*) child; + + if (*name == '/') + name++; + } while(*name); + + if (isdir) { + if ((dent->flags & DENT_ISDIR) == 0) + return L9660_ENOTDIR; + } else { + if ((dent->flags & DENT_ISDIR) != 0) + return L9660_ENOTFILE; + } + + return L9660_OK; +} + +l9660_status l9660_opendirat(l9660_dir *dir, l9660_dir *parent, const char *path) +{ + return openat_raw(&dir->file, parent, path, true); +} + +static inline unsigned aligneven(unsigned v) { + return v + (v & 1); +} + +l9660_status l9660_readdir(l9660_dir *dir, l9660_dirent **pdirent) +{ + l9660_status rv; + l9660_file *f = &dir->file; + +rebuffer: + if(f->position >= f->length) { + *pdirent = NULL; + return L9660_OK; + } + + if ((rv = prebuffer(f))) + return rv; + + char *off = BUF(f) + fsectoff(f); + if (*off == 0) { + // Padded end of sector + f->position = fnextsectpos(f); + goto rebuffer; + } + + l9660_dirent *dirent = (l9660_dirent*) off; + f->position += aligneven(dirent->length); + + *pdirent = dirent; + return L9660_OK; +} + +l9660_status l9660_openat(l9660_file *child, l9660_dir *parent, const char * name) +{ + return openat_raw(child, parent, name, false); +} + +/*! Seek the file to \p offset from \p whence */ +l9660_status l9660_seek(l9660_file *f, int whence, int32_t offset) +{ + l9660_status rv; + uint32_t cursect = fsector(f); + + switch (whence) { + case SEEK_SET: + f->position = offset; + break; + + case SEEK_CUR: + f->position = f->position + offset; + break; + + case SEEK_END: + f->position = f->length - offset; + break; + } + + if (fsector(f) != cursect && fsectoff(f) != 0) { + if ((rv = buffer(f))) + return rv; + } + + return L9660_OK; +} + +uint32_t l9660_tell(l9660_file *f) +{ + return f->position; +} + +l9660_status l9660_read(l9660_file *f, void* buf, size_t size, size_t *read) +{ + l9660_status rv; + + if ((rv = prebuffer(f))) + return rv; + + uint16_t rem = 2048 - fsectoff(f); + if (rem > f->length - f->position) + rem = f->length - f->position; + if (rem < size) + size = rem; + + memcpy(buf, BUF(f) + fsectoff(f), size); + + *read = size; + f->position += size; + + return L9660_OK; +} diff --git a/kernel/fs/lib9660.h b/kernel/fs/lib9660.h new file mode 100644 index 0000000..629bbd8 --- /dev/null +++ b/kernel/fs/lib9660.h @@ -0,0 +1,210 @@ +/* lib9660: a simple ISO9660 reader library especially suited to embedded + * systems + * + * SPDX-License-Identifier: LicenseRef-ISC1 + * SPDX-FileCopyrightText: © 2014 Erin Shepherd + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice appears in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef LIB9660_H +#define LIB9660_H +#include +#include +#include + +struct proc; +struct reschedule_ctx; + +#ifdef L9660_HAVE_STDIO +#include +#define L9660_SEEK_END SEEK_END +#define L9660_SEEK_SET SEEK_SET +#define L9660_SEEK_CUR SEEK_CUR +#else +#define L9660_SEEK_END -1 +#define L9660_SEEK_SET 0 +#define L9660_SEEK_CUR +1 +#endif + +/* Our error return format */ +typedef enum { + /*! Success! */ + L9660_OK = 0, + /*! read_sector callback returned false */ + L9660_EIO, + /*! file system is bad */ + L9660_EBADFS, + /*! specified name does not exist */ + L9660_ENOENT, + /*! attempted to open a non-file (e.g. a directory) as a file */ + L9660_ENOTFILE, + /*! attempted to open a non-directory (e.g. a file) as a directory + * may be returned by l9660_openat if e.g. you pass path "a/b" and + * "a" is a file + */ + L9660_ENOTDIR, +} l9660_status; + +/* ISO9660 uses big/little/dual endian integers */ +typedef struct { uint8_t le[2]; } l9660_luint16; +typedef struct { uint8_t be[2]; } l9660_buint16; +typedef struct { uint8_t le[2], be[2]; } l9660_duint16; +typedef struct { uint8_t le[4]; } l9660_luint32; +typedef struct { uint8_t be[4]; } l9660_buint32; +typedef struct { uint8_t le[4], be[4]; } l9660_duint32; + +/* Descriptor time format */ +typedef struct { + char d[17]; +} l9660_desctime; + +/* File time format */ +typedef struct { + char d[7]; +} l9660_filetime; + +/* Directory entry */ +typedef struct { + uint8_t length; + uint8_t xattr_length; + l9660_duint32 sector; + l9660_duint32 size; + l9660_filetime time; + uint8_t flags; + uint8_t unit_size; + uint8_t gap_size; + l9660_duint16 vol_seq_number; + uint8_t name_len; + char name[/*name_len*/]; +} l9660_dirent; + +/* Volume descriptor header */ +typedef struct { + uint8_t type; + char magic[5]; + uint8_t version; +} l9660_vdesc_header; + +/* Primary volume descriptor */ +typedef struct { + l9660_vdesc_header hdr; + char pad0[1]; + char system_id[32]; + char volume_id[32]; + char pad1[8]; + l9660_duint32 volume_space_size; + char pad2[32]; + l9660_duint16 volume_set_size; + l9660_duint16 volume_seq_number; + l9660_duint16 logical_block_size; + l9660_duint32 path_table_size; + l9660_luint32 path_table_le; + l9660_luint32 path_table_opt_le; + l9660_buint32 path_table_be; + l9660_buint32 path_table_opt_be; + union { + l9660_dirent root_dir_ent; + char pad3[34]; + } rde; + char volume_set_id[128]; + char data_preparer_id[128]; + char app_id[128]; + char copyright_file[38]; + char abstract_file[36]; + char bibliography_file[37]; + l9660_desctime volume_created, + volume_modified, + volume_expires, + volume_effective; + uint8_t file_structure_version; + char pad4[1]; + char app_reserved[512]; + char reserved[653]; +} l9660_vdesc_primary; + +/* A generic volume descriptor (i.e. 2048 bytes) */ +typedef union { + l9660_vdesc_header hdr; + char _bits[2048]; +} l9660_vdesc; + +/* File system structure. + * Stick this inside your own structure and cast/offset as appropriate to store + * private data + */ +typedef struct l9660_fs { + void* udata; + struct proc* proc; + struct reschedule_ctx* rctx; +#ifdef L9660_SINGLEBUFFER + union { + l9660_dirent root_dir_ent; + char root_dir_pad[34]; + }; +#else + /* Sector buffer to hold the PVD */ + l9660_vdesc pvd; +#endif + + /* read_sector func */ + bool (*read_sector)(struct l9660_fs *fs, void *buf, uint32_t sector); +} l9660_fs; + +typedef struct { +#ifndef L9660_SINGLEBUFFER + /* single sector buffer */ + char buf[2048]; +#endif + l9660_fs *fs; + uint32_t first_sector; + uint32_t position; + uint32_t length; +} l9660_file; + +typedef struct { + /* directories are mostly just files with special accessors, but we like type safetey */ + l9660_file file; +} l9660_dir; + +/* Open a file system, initialising *fs. */ +l9660_status l9660_openfs( + l9660_fs *fs, + bool (*read_sector)(l9660_fs *fs, void *buf, uint32_t sector)); + +/*void l9660_closefs(l9660_fs *fs); (nop) */ + +/*! Open the root directory */ +l9660_status l9660_fs_open_root(l9660_dir *dir, l9660_fs *fs); + +/*! Open the subdirectory given by \p path */ +l9660_status l9660_opendirat(l9660_dir *dir, l9660_dir *parent, const char *path); +/*! Returns the next directory entry. If end-of-directory is reached, *dirent is + * set to NULL. */ +l9660_status l9660_readdir(l9660_dir *dir, l9660_dirent **dirent); + +#define l9660_seekdir(dir, pos) (l9660_seek(&(dir)->file, L9660_SEEK_SET, (pos))) +#define l9660_telldir(dir) (l9660_tell(&(dir)->file)) + +/*! Open the file given by \p path in \p parent */ +l9660_status l9660_openat(l9660_file *file, l9660_dir *parent, const char *path); + +/*! Read \p size bytes into \p buf. The number of bytes read will be returned in + * \p *read. May be less than \p size (but only 0 on EOF) + */ +l9660_status l9660_read(l9660_file *file, void* buf, size_t size, size_t *read); +/*! Seek the file to \p offset from \p whence */ +l9660_status l9660_seek(l9660_file *file, int whence, int32_t offset); +/*! Return the current position (suitable for passing to l9660_seek(file, SEEK_SET, ...)) */ +uint32_t l9660_tell(l9660_file *file); + +#endif diff --git a/kernel/fs/src.mk b/kernel/fs/src.mk index 1e832a9..48a3d6a 100644 --- a/kernel/fs/src.mk +++ b/kernel/fs/src.mk @@ -2,10 +2,14 @@ c += fs/vfs.c \ fs/tarfs.c \ fs/path.c \ fs/fat1.c \ - fs/fatfs.c + fs/fatfs.c \ + fs/lib9660.c \ + fs/iso9660fs.c o += fs/vfs.o \ fs/tarfs.o \ fs/path.o \ fs/fat1.o \ - fs/fatfs.o + fs/fatfs.o \ + fs/lib9660.o \ + fs/iso9660fs.o diff --git a/kernel/fs/vfs.c b/kernel/fs/vfs.c index a0b9015..9e9978b 100644 --- a/kernel/fs/vfs.c +++ b/kernel/fs/vfs.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -92,6 +93,17 @@ int vfs_create_volume (struct proc* proc, struct reschedule_ctx* rctx, const cha volume->driver_ops.create_dir = &fatfs_create_dir; volume->driver_ops.remove = &fatfs_remove; break; + case FS_ISO9660: + volume->driver_ops.mount = &iso9660fs_mount; + volume->driver_ops.format = &iso9660fs_format; + volume->driver_ops.describe = &iso9660fs_describe; + volume->driver_ops.read_file = &iso9660fs_read_file; + volume->driver_ops.write_file = &iso9660fs_write_file; + volume->driver_ops.read_dir_entry = &iso9660fs_read_dir_entry; + volume->driver_ops.create_file = &iso9660fs_create_file; + volume->driver_ops.create_dir = &iso9660fs_create_dir; + volume->driver_ops.remove = &iso9660fs_remove; + break; default: free (volume); return -ST_MOUNT_ERROR;