Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / fs / hfsplus / hfsp_fs.c
diff --git a/qemu/roms/openbios/fs/hfsplus/hfsp_fs.c b/qemu/roms/openbios/fs/hfsplus/hfsp_fs.c
new file mode 100644 (file)
index 0000000..df234bb
--- /dev/null
@@ -0,0 +1,632 @@
+/*
+ *   Creation Date: <2001/05/05 23:33:49 samuel>
+ *   Time-stamp: <2004/01/12 10:25:39 samuel>
+ *
+ *     /package/hfsplus-files
+ *
+ *     HFS+ file system interface (and ROM lookup support)
+ *
+ *   Copyright (C) 2001, 2002, 2003, 2004 Samuel Rydh (samuel@ibrium.se)
+ *   Copyright (C) 2010 Mark Cave-Ayland (mark.cave-ayland@siriusit.co.uk)
+ *
+ *   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 "libhfsp.h"
+#include "volume.h"
+#include "record.h"
+#include "unicode.h"
+#include "blockiter.h"
+#include "libc/diskio.h"
+#include "libc/vsprintf.h"
+
+#define MAC_OS_ROM_CREATOR     0x63687270      /* 'chrp' */
+#define MAC_OS_ROM_TYPE                0x74627869      /* 'tbxi' */
+#define MAC_OS_ROM_NAME                "Mac OS ROM"
+
+#define FINDER_TYPE            0x464E4452      /* 'FNDR' */
+#define FINDER_CREATOR         0x4D414353      /* 'MACS' */
+#define SYSTEM_TYPE            0x7A737973      /* 'zsys' */
+#define SYSTEM_CREATOR         0x4D414353      /* 'MACS' */
+
+#define VOLNAME_SIZE   64
+
+extern void     hfsp_init( void );
+
+typedef struct {
+       record          rec;
+       char            *path;
+       off_t           pos;
+} hfsp_file_t;
+
+typedef struct {
+       volume *vol;
+       hfsp_file_t *hfspfile;
+} hfsp_info_t;
+
+DECLARE_NODE( hfsp, 0, sizeof(hfsp_info_t), "+/packages/hfsplus-files" );
+
+
+/************************************************************************/
+/*     Search implementation                                           */
+/************************************************************************/
+
+typedef int (*match_proc_t)( record *r, record *parent, const void *match_data, hfsp_file_t *pt );
+
+static int
+search_files( record *par, int recursive, match_proc_t proc, const void *match_data, hfsp_file_t *pt )
+{
+       hfsp_file_t t;
+       record r;
+       int ret = 1;
+
+       t.path = NULL;
+
+       record_init_parent( &r, par );
+       do{
+               if( r.record.type == HFSP_FOLDER || r.record.type == HFSP_FILE )
+                       ret = (*proc)( &r, par, match_data, &t );
+
+               if( ret && r.record.type == HFSP_FOLDER && recursive )
+                       ret = search_files( &r, 1, proc, match_data, &t );
+
+       } while( ret && !record_next(&r) );
+
+       if( !ret && pt ) {
+                char name[256];
+                const char *s2 = t.path ? t.path : "";
+
+               unicode_uni2asc( name, &r.key.name, sizeof(name));
+
+               pt->rec = t.rec;
+               pt->path = malloc( strlen(name) + strlen(s2) + 2 );
+               strcpy( pt->path, name );
+               if( strlen(s2) ) {
+                       strcat( pt->path, "\\" );
+                       strcat( pt->path, s2 );
+               }
+       }
+
+       if( t.path )
+               free( t.path );
+
+       return ret;
+}
+
+static int
+root_search_files( volume *vol, int recursive, match_proc_t proc, const void *match_data, hfsp_file_t *pt )
+{
+       record r;
+
+       record_init_root( &r, &vol->catalog );
+       return search_files( &r, recursive, proc, match_data, pt );
+}
+
+static int
+match_file( record *r, record *parent, const void *match_data, hfsp_file_t *pt )
+{
+        const char *p = (const char*)match_data;
+       char name[256];
+       int ret=1;
+
+       if( r->record.type != HFSP_FILE )
+               return 1;
+
+       (void) unicode_uni2asc(name, &r->key.name, sizeof(name));
+       if( !(ret=strcasecmp(p, name)) && pt )
+               pt->rec = *r;
+
+       return ret;
+}
+
+static int
+match_rom( record *r, record *par, const void *match_data, hfsp_file_t *pt )
+{
+       hfsp_cat_file *file = &r->record.u.file;
+       FInfo *fi = &file->user_info;
+       int ret = 1;
+       char buf[256];
+
+       if( r->record.type == HFSP_FILE && fi->fdCreator == MAC_OS_ROM_CREATOR && fi->fdType == MAC_OS_ROM_TYPE ) {
+               ret = search_files( par, 0, match_file, "System", NULL )
+                       || search_files( par, 0, match_file, "Finder", NULL );
+
+               (void) unicode_uni2asc(buf, &r->key.name, sizeof(buf));
+               if( !strcasecmp("BootX", buf) )
+                       return 1;
+
+               if( !ret && pt )
+                       pt->rec = *r;
+       }
+       return ret;
+}
+
+static int
+match_path( record *r, record *par, const void *match_data, hfsp_file_t *pt )
+{
+       char name[256], *s, *next, *org;
+       int ret=1;
+
+       next = org = strdup( (char*)match_data );
+       while( (s=strsep( &next, "\\/" )) && !strlen(s) )
+               ;
+       if( !s ) {
+               free( org );
+               return 1;
+       }
+
+       if( *s == ':' && strlen(s) == 5 ) {
+               if( r->record.type == HFSP_FILE && !next ) {
+                       /* match type */
+                       hfsp_cat_file *file = &r->record.u.file;
+                       FInfo *fi = &file->user_info;
+                       int i, type=0;
+                       for( i=1; s[i] && i<=4; i++ )
+                               type = (type << 8) | s[i];
+                       /* printk("fi->fdType: %s / %s\n", s+1, b ); */
+                       if( fi->fdType == type ) {
+                               if( pt )
+                                       pt->rec = *r;
+                               ret = 0;
+                       }
+               }
+       } else {
+               (void) unicode_uni2asc(name, &r->key.name, sizeof(name));
+
+               if( !strcasecmp(s, name) ) {
+                       if( r->record.type == HFSP_FILE && !next ) {
+                               if( pt )
+                                       pt->rec = *r;
+                               ret = 0;
+                       } else /* must be a directory */
+                               ret = search_files( r, 0, match_path, next, pt );
+               }
+       }
+       free( org );
+       return ret;
+}
+
+
+/************************************************************************/
+/*     Standard package methods                                                */
+/************************************************************************/
+
+/* ( -- success? ) */
+static void
+hfsp_files_open( hfsp_info_t *mi )
+{
+       int fd;
+       char *path = my_args_copy();
+
+       if ( ! path )
+               RET( 0 );
+
+       fd = open_ih( my_parent() );
+       if ( fd == -1 ) {
+               free( path );
+               RET( 0 );
+       }
+
+       mi->vol = malloc( sizeof(volume) );
+       if (volume_open(mi->vol, fd)) {
+               free( path );
+               close_io( fd );
+               RET( 0 );
+       }
+
+       mi->hfspfile = malloc( sizeof(hfsp_file_t) );
+       
+       /* Leading \\ means system folder. The finder info block has
+        * the following meaning.
+        *
+        *  [0] Prefered boot directory ID
+        *  [3] MacOS 9 boot directory ID
+        *  [5] MacOS X boot directory ID
+        */
+       if( !strncmp(path, "\\\\", 2) ) {
+               int *p = (int*)&(mi->vol)->vol.finder_info[0];
+               int cnid = p[0];
+               /* printk(" p[0] = %x, p[3] = %x, p[5] = %x\n", p[0], p[3], p[5] ); */
+               if( p[0] == p[5] && p[3] )
+                       cnid = p[3];
+               if( record_init_cnid(&(mi->hfspfile->rec), &(mi->vol)->catalog, cnid) )
+                       RET ( 0 );
+               path += 2;
+       } else {
+               record_init_root( &(mi->hfspfile->rec), &(mi->vol)->catalog );
+       }
+
+       if( !search_files(&(mi->hfspfile->rec), 0, match_path, path, mi->hfspfile ) )
+               RET ( -1 );
+       
+       RET ( -1 );
+}
+
+/* ( -- ) */
+static void
+hfsp_files_close( hfsp_info_t *mi )
+{
+       volume_close(mi->vol);
+
+       if( mi->hfspfile->path )
+               free( mi->hfspfile->path );
+       free( mi->hfspfile );
+}
+
+/* ( buf len -- actlen ) */
+static void
+hfsp_files_read( hfsp_info_t *mi )
+{
+       int count = POP();
+       char *buf = (char *)cell2pointer(POP());
+
+       hfsp_file_t *t = mi->hfspfile;
+       volume *vol = t->rec.tree->vol;
+       UInt32 blksize = vol->blksize;
+       hfsp_cat_file *file = &t->rec.record.u.file;
+       blockiter iter;
+       char buf2[blksize];
+       int act_count, curpos=0;
+
+       blockiter_init( &iter, vol, &file->data_fork, HFSP_EXTENT_DATA, file->id );
+       while( curpos + blksize < t->pos ) {
+               if( blockiter_next( &iter ) ) {
+                       RET ( -1 );
+                       return;
+               }
+               curpos += blksize;
+       }
+       act_count = 0;
+
+       while( act_count < count ){
+               UInt32 block = blockiter_curr(&iter);
+               int max = blksize, add = 0, size;
+
+               if( volume_readinbuf( vol, buf2, block ) )
+                       break;
+
+               if( curpos < t->pos ){
+                       add += t->pos - curpos;
+                       max -= t->pos - curpos;
+               }
+               size = (count-act_count > max)? max : count-act_count;
+               memcpy( (char *)buf + act_count, &buf2[add], size );
+
+               curpos += blksize;
+               act_count += size;
+
+               if( blockiter_next( &iter ) )
+                       break;
+       }
+
+       t->pos += act_count;
+
+       RET ( act_count );
+}
+
+/* ( pos.d -- status ) */
+static void
+hfsp_files_seek( hfsp_info_t *mi )
+{
+       long long pos = DPOP();
+       int offs = (int)pos;
+       int whence = SEEK_SET;
+
+       hfsp_file_t *t = mi->hfspfile;
+       hfsp_cat_file *file = &t->rec.record.u.file;
+       int total = file->data_fork.total_size;
+
+       if( offs == -1 ) {
+               offs = 0;
+               whence = SEEK_END;
+       }
+
+       switch( whence ){
+       case SEEK_END:
+               t->pos = total + offs;
+               break;
+       default:
+       case SEEK_SET:
+               t->pos = offs;
+               break;
+       }
+
+       if( t->pos < 0 )
+               t->pos = 0;
+
+       if( t->pos > total )
+               t->pos = total;
+
+       RET ( 0 );
+}
+
+/* ( addr -- size ) */
+static void
+hfsp_files_load( hfsp_info_t *mi )
+{
+       char *buf = (char *)cell2pointer(POP());
+
+       hfsp_file_t *t = mi->hfspfile;
+       volume *vol = t->rec.tree->vol;
+       UInt32 blksize = vol->blksize;
+       hfsp_cat_file *file = &t->rec.record.u.file;
+       int total = file->data_fork.total_size;
+       blockiter iter;
+       char buf2[blksize];
+       int act_count;
+
+       blockiter_init( &iter, vol, &file->data_fork, HFSP_EXTENT_DATA, file->id );
+
+       act_count = 0;
+
+       while( act_count < total ){
+               UInt32 block = blockiter_curr(&iter);
+               int max = blksize, size;
+
+               if( volume_readinbuf( vol, buf2, block ) )
+                       break;
+
+               size = (total-act_count > max)? max : total-act_count;
+               memcpy( (char *)buf + act_count, &buf2, size );
+
+               act_count += size;
+
+               if( blockiter_next( &iter ) )
+                       break;
+       }
+
+       RET ( act_count );
+}
+
+/* ( -- cstr ) */
+static void
+hfsp_files_get_fstype( hfsp_info_t *mi )
+{
+       PUSH( pointer2cell(strdup("HFS+")) );
+}
+
+/* ( -- cstr ) */
+static void
+hfsp_files_get_path( hfsp_info_t *mi )
+{
+       char *buf;
+       hfsp_file_t *t = mi->hfspfile;
+
+       if( !t->path )
+               RET ( 0 );
+
+       buf = malloc(strlen(t->path) + 1);
+       strncpy( buf, t->path, strlen(t->path) );
+       buf[strlen(t->path)] = 0;
+
+       PUSH(pointer2cell(buf));
+}
+
+/* ( -- success? ) */
+static void
+hfsp_files_open_nwrom( hfsp_info_t *mi )
+{
+       /* Switch to an existing ROM image file on the fs! */
+       if( !root_search_files(mi->vol, 1, match_rom, NULL, mi->hfspfile) )
+               RET ( -1 );
+
+       RET ( 0 );
+}
+
+/* ( -- cstr|0 ) */
+static void
+hfsp_files_volume_name( hfsp_info_t *mi )
+{
+       int fd;
+       char *volname = malloc(VOLNAME_SIZE);
+
+       fd = open_ih(my_self());
+        if (fd >= 0) {
+                get_hfs_vol_name(fd, volname, VOLNAME_SIZE);
+                close_io(fd);
+        } else {
+                volname[0] = '\0';
+        }
+
+       PUSH(pointer2cell(volname));
+}
+
+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(uint32_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 += 1904;
+
+       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);
+}
+
+/* static method, ( pathstr len ihandle -- ) */
+static void
+hfsp_files_dir( hfsp_info_t *dummy )
+{
+       ihandle_t ih = POP_ih();
+       char *path = pop_fstr_copy();
+       int fd, found;
+       volume *vol;
+       record rec, r, folrec;
+       char name[256], *curfol, *tmppath;
+       
+       fd = open_ih(ih);
+       if ( fd == -1 ) {
+               free( path );
+               RET( 0 );
+       }
+
+       vol = malloc( sizeof(volume) );
+       if (volume_open(vol, fd)) {
+               free( path );
+               close_io( fd );
+               RET( 0 );
+       }
+       
+       /* First move to the specified folder */
+       tmppath = strdup(path);
+       record_init_root( &rec, &vol->catalog );
+       record_init_parent( &r, &rec );
+       
+       /* Remove initial \ or / */
+       curfol = strsep(&tmppath, "\\//");
+       curfol = strsep(&tmppath, "\\//");
+       forth_printf("\n");
+       
+       while (curfol && strlen(curfol)) {          
+           found = 0;
+           do {
+               if (r.record.type == HFSP_FOLDER) {
+                   unicode_uni2asc(name, &r.key.name, sizeof(name));
+                   
+                   if (!strcmp(name, curfol)) {
+                       folrec = r;
+                       found = -1;
+                   }
+               }
+           } while ( !record_next(&r) );
+           
+           if (!found) {
+               forth_printf("Unable to locate path %s on filesystem\n", path);
+               goto done;
+           } else {
+               record_init_parent( &r, &folrec );
+           }
+           
+           curfol = strsep(&tmppath, "\\//");
+       }
+       
+       /* Output the directory contents */
+       found = 0;
+       do {
+           unicode_uni2asc(name, &r.key.name, sizeof(name));
+           
+           if (r.record.type == HFSP_FILE) {
+               /* Grab the file entry */
+               hfsp_cat_file *file = &r.record.u.file;
+               forth_printf("% 10lld ", file->data_fork.total_size);
+               print_date(file->create_date);
+               forth_printf(" %s\n", name);
+               found = -1;
+           }
+           
+           if (r.record.type == HFSP_FOLDER) {
+               /* Grab the directory entry */
+               hfsp_cat_folder *folder = &r.record.u.folder;
+               forth_printf("         0 ");
+               print_date(folder->create_date);
+               forth_printf(" %s\\\n", name);
+               found = -1;
+           }
+           
+       } while ( !record_next(&r) );
+       
+       if (!found) {
+           forth_printf("  (Empty folder)\n");
+       }
+       
+done:
+       volume_close(vol);
+       free(vol);
+       free(path);
+       if (tmppath)
+           free(tmppath);
+}
+
+/* static method, ( pos.d ih -- flag? ) */
+static void
+hfsp_files_probe( hfsp_info_t *dummy )
+{
+       ihandle_t ih = POP_ih();
+       long long offs = DPOP();
+       int fd, ret = 0;
+
+       fd = open_ih(ih);
+        if (fd >= 0) {
+                if (volume_probe(fd, offs)) {
+                        ret = -1;
+                }
+                close_io(fd);
+        } else {
+                ret = -1;
+        }
+
+       RET (ret);
+}
+
+static void
+hfsp_initializer( hfsp_info_t *dummy )
+{
+       fword("register-fs-package");
+}
+
+NODE_METHODS( hfsp ) = {
+       { "probe",      hfsp_files_probe        },
+       { "open",       hfsp_files_open         },
+       { "close",      hfsp_files_close        },
+       { "read",       hfsp_files_read         },
+       { "seek",       hfsp_files_seek         },
+       { "load",       hfsp_files_load         },
+       { "dir",        hfsp_files_dir          },
+
+       /* special */
+       { "open-nwrom",         hfsp_files_open_nwrom   },
+       { "get-path",           hfsp_files_get_path     },
+       { "get-fstype",         hfsp_files_get_fstype   },
+       { "volume-name",        hfsp_files_volume_name  },
+
+       { NULL,         hfsp_initializer        },
+};
+
+void
+hfsp_init( void )
+{
+       REGISTER_NODE( hfsp );
+}