Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / fs / hfs / hfs_fs.c
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
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
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) );
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) );
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 *
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
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
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
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
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
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
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
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
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
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
469 hfs_files_get_fstype( hfs_info_t *mi )
470 {
471         PUSH( pointer2cell(strdup("HFS")) );
472 }
473
474 /* ( -- cstr|0 ) */
475 static void
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
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
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
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
590 hfs_init( void )
591 {
592         REGISTER_NODE( hfs );
593 }