Files
openbios/fs/hfs/hfs_fs.c

469 lines
9.0 KiB
C
Raw Normal View History

/*
* Creation Date: <2001/05/06 22:47:23 samuel>
* Time-stamp: <2004/01/12 10:24:35 samuel>
*
* <hfs_fs.c>
*
* HFS world interface
*
* Copyright (C) 2001-2004 Samuel Rydh (samuel@ibrium.se)
*
* 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 "fs/fs.h"
#include "libc/vsprintf.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' */
typedef struct {
enum { FILE, DIR } type;
union {
hfsdir *dir;
hfsfile *file;
};
} hfscommon;
/************************************************************************/
/* Search Functions */
/************************************************************************/
static int
_find_file( hfsvol *vol, const char *path, ulong type, ulong 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 = !(*(ulong*)ent.u.file.type == type && *(ulong*)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, file_desc_t **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) ) {
ulong 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 = *(ulong*)ent.u.file.type;
creator = *(ulong*)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=(file_desc_t*)hfs_open(vol, buf)) ) {
printk("Unexpected error: failed to open matched ROM\n");
status = 1;
}
hfs_closedir( dir );
return status;
}
static file_desc_t *
_do_search( fs_ops_t *fs, const char *sname )
{
hfsvol *vol = hfs_getvol( NULL );
file_desc_t *ret_fd = NULL;
(void)_search( vol, ":", sname, &ret_fd );
return ret_fd;
}
static file_desc_t *
search_rom( fs_ops_t *fs )
{
return _do_search( fs, NULL );
}
static file_desc_t *
search_file( fs_ops_t *fs, const char *sname )
{
return _do_search( fs, sname );
}
/************************************************************************/
/* file/fs ops */
/************************************************************************/
static void
file_close( file_desc_t *fd )
{
hfscommon *common = (hfscommon*)fd;
if (common->type == FILE)
hfs_close( common->file );
else if (common->type == DIR)
hfs_closedir( common->dir );
free(common);
}
static int
file_lseek( file_desc_t *fd, off_t offs, int whence )
{
hfscommon *common = (hfscommon*)fd;
if (common->type != FILE)
return -1;
switch( whence ) {
case SEEK_CUR:
whence = HFS_SEEK_CUR;
break;
case SEEK_END:
whence = HFS_SEEK_END;
break;
default:
case SEEK_SET:
whence = HFS_SEEK_SET;
break;
}
return hfs_seek( common->file, offs, whence );
}
static int
file_read( file_desc_t *fd, void *buf, size_t count )
{
hfscommon *common = (hfscommon*)fd;
if (common->type != FILE)
return -1;
return hfs_read( common->file, buf, count );
}
static char *
get_path( file_desc_t *fd, char *retbuf, int len )
{
char buf[256], buf2[256];
hfscommon *common = (hfscommon*)fd;
hfsvol *vol = hfs_getvol( NULL );
hfsdirent ent;
int start, ns;
ulong id;
if (common->type != FILE)
return NULL;
hfs_fstat( common->file, &ent );
start = sizeof(buf) - strlen(ent.name) - 1;
if( start <= 0 )
return NULL;
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 )
return NULL;
strcpy( buf+ns, buf2 );
buf[--ns] = buf[start] = '\\';
}
if( strlen(buf + start) >= len )
return NULL;
strcpy( retbuf, buf+start );
return retbuf;
}
static char *
vol_name( fs_ops_t *fs, char *buf, int size )
{
return get_hfs_vol_name( fs->fd, buf, size );
}
static file_desc_t *
open_path( fs_ops_t *fs, const char *fullpath )
{
hfsvol *vol = (hfsvol*)fs->fs_data;
const char *s;
char buf[256];
hfscommon *common;
char *path = strdup(fullpath);
if( !strncmp(path, "\\\\", 2) ) {
hfsvolent ent;
/* \\ is an alias for the (blessed) system folder */
if( hfs_vstat(vol, &ent) < 0 || hfs_setcwd(vol, ent.blessed) ) {
free(path);
return NULL;
}
path += 2;
} else {
hfs_chdir( vol, ":" );
}
common = malloc(sizeof(*common));
if (!common) {
free(path);
return NULL;
}
if (strcmp(path, "\\") == 0) {
/* root directory is in fact ":" */
common->dir = hfs_opendir(vol, ":");
common->type = DIR;
free(path);
return (file_desc_t*)common;
}
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(vol, buf) ) {
free(common);
free(path);
return NULL;
}
}
/* support the ':filetype' syntax */
if( *s == ':' ) {
unsigned long id, oldid = hfs_getcwd(vol);
file_desc_t *ret = NULL;
hfsdirent ent;
hfsdir *dir;
s++;
id = oldid;
hfs_dirinfo( vol, &id, buf );
hfs_setcwd( vol, id );
if( !(dir=hfs_opendir(vol, buf)) ) {
free(common);
free(path);
return NULL;
}
hfs_setcwd( vol, oldid );
while( !hfs_readdir(dir, &ent) ) {
if( ent.flags & HFS_ISDIR )
continue;
if( !strncmp(s, ent.u.file.type, 4) ) {
common->type = FILE;
common->file = hfs_open( vol, ent.name );
ret = (file_desc_t*)common;
break;
}
}
hfs_closedir( dir );
free(path);
return ret;
}
common->dir = hfs_opendir(vol, s);
if (!common->dir) {
common->file = hfs_open( vol, s );
if (common->file == NULL) {
free(common);
free(path);
return NULL;
}
common->type = FILE;
free(path);
return (file_desc_t*)common;
}
common->type = DIR;
free(path);
return (file_desc_t*)common;
}
static void
close_fs( fs_ops_t *fs )
{
hfsvol *vol = (hfsvol*)fs->fs_data;
hfs_umount( vol );
/* callers responsibility to call free(fs) */
}
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);
}
}
static const char *
get_fstype( fs_ops_t *fs )
{
return ("HFS");
}
static const fs_ops_t hfs_ops = {
.dir = dir_fs,
.close_fs = close_fs,
.open_path = open_path,
.search_rom = search_rom,
.search_file = search_file,
.vol_name = vol_name,
.get_path = get_path,
.close = file_close,
.read = file_read,
.lseek = file_lseek,
.get_fstype = get_fstype,
};
int
fs_hfs_open( int os_fd, fs_ops_t *fs )
{
hfsvol *vol = hfs_mount( os_fd, 0 );
if( !vol )
return -1;
*fs = hfs_ops;
fs->fs_data = vol;
return 0;
}
int
fs_hfs_probe( int fd, llong offs )
{
if (hfs_probe(fd, offs))
return -1;
return 0;
}