/* * /packages/ext2-files * * (c) 2008-2009 Laurent Vivier * (c) 2010 Mark Cave-Ayland * * This file has been copied from EMILE, http://emile.sf.net * */ #include "config.h" #include "libopenbios/bindings.h" #include "libext2.h" #include "ext2_utils.h" #include "fs/fs.h" #include "libc/vsprintf.h" #include "libc/diskio.h" extern void ext2_init( void ); typedef struct { enum { FILE, DIR } type; union { ext2_FILE *file; ext2_DIR *dir; }; } ext2_COMMON; typedef struct { ext2_VOLUME *volume; ext2_COMMON *common; } ext2_info_t; DECLARE_NODE( ext2, 0, sizeof(ext2_info_t), "+/packages/ext2-files" ); static const int days_month[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static const int days_month_leap[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static inline int is_leap(int year) { return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0); } static void print_date(time_t sec) { unsigned int second, minute, hour, month, day, year; int current; const int *days; second = sec % 60; sec /= 60; minute = sec % 60; sec /= 60; hour = sec % 24; sec /= 24; year = sec * 100 / 36525; sec -= year * 36525 / 100; year += 1970; days = is_leap(year) ? days_month_leap : days_month; current = 0; month = 0; while (month < 12) { if (sec <= current + days[month]) { break; } current += days[month]; month++; } month++; day = sec - current + 1; forth_printf("%d-%02d-%02d %02d:%02d:%02d ", year, month, day, hour, minute, second); } /************************************************************************/ /* Standard package methods */ /************************************************************************/ /* ( -- success? ) */ static void ext2_files_open( ext2_info_t *mi ) { int fd; char *path = my_args_copy(); fd = open_ih( my_parent() ); if ( fd == -1 ) { free( path ); RET( 0 ); } mi->volume = ext2_mount(fd); if (!mi->volume) { RET( 0 ); } mi->common = (ext2_COMMON*)malloc(sizeof(ext2_COMMON)); if (mi->common == NULL) RET( 0 ); mi->common->dir = ext2_opendir(mi->volume, path); if (mi->common->dir == NULL) { mi->common->file = ext2_open(mi->volume, path); if (mi->common->file == NULL) { free(mi->common); RET( 0 ); } mi->common->type = FILE; RET( -1 ); } mi->common->type = DIR; RET( -1 ); } /* ( -- ) */ static void ext2_files_close( ext2_info_t *mi ) { ext2_COMMON *common = mi->common; if (common->type == FILE) ext2_close(common->file); else if (common->type == DIR) ext2_closedir(common->dir); free(common); ext2_umount(mi->volume); } /* ( buf len -- actlen ) */ static void ext2_files_read( ext2_info_t *mi ) { int count = POP(); char *buf = (char *)cell2pointer(POP()); ext2_COMMON *common = mi->common; if (common->type != FILE) RET( -1 ); RET ( ext2_read( common->file, buf, count ) ); } /* ( pos.d -- status ) */ static void ext2_files_seek( ext2_info_t *mi ) { long long pos = DPOP(); int offs = (int)pos; int whence = SEEK_SET; int ret; ext2_COMMON *common = mi->common; if (common->type != FILE) RET( -1 ); ret = ext2_lseek(common->file, offs, whence); if (ret) RET( -1 ); else RET( 0 ); } /* ( addr -- size ) */ static void ext2_files_load( ext2_info_t *mi ) { char *buf = (char *)cell2pointer(POP()); int count; ext2_COMMON *common = mi->common; if (common->type != FILE) RET( -1 ); /* Seek to the end in order to get the file size */ ext2_lseek(common->file, 0, SEEK_END); count = common->file->offset; ext2_lseek(common->file, 0, SEEK_SET); RET ( ext2_read( common->file, buf, count ) ); } /* ( -- cstr ) */ static void ext2_files_get_path( ext2_info_t *mi ) { ext2_COMMON *common = mi->common; if (common->type != FILE) RET( 0 ); RET( pointer2cell(strdup(common->file->path)) ); } /* ( -- cstr ) */ static void ext2_files_get_fstype( ext2_info_t *mi ) { PUSH( pointer2cell(strdup("ext2")) ); } /* static method, ( pathstr len ihandle -- ) */ static void ext2_files_dir( ext2_info_t *dummy ) { ext2_COMMON *common; ext2_VOLUME *volume; struct ext2_dir_entry_2 *entry; struct ext2_inode inode; int fd; ihandle_t ih = POP(); char *path = pop_fstr_copy(); fd = open_ih( ih ); if ( fd == -1 ) { free( path ); return; } volume = ext2_mount(fd); if (!volume) { return; } common = (ext2_COMMON*)malloc(sizeof(ext2_COMMON)); common->dir = ext2_opendir(volume, path); forth_printf("\n"); while ( (entry = ext2_readdir(common->dir)) ) { ext2_get_inode(common->dir->volume, entry->inode, &inode); forth_printf("% 10d ", inode.i_size); print_date(inode.i_mtime); if (S_ISDIR(inode.i_mode)) forth_printf("%s\\\n", entry->name); else forth_printf("%s\n", entry->name); } ext2_closedir( common->dir ); ext2_umount( volume ); close_io( fd ); free( common ); free( path ); } /* static method, ( pos.d ih -- flag? ) */ static void ext2_files_probe( ext2_info_t *dummy ) { ihandle_t ih = POP_ih(); long long offs = DPOP(); int fd, ret = 0; fd = open_ih(ih); if (fd >= 0) { if (ext2_probe(fd, offs)) { ret = -1; } close_io(fd); } else { ret = -1; } RET (ret); } static void ext2_initializer( ext2_info_t *dummy ) { fword("register-fs-package"); } NODE_METHODS( ext2 ) = { { "probe", ext2_files_probe }, { "open", ext2_files_open }, { "close", ext2_files_close }, { "read", ext2_files_read }, { "seek", ext2_files_seek }, { "load", ext2_files_load }, { "dir", ext2_files_dir }, /* special */ { "get-path", ext2_files_get_path }, { "get-fstype", ext2_files_get_fstype }, { NULL, ext2_initializer }, }; void ext2_init( void ) { REGISTER_NODE( ext2 ); }