Fix bug in dir path parsing on HFS filesystems.
[openbios.git] / fs / hfs / hfs_fs.c
bloba0ce21e760b33046f81743f9be62bd28efb8fee2
1 /*
2 * Creation Date: <2001/05/06 22:47:23 samuel>
3 * Time-stamp: <2004/01/12 10:24:35 samuel>
5 * /packages/hfs-files
7 * HFS world interface
9 * Copyright (C) 2001-2004 Samuel Rydh (samuel@ibrium.se)
10 * Copyright (C) 2010 Mark Cave-Ayland (mark.cave-ayland@siriusit.co.uk)
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation
18 #include "config.h"
19 #include "libopenbios/bindings.h"
20 #include "fs/fs.h"
21 #include "libc/vsprintf.h"
22 #include "libc/diskio.h"
23 #include "libhfs.h"
25 #define MAC_OS_ROM_CREATOR 0x63687270 /* 'chrp' */
26 #define MAC_OS_ROM_TYPE 0x74627869 /* 'tbxi' */
27 #define MAC_OS_ROM_NAME "Mac OS ROM"
29 #define FINDER_TYPE 0x464E4452 /* 'FNDR' */
30 #define FINDER_CREATOR 0x4D414353 /* 'MACS' */
31 #define SYSTEM_TYPE 0x7A737973 /* 'zsys' */
32 #define SYSTEM_CREATOR 0x4D414353 /* 'MACS' */
34 #define VOLNAME_SIZE 64
36 extern void hfs_init( void );
38 typedef struct {
39 enum { FILE, DIR } type;
40 union {
41 hfsdir *dir;
42 hfsfile *file;
44 } hfscommon;
46 typedef struct {
47 hfsvol *vol;
48 hfscommon *common;
49 } hfs_info_t;
51 DECLARE_NODE( hfs, 0, sizeof(hfs_info_t), "+/packages/hfs-files" );
53 /************************************************************************/
54 /* Search Functions */
55 /************************************************************************/
57 static int
58 _find_file( hfsvol *vol, const char *path, ulong type, ulong creator )
60 hfsdirent ent;
61 hfsdir *dir;
62 int ret=1;
64 if( !(dir=hfs_opendir(vol, path)) )
65 return 1;
67 while( ret && !hfs_readdir(dir, &ent) ) {
68 if( ent.flags & HFS_ISDIR )
69 continue;
70 ret = !(*(ulong*)ent.u.file.type == type && *(ulong*)ent.u.file.creator == creator );
73 hfs_closedir( dir );
74 return ret;
78 /* ret: 0=success, 1=not_found, 2=not_a_dir */
79 static int
80 _search( hfsvol *vol, const char *path, const char *sname, hfsfile **ret_fd )
82 hfsdir *dir;
83 hfsdirent ent;
84 int topdir=0, status = 1;
85 char *p, buf[256];
87 strncpy( buf, path, sizeof(buf) );
88 if( buf[strlen(buf)-1] != ':' )
89 strncat( buf, ":", sizeof(buf) );
90 buf[sizeof(buf)-1] = 0;
91 p = buf + strlen( buf );
93 if( !(dir=hfs_opendir(vol, path)) )
94 return 2;
96 /* printk("DIRECTORY: %s\n", path ); */
98 while( status && !hfs_readdir(dir, &ent) ) {
99 ulong type, creator;
101 *p = 0;
102 topdir = 0;
104 strncat( buf, ent.name, sizeof(buf) );
105 if( (status=_search(vol, buf, sname, ret_fd)) != 2 )
106 continue;
107 topdir = 1;
109 /* name search? */
110 if( sname ) {
111 status = strcasecmp( ent.name, sname );
112 continue;
115 type = *(ulong*)ent.u.file.type;
116 creator = *(ulong*)ent.u.file.creator;
118 /* look for Mac OS ROM, System and Finder in the same directory */
119 if( type == MAC_OS_ROM_TYPE && creator == MAC_OS_ROM_CREATOR ) {
120 if( strcasecmp(ent.name, MAC_OS_ROM_NAME) )
121 continue;
123 status = _find_file( vol, path, FINDER_TYPE, FINDER_CREATOR )
124 || _find_file( vol, path, SYSTEM_TYPE, SYSTEM_CREATOR );
127 if( !status && topdir && ret_fd && !(*ret_fd=hfs_open(vol, buf)) ) {
128 printk("Unexpected error: failed to open matched ROM\n");
129 status = 1;
132 hfs_closedir( dir );
133 return status;
136 static hfsfile *
137 _do_search( hfs_info_t *mi, const char *sname )
139 hfsvol *vol = hfs_getvol( NULL );
141 mi->common->type = FILE;
142 (void)_search( vol, ":", sname, &mi->common->file );
144 return mi->common->file;
148 static const int days_month[12] =
149 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
150 static const int days_month_leap[12] =
151 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
153 static inline int is_leap(int year)
155 return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
158 static void
159 print_date(time_t sec)
161 unsigned int second, minute, hour, month, day, year;
162 int current;
163 const int *days;
165 second = sec % 60;
166 sec /= 60;
168 minute = sec % 60;
169 sec /= 60;
171 hour = sec % 24;
172 sec /= 24;
174 year = sec * 100 / 36525;
175 sec -= year * 36525 / 100;
176 year += 1970;
178 days = is_leap(year) ? days_month_leap : days_month;
180 current = 0;
181 month = 0;
182 while (month < 12) {
183 if (sec <= current + days[month]) {
184 break;
186 current += days[month];
187 month++;
189 month++;
191 day = sec - current + 1;
193 forth_printf("%d-%02d-%02d %02d:%02d:%02d ",
194 year, month, day, hour, minute, second);
198 static void
199 dir_fs( file_desc_t *fd )
201 hfscommon *common = (hfscommon*)fd;
202 hfsdirent ent;
204 if (common->type != DIR)
205 return;
207 forth_printf("\n");
208 while( !hfs_readdir(common->dir, &ent) ) {
209 forth_printf("% 10d ", ent.u.file.dsize);
210 print_date(ent.mddate);
211 if( ent.flags & HFS_ISDIR )
212 forth_printf("%s\\\n", ent.name);
213 else
214 forth_printf("%s\n", ent.name);
219 /************************************************************************/
220 /* Standard package methods */
221 /************************************************************************/
223 /* ( -- success? ) */
224 static void
225 hfs_files_open( hfs_info_t *mi )
227 int fd;
228 char *path = my_args_copy();
230 const char *s;
231 char buf[256];
233 fd = open_ih( my_parent() );
234 if ( fd == -1 ) {
235 free( path );
236 RET( 0 );
239 mi->vol = hfs_mount(fd, 0);
240 if (!mi->vol) {
241 RET( 0 );
244 if( !strncmp(path, "\\\\", 2) ) {
245 hfsvolent ent;
247 /* \\ is an alias for the (blessed) system folder */
248 if( hfs_vstat(mi->vol, &ent) < 0 || hfs_setcwd(mi->vol, ent.blessed) ) {
249 free(path);
250 RET( -1 );
252 path += 2;
253 } else {
254 hfs_chdir( mi->vol, ":" );
257 mi->common = malloc(sizeof(hfscommon));
258 if (!mi->common) {
259 free(path);
260 RET( 0 );
263 if (strcmp(path, "\\") == 0) {
264 /* root directory is in fact ":" */
265 mi->common->dir = hfs_opendir(mi->vol, ":");
266 mi->common->type = DIR;
267 free(path);
268 RET( -1 );
271 if (path[strlen(path) - 1] == '\\') {
272 path[strlen(path) - 1] = 0;
275 for( path-- ;; ) {
276 int n;
278 s = ++path;
279 path = strchr(s, '\\');
280 if( !path || !path[1])
281 break;
282 n = MIN( sizeof(buf)-1, (path-s) );
283 if( !n )
284 continue;
286 strncpy( buf, s, n );
287 buf[n] = 0;
288 if( hfs_chdir(mi->vol, buf) ) {
289 free(mi->common);
290 free(path);
291 RET( 0 );
295 /* support the ':filetype' syntax */
296 if( *s == ':' ) {
297 unsigned long id, oldid = hfs_getcwd(mi->vol);
298 hfsdirent ent;
299 hfsdir *dir;
301 s++;
302 id = oldid;
303 hfs_dirinfo( mi->vol, &id, buf );
304 hfs_setcwd( mi->vol, id );
306 if( !(dir=hfs_opendir(mi->vol, buf)) ) {
307 free(mi->common);
308 free(path);
309 RET( 0 );
311 hfs_setcwd( mi->vol, oldid );
313 while( !hfs_readdir(dir, &ent) ) {
314 if( ent.flags & HFS_ISDIR )
315 continue;
316 if( !strncmp(s, ent.u.file.type, 4) ) {
317 mi->common->type = FILE;
318 mi->common->file = hfs_open( mi->vol, ent.name );
319 break;
322 hfs_closedir( dir );
323 free(path);
324 RET( -1 );
327 mi->common->dir = hfs_opendir(mi->vol, s);
328 if (!mi->common->dir) {
329 mi->common->file = hfs_open( mi->vol, s );
330 if (mi->common->file == NULL) {
331 free(mi->common);
332 free(path);
333 RET( 0 );
335 mi->common->type = FILE;
336 free(path);
337 RET( -1 );
339 mi->common->type = DIR;
340 free(path);
342 RET( -1 );
345 /* ( -- ) */
346 static void
347 hfs_files_close( hfs_info_t *mi )
349 hfscommon *common = mi->common;
350 if (common->type == FILE)
351 hfs_close( common->file );
352 else if (common->type == DIR)
353 hfs_closedir( common->dir );
354 free(common);
357 /* ( buf len -- actlen ) */
358 static void
359 hfs_files_read( hfs_info_t *mi )
361 int count = POP();
362 char *buf = (char *)POP();
364 hfscommon *common = mi->common;
365 if (common->type != FILE)
366 RET( -1 );
368 RET ( hfs_read( common->file, buf, count ) );
371 /* ( pos.d -- status ) */
372 static void
373 hfs_files_seek( hfs_info_t *mi )
375 llong pos = DPOP();
376 int offs = (int)pos;
377 int whence = SEEK_SET;
378 int ret;
379 hfscommon *common = mi->common;
381 if (common->type != FILE)
382 RET( -1 );
384 switch( whence ) {
385 case SEEK_END:
386 whence = HFS_SEEK_END;
387 break;
388 default:
389 case SEEK_SET:
390 whence = HFS_SEEK_SET;
391 break;
394 ret = hfs_seek( common->file, offs, whence );
395 if (ret != -1)
396 RET( 0 );
397 else
398 RET( -1 );
401 /* ( addr -- size ) */
402 static void
403 hfs_files_load( hfs_info_t *mi )
405 char *buf = (char *)POP();
406 int count;
408 hfscommon *common = mi->common;
409 if (common->type != FILE)
410 RET( -1 );
412 /* Seek to the end in order to get the file size */
413 hfs_seek(common->file, 0, HFS_SEEK_END);
414 count = common->file->pos;
415 hfs_seek(common->file, 0, HFS_SEEK_SET);
417 RET ( hfs_read( common->file, buf, count ) );
420 /* ( -- success? ) */
421 static void
422 hfs_files_open_nwrom( hfs_info_t *mi )
424 /* Switch to an existing ROM image file on the fs! */
425 if ( _do_search( mi, NULL ) )
426 RET( -1 );
428 RET( 0 );
431 /* ( -- cstr ) */
432 static void
433 hfs_files_get_path( hfs_info_t *mi )
435 char buf[256], buf2[256];
436 hfscommon *common = mi->common;
437 hfsvol *vol = hfs_getvol( NULL );
438 hfsdirent ent;
439 int start, ns;
440 ulong id;
442 if (common->type != FILE)
443 RET( 0 );
445 hfs_fstat( common->file, &ent );
446 start = sizeof(buf) - strlen(ent.name) - 1;
447 if( start <= 0 )
448 RET ( 0 );
449 strcpy( buf+start, ent.name );
450 buf[--start] = '\\';
452 ns = start;
453 for( id=ent.parid ; !hfs_dirinfo(vol, &id, buf2) ; ) {
454 start = ns;
455 ns -= strlen(buf2);
456 if( ns <= 0 )
457 RET( 0 );
458 strcpy( buf+ns, buf2 );
459 buf[--ns] = buf[start] = '\\';
461 if( strlen(buf) >= sizeof(buf) )
462 RET( 0 );
464 RET( (ucell) strdup(buf+start) );
467 /* ( -- cstr ) */
468 static void
469 hfs_files_get_fstype( hfs_info_t *mi )
471 PUSH( (ucell)strdup("HFS") );
474 /* ( -- cstr|0 ) */
475 static void
476 hfs_files_volume_name( hfs_info_t *mi )
478 int fd;
479 char *volname = malloc(VOLNAME_SIZE);
481 fd = open_ih(my_self());
482 get_hfs_vol_name(fd, volname, VOLNAME_SIZE);
483 close_io(fd);
485 PUSH ((ucell)volname);
488 /* static method, ( pathstr len ihandle -- ) */
489 static void
490 hfs_files_dir( hfs_info_t *dummy )
492 hfsvol *volume;
493 hfscommon *common;
494 hfsdirent ent;
495 int i;
496 int fd;
498 ihandle_t ih = POP();
499 char *path = pop_fstr_copy();
501 fd = open_ih( ih );
502 if ( fd == -1 ) {
503 free( path );
504 return;
507 volume = hfs_mount(fd, 0);
508 if (!volume) {
509 return;
512 common = malloc(sizeof(hfscommon));
514 /* HFS paths are colon separated, not backslash separated */
515 for (i = 0; i < strlen(path); i++)
516 if (path[i] == '\\')
517 path[i] = ':';
519 common->dir = hfs_opendir(volume, path);
521 forth_printf("\n");
522 while( !hfs_readdir(common->dir, &ent) ) {
523 forth_printf("% 10d ", ent.u.file.dsize);
524 print_date(ent.mddate);
525 if( ent.flags & HFS_ISDIR )
526 forth_printf("%s\\\n", ent.name);
527 else
528 forth_printf("%s\n", ent.name);
531 hfs_closedir( common->dir );
532 hfs_umount( volume );
534 close_io( fd );
536 free( common );
537 free( path );
540 /* static method, ( pos.d ih -- flag? ) */
541 static void
542 hfs_files_probe( hfs_info_t *dummy )
544 ihandle_t ih = POP_ih();
545 llong offs = DPOP();
546 int fd, ret = 0;
548 fd = open_ih(ih);
549 if (hfs_probe(fd, offs))
550 ret = -1;
552 close_io(fd);
554 RET (ret);
557 static void
558 hfs_initializer( hfs_info_t *dummy )
560 fword("register-fs-package");
563 NODE_METHODS( hfs ) = {
564 { "probe", hfs_files_probe },
565 { "open", hfs_files_open },
566 { "close", hfs_files_close },
567 { "read", hfs_files_read },
568 { "seek", hfs_files_seek },
569 { "load", hfs_files_load },
570 { "dir", hfs_files_dir },
572 /* special */
573 { "open-nwrom", hfs_files_open_nwrom },
574 { "get-path", hfs_files_get_path },
575 { "get-fstype", hfs_files_get_fstype },
576 { "volume-name", hfs_files_volume_name },
578 { NULL, hfs_initializer },
581 void
582 hfs_init( void )
584 REGISTER_NODE( hfs );