/* * Creation Date: <2001/05/06 22:47:23 samuel> * Time-stamp: <2004/01/12 10:24:35 samuel> * * * * 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; }