diff --git a/Makefile b/Makefile index 3599bbe..bffc286 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,8 @@ OBJS = bin/boot.o bin/kernel.o bin/idt.o bin/isr.o bin/screen.o \ bin/utils.o bin/string.o bin/stdio.o bin/disk.o bin/pic.o bin/pit.o \ bin/keyboard.o bin/paging.o bin/page_alloc.o bin/page_tables.o \ bin/fat.o bin/task.o bin/switch.o bin/sys_exit.o bin/gdt.o bin/isr-asm.o \ - bin/kheap.o bin/liballoc.o bin/exec_from_file.o bin/syscalls.o bin/atapi.o + bin/kheap.o bin/liballoc.o bin/exec_from_file.o bin/syscalls.o bin/atapi.o \ + bin/lib9660.o # Define the compiler and assembler #CC = i686-elf-gcc -D__is_katauos @@ -150,6 +151,9 @@ bin/syscalls.o: src/tasking/syscalls.c bin/atapi.o: src/drivers/atapi.c $(CC) $(CFLAGS) -c $< -o $@ +bin/lib9660.o: src/drivers/lib9660.c + $(CC) $(CFLAGS) -c $< -o $@ + clean: rm -rf bin/ rm -f $(OUTPUT) diff --git a/include/lib9660.h b/include/lib9660.h new file mode 100644 index 0000000..d05f3ea --- /dev/null +++ b/include/lib9660.h @@ -0,0 +1,204 @@ +/* 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 + +#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]; + }; + 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 { +#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/src/drivers/lib9660.c b/src/drivers/lib9660.c new file mode 100644 index 0000000..622d1df --- /dev/null +++ b/src/drivers/lib9660.c @@ -0,0 +1,337 @@ +/* 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 "lib9660.h" +#include + +#ifdef DEBUG +#include +#endif + +#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) { + if((*s++) == c) + break; + } + 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)->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; +} + +#if defined(DEBUG) +static void print_dirent(l9660_dirent *dent) +{ + if (!getenv("L9660_DEBUG")) + return; + + printf("| ---- dirent\n"); + printf("| length %d\n", dent->length); + printf("| xattr_length %d\n", dent->xattr_length); + printf("| sector %u\n", READ32(dent->sector)); + printf("| size %u\n", READ32(dent->size)); + printf("| name \"%.*s\"\n", dent->name_len, dent->name); + printf("| ---- end dirent\n"); +} +#endif + +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; + +#ifdef DEBUG + print_dirent(dent); +#endif + + /* 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; + } 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/src/kernel/kernel.c b/src/kernel/kernel.c index 3c77cca..bd961de 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -14,6 +14,7 @@ #include "../include/kheap.h" #include "../include/exec_from_file.h" #include "../include/atapi.h" +#include "../include/lib9660.h" #include @@ -24,6 +25,99 @@ void apply_pic_masks() outb(0xa1,0xff); } +static l9660_fs g_iso_fs; +static l9660_dir g_root_dir; +static l9660_file g_test_file; + +bool read_sector_callback(l9660_fs *fs, void *buf, uint32_t sector) { + // The 'fs' parameter is not needed for our simple case, so we can ignore it. + (void)fs; + + // Call your existing CD-ROM read function to read 1 sector. + // We assume the CD-ROM is the secondary master (0x170). + int result = read_cdrom(0x170, false, sector, 1, (uint16_t*)buf); + + // lib9660 expects 'true' for success and 'false' for failure. + // Your read_cdrom returns 0 for success. + return (result == 0); +} + +void test_iso9660() +{ + l9660_status status = l9660_openfs(&g_iso_fs, read_sector_callback); + + if (status != L9660_OK) { + printf("Error opening ISO9660 filesystem!\n"); + return; + } + + // 2. Open the root directory + status = l9660_fs_open_root(&g_root_dir, &g_iso_fs); + if (status != L9660_OK) { + printf("Error opening root directory!\n"); + return; + } + + printf("Files in root directory:\n"); + for (;;) { + l9660_dirent *dent; + status = l9660_readdir(&g_root_dir, &dent); + + if (dent == NULL || status != L9660_OK) { + break; // End of directory or error + } + + // Print the filename. It's not null-terminated, so use the length field. + for (int i = 0; i < dent->name_len; ++i) { + printf("%c", dent->name[i]); + } + printf("\n"); + } + + printf("\nReading content of /TEST.TXT:\n"); + + // Re-open the root directory to reset its position before searching + l9660_fs_open_root(&g_root_dir, &g_iso_fs); + + // Note: ISO9660 filenames are often uppercase. + status = l9660_openat(&g_test_file, &g_root_dir, "test.txt;1"); + + if (status != L9660_OK) { + printf("Error opening TEST.TXT;1. Error code: "); + printf("%X\n", status); // Print the error code + printf("\n"); + } else { + char file_buffer[129]; + size_t bytes_read; + uint32_t total_bytes; + + for (;;) { + // Read the next chunk of the file + status = l9660_read(&g_test_file, file_buffer, 128, &bytes_read); + + // Check for a read error + if (status != L9660_OK) { + printf("An error occurred during file read!\n"); + break; + } + + // Check for End-Of-File + if (bytes_read == 0) { + break; // We're done + } + + // Process the data we just read + total_bytes += bytes_read; + file_buffer[bytes_read] = '\0'; // Null-terminate the chunk + printf(file_buffer); // Print the chunk + } + + printf("\n--- EOF ---\n"); + printf("Total bytes read: "); + printf("0x%X\n", total_bytes); + } +} + void kmain(unsigned int magic, unsigned int info) { struct multiboot_info mbi; @@ -103,10 +197,8 @@ void kmain(unsigned int magic, unsigned int info) printf("huy1 points to: 0x%X huy2 points to:0x%X\n", huy, huy2); printf("huy1: %s\nhuy2: %s", huy, huy2); - uint8_t buffer[2048]; + test_iso9660(); - read_cdrom(0x170, false, 1230, 1, (uint16_t*)buffer); - printf("buffer:\n %s\n", buffer); //exec_from_file("/ebalo/main", "/ebalo/"); while(1)