/* * /packages/grubfs-files * * grub vfs * * Copyright (C) 2004 Stefan Reinauer * Copyright (C) 2004 Samuel Rydh * Copyright (C) 2010 Mark Cave-Ayland * * inspired by HFS code from Samuel Rydh * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation * */ #include "config.h" #include "libopenbios/bindings.h" #include "fs/fs.h" #include "filesys.h" #include "glue.h" #include "libc/diskio.h" #include "libc/vsprintf.h" extern void grubfs_init( void ); /************************************************************************/ /* grub GLOBALS (horrible... but difficult to fix) */ /************************************************************************/ /* the grub drivers want these: */ int filepos; int filemax; grub_error_t errnum; char FSYS_BUF[FSYS_BUFLEN]; /* these are not even used by us, instead * the grub fs drivers want them: */ int fsmax; void (*disk_read_hook) (int, int, int); void (*disk_read_func) (int, int, int); /************************************************************************/ /* filsystem table */ /************************************************************************/ typedef struct fsys_entry { const char *name; int (*mount_func) (void); int (*read_func) (char *buf, int len); int (*dir_func) (char *dirname); void (*close_func) (void); int (*embed_func) (int *start_sector, int needed_sectors); } fsys_entry_t; static const struct fsys_entry fsys_table[] = { # ifdef CONFIG_FSYS_FAT {"fat", fat_mount, fat_read, fat_dir, NULL, NULL}, # endif # ifdef CONFIG_FSYS_EXT2FS {"ext2fs", ext2fs_mount, ext2fs_read, ext2fs_dir, NULL, NULL}, # endif # ifdef CONFIG_FSYS_MINIX {"minix", minix_mount, minix_read, minix_dir, NULL, NULL}, # endif # ifdef CONFIG_FSYS_REISERFS {"reiserfs", reiserfs_mount, reiserfs_read, reiserfs_dir, NULL, reiserfs_embed}, # endif # ifdef CONFIG_FSYS_JFS {"jfs", jfs_mount, jfs_read, jfs_dir, NULL, jfs_embed}, # endif # ifdef CONFIG_FSYS_XFS {"xfs", xfs_mount, xfs_read, xfs_dir, NULL, NULL}, # endif # ifdef CONFIG_FSYS_UFS {"ufs", ufs_mount, ufs_read, ufs_dir, NULL, ufs_embed}, # endif # ifdef CONFIG_FSYS_ISO9660 {"iso9660", iso9660_mount, iso9660_read, iso9660_dir, NULL, NULL}, # endif # ifdef CONFIG_FSYS_NTFS {"ntfs", ntfs_mount, ntfs_read, ntfs_dir, NULL, NULL}, # endif # ifdef CONFIG_FSYS_AFFS {"affs", affs_mount, affs_read, affs_dir, NULL, NULL}, # endif }; /* We don't provide a file search mechanism (yet) */ typedef struct { unsigned long pos; unsigned long len; const char *path; } grubfile_t; typedef struct { const struct fsys_entry *fsys; grubfile_t *fd; int dev_fd; long long offset; /* Offset added onto each device read; should only ever be non-zero when probing a partition for a filesystem */ } grubfs_t; typedef struct { grubfs_t *gfs; } grubfs_info_t; /* Static block and global pointer required for I/O glue */ static grubfs_t dummy_fs; static grubfs_t *curfs = &dummy_fs; DECLARE_NODE( grubfs, 0, sizeof(grubfs_info_t), "+/packages/grubfs-files" ); /************************************************************************/ /* I/O glue (called by grub source) */ /************************************************************************/ int devread( unsigned long sector, unsigned long byte_offset, unsigned long byte_len, void *buf ) { long long offs = (long long)sector * 512 + byte_offset; #ifdef CONFIG_DEBUG_FS //printk("devread s=%x buf=%x, fd=%x\n",sector, buf, curfs->dev_fd); #endif if( !curfs ) { #ifdef CONFIG_DEBUG_FS printk("devread: fsys == NULL!\n"); #endif return -1; } if( seek_io(curfs->dev_fd, offs + curfs->offset) ) { #ifdef CONFIG_DEBUG_FS printk("seek failure\n"); #endif return -1; } return (read_io(curfs->dev_fd, buf, byte_len) == byte_len) ? 1:0; } int file_read( void *buf, unsigned long len ) { if (filepos < 0 || filepos > filemax) filepos = filemax; if (len > filemax-filepos) len = filemax - filepos; errnum = 0; return curfs->fsys->read_func( buf, len ); } /************************************************************************/ /* Standard package methods */ /************************************************************************/ /* ( -- success? ) */ static void grubfs_files_open( grubfs_info_t *mi ) { int fd, i; char *path = my_args_copy(); char *s; fd = open_ih( my_parent() ); if ( fd == -1 ) { free( path ); RET( 0 ); } mi->gfs = &dummy_fs; for (i = 0; i < sizeof(fsys_table)/sizeof(fsys_table[0]); i++) { #ifdef CONFIG_DEBUG_FS printk("Trying %s\n", fsys_table[i].name); #endif if (fsys_table[i].mount_func()) { const fsys_entry_t *fsys = &fsys_table[i]; #ifdef CONFIG_DEBUG_FS printk("Mounted %s\n", fsys->name); #endif mi->gfs = malloc(sizeof(grubfs_t)); mi->gfs->fsys = fsys; mi->gfs->dev_fd = fd; mi->gfs->offset = 0; s = path; while (*s) { if(*s=='\\') *s='/'; s++; } #ifdef CONFIG_DEBUG_FS printk("Path=%s\n",path); #endif if (!mi->gfs->fsys->dir_func((char *) path)) { forth_printf("File not found\n"); RET( 0 ); } mi->gfs->fd = malloc(sizeof(grubfile_t)); mi->gfs->fd->pos = filepos; mi->gfs->fd->len = filemax; mi->gfs->fd->path = strdup(path); RET( -1 ); } } #ifdef CONFIG_DEBUG_FS printk("Unknown filesystem type\n"); #endif RET( 0 ); } /* ( -- ) */ static void grubfs_files_close( grubfs_info_t *mi ) { grubfile_t *gf = mi->gfs->fd; if (gf->path) free((void *)(gf->path)); free(gf); filepos = 0; filemax = 0; } /* ( buf len -- actlen ) */ static void grubfs_files_read( grubfs_info_t *mi ) { int count = POP(); char *buf = (char *)cell2pointer(POP()); grubfile_t *file = mi->gfs->fd; int ret; filepos = file->pos; filemax = file->len; if (count > filemax - filepos) count = filemax - filepos; ret = mi->gfs->fsys->read_func(buf, count); file->pos = filepos; RET( ret ); } /* ( pos.d -- status ) */ static void grubfs_files_seek( grubfs_info_t *mi ) { long long pos = DPOP(); int offs = (int)pos; int whence = SEEK_SET; grubfile_t *file = mi->gfs->fd; unsigned long newpos; switch( whence ) { case SEEK_END: if (offs < 0 && (unsigned long) -offs > file->len) newpos = 0; else newpos = file->len + offs; break; default: case SEEK_SET: newpos = (offs < 0) ? 0 : offs; break; } if (newpos > file->len) newpos = file->len; file->pos = newpos; if (newpos) RET( -1 ); else RET( 0 ); } /* ( addr -- size ) */ static void grubfs_files_load( grubfs_info_t *mi ) { char *buf = (char *)cell2pointer(POP()); int count, ret; grubfile_t *file = mi->gfs->fd; count = file->len; ret = mi->gfs->fsys->read_func(buf, count); file->pos = filepos; RET( ret ); } /* ( -- cstr ) */ static void grubfs_files_get_path( grubfs_info_t *mi ) { grubfile_t *file = mi->gfs->fd; const char *path = file->path; RET( pointer2cell(strdup(path)) ); } /* ( -- cstr ) */ static void grubfs_files_get_fstype( grubfs_info_t *mi ) { grubfs_t *gfs = mi->gfs; PUSH( pointer2cell(strdup(gfs->fsys->name)) ); } /* static method, ( pos.d ih -- flag? ) */ static void grubfs_files_probe( grubfs_info_t *dummy ) { ihandle_t ih = POP_ih(); long long offs = DPOP(); int i; curfs->dev_fd = open_ih(ih); if (curfs->dev_fd == -1) { RET( -1 ); } curfs->offset = offs; for (i = 0; i < sizeof(fsys_table)/sizeof(fsys_table[0]); i++) { #ifdef CONFIG_DEBUG_FS printk("Probing for %s\n", fsys_table[i].name); #endif if (fsys_table[i].mount_func()) { RET( -1 ); } } #ifdef CONFIG_DEBUG_FS printk("Unknown filesystem type\n"); #endif close_io(curfs->dev_fd); RET ( 0 ); } /* static method, ( pathstr len ihandle -- ) */ static void grubfs_files_dir( grubfs_info_t *dummy ) { forth_printf("dir method not implemented for grubfs filesystem\n"); POP(); POP(); POP(); } static void grubfs_initializer( grubfs_info_t *dummy ) { fword("register-fs-package"); } NODE_METHODS( grubfs ) = { { "probe", grubfs_files_probe }, { "open", grubfs_files_open }, { "close", grubfs_files_close }, { "read", grubfs_files_read }, { "seek", grubfs_files_seek }, { "load", grubfs_files_load }, { "dir", grubfs_files_dir }, /* special */ { "get-path", grubfs_files_get_path }, { "get-fstype", grubfs_files_get_fstype }, { NULL, grubfs_initializer }, }; void grubfs_init( void ) { REGISTER_NODE( grubfs ); }