/* * Creation Date: <2001/05/06 22:47:23 samuel> * Time-stamp: <2004/01/12 10:24:35 samuel> * * /packages/hfs-files * * HFS world interface * * Copyright (C) 2001-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 "libc/vsprintf.h" #include "libc/diskio.h" #include "libhfs.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 hfs_init( void ); typedef struct { enum { FILE, DIR } type; union { hfsdir *dir; hfsfile *file; }; } hfscommon; typedef struct { hfsvol *vol; hfscommon *common; } hfs_info_t; DECLARE_NODE( hfs, 0, sizeof(hfs_info_t), "+/packages/hfs-files" ); /************************************************************************/ /* Search Functions */ /************************************************************************/ static int _find_file( hfsvol *vol, const char *path, unsigned long type, unsigned long creator ) { hfsdirent ent; hfsdir *dir; int ret=1; if( !(dir=hfs_opendir(vol, path)) ) return 1; while( ret && !hfs_readdir(dir, &ent) ) { if( ent.flags & HFS_ISDIR ) continue; ret = !(*(unsigned long*)ent.u.file.type == type && *(unsigned long*)ent.u.file.creator == creator ); } hfs_closedir( dir ); return ret; } /* ret: 0=success, 1=not_found, 2=not_a_dir */ static int _search( hfsvol *vol, const char *path, const char *sname, hfsfile **ret_fd ) { hfsdir *dir; hfsdirent ent; int topdir=0, status = 1; char *p, buf[256]; strncpy( buf, path, sizeof(buf) ); if( buf[strlen(buf)-1] != ':' ) strncat( buf, ":", sizeof(buf) ); buf[sizeof(buf)-1] = 0; p = buf + strlen( buf ); if( !(dir=hfs_opendir(vol, path)) ) return 2; /* printk("DIRECTORY: %s\n", path ); */ while( status && !hfs_readdir(dir, &ent) ) { unsigned long type, creator; *p = 0; topdir = 0; strncat( buf, ent.name, sizeof(buf) ); if( (status=_search(vol, buf, sname, ret_fd)) != 2 ) continue; topdir = 1; /* name search? */ if( sname ) { status = strcasecmp( ent.name, sname ); continue; } type = *(unsigned long*)ent.u.file.type; creator = *(unsigned long*)ent.u.file.creator; /* look for Mac OS ROM, System and Finder in the same directory */ if( type == MAC_OS_ROM_TYPE && creator == MAC_OS_ROM_CREATOR ) { if( strcasecmp(ent.name, MAC_OS_ROM_NAME) ) continue; status = _find_file( vol, path, FINDER_TYPE, FINDER_CREATOR ) || _find_file( vol, path, SYSTEM_TYPE, SYSTEM_CREATOR ); } } if( !status && topdir && ret_fd && !(*ret_fd=hfs_open(vol, buf)) ) { printk("Unexpected error: failed to open matched ROM\n"); status = 1; } hfs_closedir( dir ); return status; } static hfsfile * _do_search( hfs_info_t *mi, const char *sname ) { hfsvol *vol = hfs_getvol( NULL ); mi->common->type = FILE; (void)_search( vol, ":", sname, &mi->common->file ); return mi->common->file; } 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); } /* static void dir_fs( file_desc_t *fd ) { hfscommon *common = (hfscommon*)fd; hfsdirent ent; if (common->type != DIR) return; forth_printf("\n"); while( !hfs_readdir(common->dir, &ent) ) { forth_printf("% 10d ", ent.u.file.dsize); print_date(ent.mddate); if( ent.flags & HFS_ISDIR ) forth_printf("%s\\\n", ent.name); else forth_printf("%s\n", ent.name); } } */ /************************************************************************/ /* Standard package methods */ /************************************************************************/ /* ( -- success? ) */ static void hfs_files_open( hfs_info_t *mi ) { int fd; char *path = my_args_copy(); const char *s; char buf[256]; fd = open_ih( my_parent() ); if ( fd == -1 ) { free( path ); RET( 0 ); } mi->vol = hfs_mount(fd, 0); if (!mi->vol) { RET( 0 ); } if( !strncmp(path, "\\\\", 2) ) { hfsvolent ent; /* \\ is an alias for the (blessed) system folder */ if( hfs_vstat(mi->vol, &ent) < 0 || hfs_setcwd(mi->vol, ent.blessed) ) { free(path); RET( -1 ); } path += 2; } else { hfs_chdir( mi->vol, ":" ); } mi->common = malloc(sizeof(hfscommon)); if (!mi->common) { free(path); RET( 0 ); } if (strcmp(path, "\\") == 0) { /* root directory is in fact ":" */ mi->common->dir = hfs_opendir(mi->vol, ":"); mi->common->type = DIR; free(path); RET( -1 ); } if (path[strlen(path) - 1] == '\\') { path[strlen(path) - 1] = 0; } for( path-- ;; ) { int n; s = ++path; path = strchr(s, '\\'); if( !path || !path[1]) break; n = MIN( sizeof(buf)-1, (path-s) ); if( !n ) continue; strncpy( buf, s, n ); buf[n] = 0; if( hfs_chdir(mi->vol, buf) ) { free(mi->common); free(path); RET( 0 ); } } /* support the ':filetype' syntax */ if( *s == ':' ) { unsigned long id, oldid = hfs_getcwd(mi->vol); hfsdirent ent; hfsdir *dir; s++; id = oldid; hfs_dirinfo( mi->vol, &id, buf ); hfs_setcwd( mi->vol, id ); if( !(dir=hfs_opendir(mi->vol, buf)) ) { free(mi->common); free(path); RET( 0 ); } hfs_setcwd( mi->vol, oldid ); while( !hfs_readdir(dir, &ent) ) { if( ent.flags & HFS_ISDIR ) continue; if( !strncmp(s, ent.u.file.type, 4) ) { mi->common->type = FILE; mi->common->file = hfs_open( mi->vol, ent.name ); break; } } hfs_closedir( dir ); free(path); RET( -1 ); } mi->common->dir = hfs_opendir(mi->vol, s); if (!mi->common->dir) { mi->common->file = hfs_open( mi->vol, s ); if (mi->common->file == NULL) { free(mi->common); free(path); RET( 0 ); } mi->common->type = FILE; free(path); RET( -1 ); } mi->common->type = DIR; free(path); RET( -1 ); } /* ( -- ) */ static void hfs_files_close( hfs_info_t *mi ) { hfscommon *common = mi->common; if (common->type == FILE) hfs_close( common->file ); else if (common->type == DIR) hfs_closedir( common->dir ); free(common); } /* ( buf len -- actlen ) */ static void hfs_files_read( hfs_info_t *mi ) { int count = POP(); char *buf = (char *)cell2pointer(POP()); hfscommon *common = mi->common; if (common->type != FILE) RET( -1 ); RET ( hfs_read( common->file, buf, count ) ); } /* ( pos.d -- status ) */ static void hfs_files_seek( hfs_info_t *mi ) { long long pos = DPOP(); int offs = (int)pos; int whence = SEEK_SET; int ret; hfscommon *common = mi->common; if (common->type != FILE) RET( -1 ); switch( whence ) { case SEEK_END: whence = HFS_SEEK_END; break; default: case SEEK_SET: whence = HFS_SEEK_SET; break; } ret = hfs_seek( common->file, offs, whence ); if (ret != -1) RET( 0 ); else RET( -1 ); } /* ( addr -- size ) */ static void hfs_files_load( hfs_info_t *mi ) { char *buf = (char *)cell2pointer(POP()); int count; hfscommon *common = mi->common; if (common->type != FILE) RET( -1 ); /* Seek to the end in order to get the file size */ hfs_seek(common->file, 0, HFS_SEEK_END); count = common->file->pos; hfs_seek(common->file, 0, HFS_SEEK_SET); RET ( hfs_read( common->file, buf, count ) ); } /* ( -- success? ) */ static void hfs_files_open_nwrom( hfs_info_t *mi ) { /* Switch to an existing ROM image file on the fs! */ if ( _do_search( mi, NULL ) ) RET( -1 ); RET( 0 ); } /* ( -- cstr ) */ static void hfs_files_get_path( hfs_info_t *mi ) { char buf[256], buf2[256]; hfscommon *common = mi->common; hfsvol *vol = hfs_getvol( NULL ); hfsdirent ent; int start, ns; unsigned long id; if (common->type != FILE) RET( 0 ); hfs_fstat( common->file, &ent ); start = sizeof(buf) - strlen(ent.name) - 1; if( start <= 0 ) RET ( 0 ); strcpy( buf+start, ent.name ); buf[--start] = '\\'; ns = start; for( id=ent.parid ; !hfs_dirinfo(vol, &id, buf2) ; ) { start = ns; ns -= strlen(buf2); if( ns <= 0 ) RET( 0 ); strcpy( buf+ns, buf2 ); buf[--ns] = buf[start] = '\\'; } if( strlen(buf) >= sizeof(buf) ) RET( 0 ); RET( pointer2cell(strdup(buf+start)) ); } /* ( -- cstr ) */ static void hfs_files_get_fstype( hfs_info_t *mi ) { PUSH( pointer2cell(strdup("HFS")) ); } /* ( -- cstr|0 ) */ static void hfs_files_volume_name( hfs_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 method, ( pathstr len ihandle -- ) */ static void hfs_files_dir( hfs_info_t *dummy ) { hfsvol *volume; hfscommon *common; hfsdirent ent; int i; int fd; ihandle_t ih = POP(); char *path = pop_fstr_copy(); fd = open_ih( ih ); if ( fd == -1 ) { free( path ); return; } volume = hfs_mount(fd, 0); if (!volume) { return; } common = malloc(sizeof(hfscommon)); /* HFS paths are colon separated, not backslash separated */ for (i = 0; i < strlen(path); i++) if (path[i] == '\\') path[i] = ':'; common->dir = hfs_opendir(volume, path); forth_printf("\n"); while( !hfs_readdir(common->dir, &ent) ) { forth_printf("% 10ld ", ent.u.file.dsize); print_date(ent.mddate); if( ent.flags & HFS_ISDIR ) forth_printf("%s\\\n", ent.name); else forth_printf("%s\n", ent.name); } hfs_closedir( common->dir ); hfs_umount( volume ); close_io( fd ); free( common ); free( path ); } /* static method, ( pos.d ih -- flag? ) */ static void hfs_files_probe( hfs_info_t *dummy ) { ihandle_t ih = POP_ih(); long long offs = DPOP(); int fd, ret = 0; fd = open_ih(ih); if (fd >= 0) { if (hfs_probe(fd, offs)) { ret = -1; } close_io(fd); } else { ret = -1; } RET (ret); } static void hfs_initializer( hfs_info_t *dummy ) { fword("register-fs-package"); } NODE_METHODS( hfs ) = { { "probe", hfs_files_probe }, { "open", hfs_files_open }, { "close", hfs_files_close }, { "read", hfs_files_read }, { "seek", hfs_files_seek }, { "load", hfs_files_load }, { "dir", hfs_files_dir }, /* special */ { "open-nwrom", hfs_files_open_nwrom }, { "get-path", hfs_files_get_path }, { "get-fstype", hfs_files_get_fstype }, { "volume-name", hfs_files_volume_name }, { NULL, hfs_initializer }, }; void hfs_init( void ) { REGISTER_NODE( hfs ); }