1 /*
2 * Creation Date: <2001/05/06 22:47:23 samuel>
3 * Time-stamp: <2004/01/12 10:24:35 samuel>
4 *
5 * /packages/hfs-files
6 *
7 * HFS world interface
8 *
9 * Copyright (C) 2001-2004 Samuel Rydh (samuel@ibrium.se)
10 * Copyright (C) 2010 Mark Cave-Ayland (mark.cave-ayland@siriusit.co.uk)
11 *
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
15 *
16 */
17
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"
24
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"
28
29 #define FINDER_TYPE 0x464E4452 /* 'FNDR' */
30 #define FINDER_CREATOR 0x4D414353 /* 'MACS' */
31 #define SYSTEM_TYPE 0x7A737973 /* 'zsys' */
32 #define SYSTEM_CREATOR 0x4D414353 /* 'MACS' */
33
34 #define VOLNAME_SIZE 64
35
36 extern void hfs_init( void );
37
38 typedef struct {
39 enum { FILE, DIR } type;
40 union {
41 hfsdir *dir;
42 hfsfile *file;
43 };
44 } hfscommon;
45
46 typedef struct {
47 hfsvol *vol;
48 hfscommon *common;
49 } hfs_info_t;
50
51 DECLARE_NODE( hfs, 0, sizeof(hfs_info_t), "+/packages/hfs-files" );
52
53 /************************************************************************/
54 /* Search Functions */
55 /************************************************************************/
56
57 static int
_find_file(hfsvol * vol,const char * path,unsigned long type,unsigned long creator)58 _find_file( hfsvol *vol, const char *path, unsigned long type, unsigned long creator )
59 {
60 hfsdirent ent;
61 hfsdir *dir;
62 int ret=1;
63
64 if( !(dir=hfs_opendir(vol, path)) )
65 return 1;
66
67 while( ret && !hfs_readdir(dir, &ent) ) {
68 if( ent.flags & HFS_ISDIR )
69 continue;
70 ret = !(*(unsigned long*)ent.u.file.type == type && *(unsigned long*)ent.u.file.creator == creator );
71 }
72
73 hfs_closedir( dir );
74 return ret;
75 }
76
77
78 /* ret: 0=success, 1=not_found, 2=not_a_dir */
79 static int
_search(hfsvol * vol,const char * path,const char * sname,hfsfile ** ret_fd)80 _search( hfsvol *vol, const char *path, const char *sname, hfsfile **ret_fd )
81 {
82 hfsdir *dir;
83 hfsdirent ent;
84 int topdir=0, status = 1;
85 char *p, buf[256];
86
87 strncpy( buf, path, sizeof(buf) );
88 if( buf[strlen(buf)-1] != ':' )
89 strncat( buf, ":", sizeof(buf) - 1 );
90 buf[sizeof(buf)-1] = 0;
91 p = buf + strlen( buf );
92
93 if( !(dir=hfs_opendir(vol, path)) )
94 return 2;
95
96 /* printk("DIRECTORY: %s\n", path ); */
97
98 while( status && !hfs_readdir(dir, &ent) ) {
99 unsigned long type, creator;
100
101 *p = 0;
102 topdir = 0;
103
104 strncat( buf, ent.name, sizeof(buf) - 1);
105 if( (status=_search(vol, buf, sname, ret_fd)) != 2 )
106 continue;
107 topdir = 1;
108
109 /* name search? */
110 if( sname ) {
111 status = strcasecmp( ent.name, sname );
112 continue;
113 }
114
115 type = *(unsigned long*)ent.u.file.type;
116 creator = *(unsigned long*)ent.u.file.creator;
117
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;
122
123 status = _find_file( vol, path, FINDER_TYPE, FINDER_CREATOR )
124 || _find_file( vol, path, SYSTEM_TYPE, SYSTEM_CREATOR );
125 }
126 }
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;
130 }
131
132 hfs_closedir( dir );
133 return status;
134 }
135
136 static hfsfile *
_do_search(hfs_info_t * mi,const char * sname)137 _do_search( hfs_info_t *mi, const char *sname )
138 {
139 hfsvol *vol = hfs_getvol( NULL );
140
141 mi->common->type = FILE;
142 (void)_search( vol, ":", sname, &mi->common->file );
143
144 return mi->common->file;
145 }
146
147
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 };
152
is_leap(int year)153 static inline int is_leap(int year)
154 {
155 return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
156 }
157
158 static void
print_date(time_t sec)159 print_date(time_t sec)
160 {
161 unsigned int second, minute, hour, month, day, year;
162 int current;
163 const int *days;
164
165 second = sec % 60;
166 sec /= 60;
167
168 minute = sec % 60;
169 sec /= 60;
170
171 hour = sec % 24;
172 sec /= 24;
173
174 year = sec * 100 / 36525;
175 sec -= year * 36525 / 100;
176 year += 1970;
177
178 days = is_leap(year) ? days_month_leap : days_month;
179
180 current = 0;
181 month = 0;
182 while (month < 12) {
183 if (sec <= current + days[month]) {
184 break;
185 }
186 current += days[month];
187 month++;
188 }
189 month++;
190
191 day = sec - current + 1;
192
193 forth_printf("%d-%02d-%02d %02d:%02d:%02d ",
194 year, month, day, hour, minute, second);
195 }
196
197 /*
198 static void
199 dir_fs( file_desc_t *fd )
200 {
201 hfscommon *common = (hfscommon*)fd;
202 hfsdirent ent;
203
204 if (common->type != DIR)
205 return;
206
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);
215 }
216 }
217 */
218
219 /************************************************************************/
220 /* Standard package methods */
221 /************************************************************************/
222
223 /* ( -- success? ) */
224 static void
hfs_files_open(hfs_info_t * mi)225 hfs_files_open( hfs_info_t *mi )
226 {
227 int fd;
228 char *path = my_args_copy();
229
230 const char *s;
231 char buf[256];
232
233 fd = open_ih( my_parent() );
234 if ( fd == -1 ) {
235 free( path );
236 RET( 0 );
237 }
238
239 mi->vol = hfs_mount(fd, 0);
240 if (!mi->vol) {
241 RET( 0 );
242 }
243
244 if( !strncmp(path, "\\\\", 2) ) {
245 hfsvolent ent;
246
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 );
251 }
252 path += 2;
253 } else {
254 hfs_chdir( mi->vol, ":" );
255 }
256
257 mi->common = malloc(sizeof(hfscommon));
258 if (!mi->common) {
259 free(path);
260 RET( 0 );
261 }
262
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 );
269 }
270
271 if (path[strlen(path) - 1] == '\\') {
272 path[strlen(path) - 1] = 0;
273 }
274
275 for( path-- ;; ) {
276 int n;
277
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;
285
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 );
292 }
293 }
294
295 /* support the ':filetype' syntax */
296 if( *s == ':' ) {
297 unsigned long id, oldid = hfs_getcwd(mi->vol);
298 hfsdirent ent;
299 hfsdir *dir;
300
301 s++;
302 id = oldid;
303 hfs_dirinfo( mi->vol, &id, buf );
304 hfs_setcwd( mi->vol, id );
305
306 if( !(dir=hfs_opendir(mi->vol, buf)) ) {
307 free(mi->common);
308 free(path);
309 RET( 0 );
310 }
311 hfs_setcwd( mi->vol, oldid );
312
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;
320 }
321 }
322 hfs_closedir( dir );
323 free(path);
324 RET( -1 );
325 }
326
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 );
334 }
335 mi->common->type = FILE;
336 free(path);
337 RET( -1 );
338 }
339 mi->common->type = DIR;
340 free(path);
341
342 RET( -1 );
343 }
344
345 /* ( -- ) */
346 static void
hfs_files_close(hfs_info_t * mi)347 hfs_files_close( hfs_info_t *mi )
348 {
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);
355 }
356
357 /* ( buf len -- actlen ) */
358 static void
hfs_files_read(hfs_info_t * mi)359 hfs_files_read( hfs_info_t *mi )
360 {
361 int count = POP();
362 char *buf = (char *)cell2pointer(POP());
363
364 hfscommon *common = mi->common;
365 if (common->type != FILE)
366 RET( -1 );
367
368 RET ( hfs_read( common->file, buf, count ) );
369 }
370
371 /* ( pos.d -- status ) */
372 static void
hfs_files_seek(hfs_info_t * mi)373 hfs_files_seek( hfs_info_t *mi )
374 {
375 long long pos = DPOP();
376 int offs = (int)pos;
377 int whence = SEEK_SET;
378 int ret;
379 hfscommon *common = mi->common;
380
381 if (common->type != FILE)
382 RET( -1 );
383
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;
392 }
393
394 ret = hfs_seek( common->file, offs, whence );
395 if (ret != -1)
396 RET( 0 );
397 else
398 RET( -1 );
399 }
400
401 /* ( addr -- size ) */
402 static void
hfs_files_load(hfs_info_t * mi)403 hfs_files_load( hfs_info_t *mi )
404 {
405 char *buf = (char *)cell2pointer(POP());
406 int count;
407
408 hfscommon *common = mi->common;
409 if (common->type != FILE)
410 RET( -1 );
411
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);
416
417 RET ( hfs_read( common->file, buf, count ) );
418 }
419
420 /* ( -- success? ) */
421 static void
hfs_files_open_nwrom(hfs_info_t * mi)422 hfs_files_open_nwrom( hfs_info_t *mi )
423 {
424 /* Switch to an existing ROM image file on the fs! */
425 if ( _do_search( mi, NULL ) )
426 RET( -1 );
427
428 RET( 0 );
429 }
430
431 /* ( -- cstr ) */
432 static void
hfs_files_get_path(hfs_info_t * mi)433 hfs_files_get_path( hfs_info_t *mi )
434 {
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 unsigned long id;
441
442 if (common->type != FILE)
443 RET( 0 );
444
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] = '\\';
451
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] = '\\';
460 }
461 if( strlen(buf) >= sizeof(buf) )
462 RET( 0 );
463
464 RET( pointer2cell(strdup(buf+start)) );
465 }
466
467 /* ( -- cstr ) */
468 static void
hfs_files_get_fstype(hfs_info_t * mi)469 hfs_files_get_fstype( hfs_info_t *mi )
470 {
471 PUSH( pointer2cell(strdup("HFS")) );
472 }
473
474 /* ( -- cstr|0 ) */
475 static void
hfs_files_volume_name(hfs_info_t * mi)476 hfs_files_volume_name( hfs_info_t *mi )
477 {
478 int fd;
479 char *volname = malloc(VOLNAME_SIZE);
480
481 fd = open_ih(my_self());
482 if (fd >= 0) {
483 get_hfs_vol_name(fd, volname, VOLNAME_SIZE);
484 close_io(fd);
485 } else {
486 volname[0] = '\0';
487 }
488
489 PUSH(pointer2cell(volname));
490 }
491
492 /* static method, ( pathstr len ihandle -- ) */
493 static void
hfs_files_dir(hfs_info_t * dummy)494 hfs_files_dir( hfs_info_t *dummy )
495 {
496 hfsvol *volume;
497 hfscommon *common;
498 hfsdirent ent;
499 int i;
500 int fd;
501
502 ihandle_t ih = POP();
503 char *path = pop_fstr_copy();
504
505 fd = open_ih( ih );
506 if ( fd == -1 ) {
507 free( path );
508 return;
509 }
510
511 volume = hfs_mount(fd, 0);
512 if (!volume) {
513 return;
514 }
515
516 common = malloc(sizeof(hfscommon));
517
518 /* HFS paths are colon separated, not backslash separated */
519 for (i = 0; i < strlen(path); i++)
520 if (path[i] == '\\')
521 path[i] = ':';
522
523 common->dir = hfs_opendir(volume, path);
524
525 forth_printf("\n");
526 while( !hfs_readdir(common->dir, &ent) ) {
527 forth_printf("% 10ld ", ent.u.file.dsize);
528 print_date(ent.mddate);
529 if( ent.flags & HFS_ISDIR )
530 forth_printf("%s\\\n", ent.name);
531 else
532 forth_printf("%s\n", ent.name);
533 }
534
535 hfs_closedir( common->dir );
536 hfs_umount( volume );
537
538 close_io( fd );
539
540 free( common );
541 free( path );
542 }
543
544 /* static method, ( pos.d ih -- flag? ) */
545 static void
hfs_files_probe(hfs_info_t * dummy)546 hfs_files_probe( hfs_info_t *dummy )
547 {
548 ihandle_t ih = POP_ih();
549 long long offs = DPOP();
550 int fd, ret = 0;
551
552 fd = open_ih(ih);
553 if (fd >= 0) {
554 if (hfs_probe(fd, offs)) {
555 ret = -1;
556 }
557 close_io(fd);
558 } else {
559 ret = -1;
560 }
561
562 RET (ret);
563 }
564
565 static void
hfs_initializer(hfs_info_t * dummy)566 hfs_initializer( hfs_info_t *dummy )
567 {
568 fword("register-fs-package");
569 }
570
571 NODE_METHODS( hfs ) = {
572 { "probe", hfs_files_probe },
573 { "open", hfs_files_open },
574 { "close", hfs_files_close },
575 { "read", hfs_files_read },
576 { "seek", hfs_files_seek },
577 { "load", hfs_files_load },
578 { "dir", hfs_files_dir },
579
580 /* special */
581 { "open-nwrom", hfs_files_open_nwrom },
582 { "get-path", hfs_files_get_path },
583 { "get-fstype", hfs_files_get_fstype },
584 { "volume-name", hfs_files_volume_name },
585
586 { NULL, hfs_initializer },
587 };
588
589 void
hfs_init(void)590 hfs_init( void )
591 {
592 REGISTER_NODE( hfs );
593 }
594