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