630 lines
13 KiB
C
630 lines
13 KiB
C
/*
|
|
* 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( 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 );
|
|
}
|