Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / fs / ext2 / ext2_fs.c
diff --git a/qemu/roms/openbios/fs/ext2/ext2_fs.c b/qemu/roms/openbios/fs/ext2/ext2_fs.c
new file mode 100644 (file)
index 0000000..66eb0b4
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+ *     /packages/ext2-files 
+ *
+ * (c) 2008-2009 Laurent Vivier <Laurent@lvivier.info>
+ * (c) 2010 Mark Cave-Ayland <mark.cave-ayland@siriusit.co.uk>
+ *
+ * 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 );
+}