| /* |
| * /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 ); |
| } |