Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / fs / hfsplus / hfsp_fs.c
1 /*
2  *   Creation Date: <2001/05/05 23:33:49 samuel>
3  *   Time-stamp: <2004/01/12 10:25:39 samuel>
4  *
5  *      /package/hfsplus-files
6  *
7  *      HFS+ file system interface (and ROM lookup support)
8  *
9  *   Copyright (C) 2001, 2002, 2003, 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 "libhfsp.h"
22 #include "volume.h"
23 #include "record.h"
24 #include "unicode.h"
25 #include "blockiter.h"
26 #include "libc/diskio.h"
27 #include "libc/vsprintf.h"
28
29 #define MAC_OS_ROM_CREATOR      0x63687270      /* 'chrp' */
30 #define MAC_OS_ROM_TYPE         0x74627869      /* 'tbxi' */
31 #define MAC_OS_ROM_NAME         "Mac OS ROM"
32
33 #define FINDER_TYPE             0x464E4452      /* 'FNDR' */
34 #define FINDER_CREATOR          0x4D414353      /* 'MACS' */
35 #define SYSTEM_TYPE             0x7A737973      /* 'zsys' */
36 #define SYSTEM_CREATOR          0x4D414353      /* 'MACS' */
37
38 #define VOLNAME_SIZE    64
39
40 extern void     hfsp_init( void );
41
42 typedef struct {
43         record          rec;
44         char            *path;
45         off_t           pos;
46 } hfsp_file_t;
47
48 typedef struct {
49         volume *vol;
50         hfsp_file_t *hfspfile;
51 } hfsp_info_t;
52
53 DECLARE_NODE( hfsp, 0, sizeof(hfsp_info_t), "+/packages/hfsplus-files" );
54
55
56 /************************************************************************/
57 /*      Search implementation                                           */
58 /************************************************************************/
59
60 typedef int (*match_proc_t)( record *r, record *parent, const void *match_data, hfsp_file_t *pt );
61
62 static int
63 search_files( record *par, int recursive, match_proc_t proc, const void *match_data, hfsp_file_t *pt )
64 {
65         hfsp_file_t t;
66         record r;
67         int ret = 1;
68
69         t.path = NULL;
70
71         record_init_parent( &r, par );
72         do{
73                 if( r.record.type == HFSP_FOLDER || r.record.type == HFSP_FILE )
74                         ret = (*proc)( &r, par, match_data, &t );
75
76                 if( ret && r.record.type == HFSP_FOLDER && recursive )
77                         ret = search_files( &r, 1, proc, match_data, &t );
78
79         } while( ret && !record_next(&r) );
80
81         if( !ret && pt ) {
82                 char name[256];
83                 const char *s2 = t.path ? t.path : "";
84
85                 unicode_uni2asc( name, &r.key.name, sizeof(name));
86
87                 pt->rec = t.rec;
88                 pt->path = malloc( strlen(name) + strlen(s2) + 2 );
89                 strcpy( pt->path, name );
90                 if( strlen(s2) ) {
91                         strcat( pt->path, "\\" );
92                         strcat( pt->path, s2 );
93                 }
94         }
95
96         if( t.path )
97                 free( t.path );
98
99         return ret;
100 }
101
102 static int
103 root_search_files( volume *vol, int recursive, match_proc_t proc, const void *match_data, hfsp_file_t *pt )
104 {
105         record r;
106
107         record_init_root( &r, &vol->catalog );
108         return search_files( &r, recursive, proc, match_data, pt );
109 }
110
111 static int
112 match_file( record *r, record *parent, const void *match_data, hfsp_file_t *pt )
113 {
114         const char *p = (const char*)match_data;
115         char name[256];
116         int ret=1;
117
118         if( r->record.type != HFSP_FILE )
119                 return 1;
120
121         (void) unicode_uni2asc(name, &r->key.name, sizeof(name));
122         if( !(ret=strcasecmp(p, name)) && pt )
123                 pt->rec = *r;
124
125         return ret;
126 }
127
128 static int
129 match_rom( record *r, record *par, const void *match_data, hfsp_file_t *pt )
130 {
131         hfsp_cat_file *file = &r->record.u.file;
132         FInfo *fi = &file->user_info;
133         int ret = 1;
134         char buf[256];
135
136         if( r->record.type == HFSP_FILE && fi->fdCreator == MAC_OS_ROM_CREATOR && fi->fdType == MAC_OS_ROM_TYPE ) {
137                 ret = search_files( par, 0, match_file, "System", NULL )
138                         || search_files( par, 0, match_file, "Finder", NULL );
139
140                 (void) unicode_uni2asc(buf, &r->key.name, sizeof(buf));
141                 if( !strcasecmp("BootX", buf) )
142                         return 1;
143
144                 if( !ret && pt )
145                         pt->rec = *r;
146         }
147         return ret;
148 }
149
150 static int
151 match_path( record *r, record *par, const void *match_data, hfsp_file_t *pt )
152 {
153         char name[256], *s, *next, *org;
154         int ret=1;
155
156         next = org = strdup( (char*)match_data );
157         while( (s=strsep( &next, "\\/" )) && !strlen(s) )
158                 ;
159         if( !s ) {
160                 free( org );
161                 return 1;
162         }
163
164         if( *s == ':' && strlen(s) == 5 ) {
165                 if( r->record.type == HFSP_FILE && !next ) {
166                         /* match type */
167                         hfsp_cat_file *file = &r->record.u.file;
168                         FInfo *fi = &file->user_info;
169                         int i, type=0;
170                         for( i=1; s[i] && i<=4; i++ )
171                                 type = (type << 8) | s[i];
172                         /* printk("fi->fdType: %s / %s\n", s+1, b ); */
173                         if( fi->fdType == type ) {
174                                 if( pt )
175                                         pt->rec = *r;
176                                 ret = 0;
177                         }
178                 }
179         } else {
180                 (void) unicode_uni2asc(name, &r->key.name, sizeof(name));
181
182                 if( !strcasecmp(s, name) ) {
183                         if( r->record.type == HFSP_FILE && !next ) {
184                                 if( pt )
185                                         pt->rec = *r;
186                                 ret = 0;
187                         } else /* must be a directory */
188                                 ret = search_files( r, 0, match_path, next, pt );
189                 }
190         }
191         free( org );
192         return ret;
193 }
194
195
196 /************************************************************************/
197 /*      Standard package methods                                                */
198 /************************************************************************/
199
200 /* ( -- success? ) */
201 static void
202 hfsp_files_open( hfsp_info_t *mi )
203 {
204         int fd;
205         char *path = my_args_copy();
206
207         if ( ! path )
208                 RET( 0 );
209
210         fd = open_ih( my_parent() );
211         if ( fd == -1 ) {
212                 free( path );
213                 RET( 0 );
214         }
215
216         mi->vol = malloc( sizeof(volume) );
217         if (volume_open(mi->vol, fd)) {
218                 free( path );
219                 close_io( fd );
220                 RET( 0 );
221         }
222
223         mi->hfspfile = malloc( sizeof(hfsp_file_t) );
224         
225         /* Leading \\ means system folder. The finder info block has
226          * the following meaning.
227          *
228          *  [0] Prefered boot directory ID
229          *  [3] MacOS 9 boot directory ID
230          *  [5] MacOS X boot directory ID
231          */
232         if( !strncmp(path, "\\\\", 2) ) {
233                 int *p = (int*)&(mi->vol)->vol.finder_info[0];
234                 int cnid = p[0];
235                 /* printk(" p[0] = %x, p[3] = %x, p[5] = %x\n", p[0], p[3], p[5] ); */
236                 if( p[0] == p[5] && p[3] )
237                         cnid = p[3];
238                 if( record_init_cnid(&(mi->hfspfile->rec), &(mi->vol)->catalog, cnid) )
239                         RET ( 0 );
240                 path += 2;
241         } else {
242                 record_init_root( &(mi->hfspfile->rec), &(mi->vol)->catalog );
243         }
244
245         if( !search_files(&(mi->hfspfile->rec), 0, match_path, path, mi->hfspfile ) )
246                 RET ( -1 );
247         
248         RET ( -1 );
249 }
250
251 /* ( -- ) */
252 static void
253 hfsp_files_close( hfsp_info_t *mi )
254 {
255         volume_close(mi->vol);
256
257         if( mi->hfspfile->path )
258                 free( mi->hfspfile->path );
259         free( mi->hfspfile );
260 }
261
262 /* ( buf len -- actlen ) */
263 static void
264 hfsp_files_read( hfsp_info_t *mi )
265 {
266         int count = POP();
267         char *buf = (char *)cell2pointer(POP());
268
269         hfsp_file_t *t = mi->hfspfile;
270         volume *vol = t->rec.tree->vol;
271         UInt32 blksize = vol->blksize;
272         hfsp_cat_file *file = &t->rec.record.u.file;
273         blockiter iter;
274         char buf2[blksize];
275         int act_count, curpos=0;
276
277         blockiter_init( &iter, vol, &file->data_fork, HFSP_EXTENT_DATA, file->id );
278         while( curpos + blksize < t->pos ) {
279                 if( blockiter_next( &iter ) ) {
280                         RET ( -1 );
281                         return;
282                 }
283                 curpos += blksize;
284         }
285         act_count = 0;
286
287         while( act_count < count ){
288                 UInt32 block = blockiter_curr(&iter);
289                 int max = blksize, add = 0, size;
290
291                 if( volume_readinbuf( vol, buf2, block ) )
292                         break;
293
294                 if( curpos < t->pos ){
295                         add += t->pos - curpos;
296                         max -= t->pos - curpos;
297                 }
298                 size = (count-act_count > max)? max : count-act_count;
299                 memcpy( (char *)buf + act_count, &buf2[add], size );
300
301                 curpos += blksize;
302                 act_count += size;
303
304                 if( blockiter_next( &iter ) )
305                         break;
306         }
307
308         t->pos += act_count;
309
310         RET ( act_count );
311 }
312
313 /* ( pos.d -- status ) */
314 static void
315 hfsp_files_seek( hfsp_info_t *mi )
316 {
317         long long pos = DPOP();
318         int offs = (int)pos;
319         int whence = SEEK_SET;
320
321         hfsp_file_t *t = mi->hfspfile;
322         hfsp_cat_file *file = &t->rec.record.u.file;
323         int total = file->data_fork.total_size;
324
325         if( offs == -1 ) {
326                 offs = 0;
327                 whence = SEEK_END;
328         }
329
330         switch( whence ){
331         case SEEK_END:
332                 t->pos = total + offs;
333                 break;
334         default:
335         case SEEK_SET:
336                 t->pos = offs;
337                 break;
338         }
339
340         if( t->pos < 0 )
341                 t->pos = 0;
342
343         if( t->pos > total )
344                 t->pos = total;
345
346         RET ( 0 );
347 }
348
349 /* ( addr -- size ) */
350 static void
351 hfsp_files_load( hfsp_info_t *mi )
352 {
353         char *buf = (char *)cell2pointer(POP());
354
355         hfsp_file_t *t = mi->hfspfile;
356         volume *vol = t->rec.tree->vol;
357         UInt32 blksize = vol->blksize;
358         hfsp_cat_file *file = &t->rec.record.u.file;
359         int total = file->data_fork.total_size;
360         blockiter iter;
361         char buf2[blksize];
362         int act_count;
363
364         blockiter_init( &iter, vol, &file->data_fork, HFSP_EXTENT_DATA, file->id );
365
366         act_count = 0;
367
368         while( act_count < total ){
369                 UInt32 block = blockiter_curr(&iter);
370                 int max = blksize, size;
371
372                 if( volume_readinbuf( vol, buf2, block ) )
373                         break;
374
375                 size = (total-act_count > max)? max : total-act_count;
376                 memcpy( (char *)buf + act_count, &buf2, size );
377
378                 act_count += size;
379
380                 if( blockiter_next( &iter ) )
381                         break;
382         }
383
384         RET ( act_count );
385 }
386
387 /* ( -- cstr ) */
388 static void
389 hfsp_files_get_fstype( hfsp_info_t *mi )
390 {
391         PUSH( pointer2cell(strdup("HFS+")) );
392 }
393
394 /* ( -- cstr ) */
395 static void
396 hfsp_files_get_path( hfsp_info_t *mi )
397 {
398         char *buf;
399         hfsp_file_t *t = mi->hfspfile;
400
401         if( !t->path )
402                 RET ( 0 );
403
404         buf = malloc(strlen(t->path) + 1);
405         strncpy( buf, t->path, strlen(t->path) );
406         buf[strlen(t->path)] = 0;
407
408         PUSH(pointer2cell(buf));
409 }
410
411 /* ( -- success? ) */
412 static void
413 hfsp_files_open_nwrom( hfsp_info_t *mi )
414 {
415         /* Switch to an existing ROM image file on the fs! */
416         if( !root_search_files(mi->vol, 1, match_rom, NULL, mi->hfspfile) )
417                 RET ( -1 );
418
419         RET ( 0 );
420 }
421
422 /* ( -- cstr|0 ) */
423 static void
424 hfsp_files_volume_name( hfsp_info_t *mi )
425 {
426         int fd;
427         char *volname = malloc(VOLNAME_SIZE);
428
429         fd = open_ih(my_self());
430         if (fd >= 0) {
431                 get_hfs_vol_name(fd, volname, VOLNAME_SIZE);
432                 close_io(fd);
433         } else {
434                 volname[0] = '\0';
435         }
436
437         PUSH(pointer2cell(volname));
438 }
439
440 static const int days_month[12] =
441         { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
442 static const int days_month_leap[12] =
443         { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
444
445 static inline int is_leap(int year)
446 {
447         return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
448 }
449
450 static void
451 print_date(uint32_t sec)
452 {
453         unsigned int second, minute, hour, month, day, year;
454         int current;
455         const int *days;
456
457         second = sec % 60;
458         sec /= 60;
459
460         minute = sec % 60;
461         sec /= 60;
462
463         hour = sec % 24;
464         sec /= 24;
465
466         year = sec * 100 / 36525;
467         sec -= year * 36525 / 100;
468         year += 1904;
469
470         days = is_leap(year) ?  days_month_leap : days_month;
471
472         current = 0;
473         month = 0;
474         while (month < 12) {
475                 if (sec <= current + days[month]) {
476                         break;
477                 }
478                 current += days[month];
479                 month++;
480         }
481         month++;
482
483         day = sec - current + 1;
484
485         forth_printf("%d-%02d-%02d %02d:%02d:%02d ",
486                      year, month, day, hour, minute, second);
487 }
488
489 /* static method, ( pathstr len ihandle -- ) */
490 static void
491 hfsp_files_dir( hfsp_info_t *dummy )
492 {
493         ihandle_t ih = POP_ih();
494         char *path = pop_fstr_copy();
495         int fd, found;
496         volume *vol;
497         record rec, r, folrec;
498         char name[256], *curfol, *tmppath;
499         
500         fd = open_ih(ih);
501         if ( fd == -1 ) {
502                 free( path );
503                 RET( 0 );
504         }
505
506         vol = malloc( sizeof(volume) );
507         if (volume_open(vol, fd)) {
508                 free( path );
509                 close_io( fd );
510                 RET( 0 );
511         }
512         
513         /* First move to the specified folder */
514         tmppath = strdup(path);
515         record_init_root( &rec, &vol->catalog );
516         record_init_parent( &r, &rec );
517         
518         /* Remove initial \ or / */
519         curfol = strsep(&tmppath, "\\//");
520         curfol = strsep(&tmppath, "\\//");
521         forth_printf("\n");
522         
523         while (curfol && strlen(curfol)) {          
524             found = 0;
525             do {
526                 if (r.record.type == HFSP_FOLDER) {
527                     unicode_uni2asc(name, &r.key.name, sizeof(name));
528                     
529                     if (!strcmp(name, curfol)) {
530                         folrec = r;
531                         found = -1;
532                     }
533                 }
534             } while ( !record_next(&r) );
535             
536             if (!found) {
537                 forth_printf("Unable to locate path %s on filesystem\n", path);
538                 goto done;
539             } else {
540                 record_init_parent( &r, &folrec );
541             }
542             
543             curfol = strsep(&tmppath, "\\//");
544         }
545         
546         /* Output the directory contents */
547         found = 0;
548         do {
549             unicode_uni2asc(name, &r.key.name, sizeof(name));
550             
551             if (r.record.type == HFSP_FILE) {
552                 /* Grab the file entry */
553                 hfsp_cat_file *file = &r.record.u.file;
554                 forth_printf("% 10lld ", file->data_fork.total_size);
555                 print_date(file->create_date);
556                 forth_printf(" %s\n", name);
557                 found = -1;
558             }
559             
560             if (r.record.type == HFSP_FOLDER) {
561                 /* Grab the directory entry */
562                 hfsp_cat_folder *folder = &r.record.u.folder;
563                 forth_printf("         0 ");
564                 print_date(folder->create_date);
565                 forth_printf(" %s\\\n", name);
566                 found = -1;
567             }
568             
569         } while ( !record_next(&r) );
570         
571         if (!found) {
572             forth_printf("  (Empty folder)\n");
573         }
574         
575 done:
576         volume_close(vol);
577         free(vol);
578         free(path);
579         if (tmppath)
580             free(tmppath);
581 }
582
583 /* static method, ( pos.d ih -- flag? ) */
584 static void
585 hfsp_files_probe( hfsp_info_t *dummy )
586 {
587         ihandle_t ih = POP_ih();
588         long long offs = DPOP();
589         int fd, ret = 0;
590
591         fd = open_ih(ih);
592         if (fd >= 0) {
593                 if (volume_probe(fd, offs)) {
594                         ret = -1;
595                 }
596                 close_io(fd);
597         } else {
598                 ret = -1;
599         }
600
601         RET (ret);
602 }
603
604 static void
605 hfsp_initializer( hfsp_info_t *dummy )
606 {
607         fword("register-fs-package");
608 }
609
610 NODE_METHODS( hfsp ) = {
611         { "probe",      hfsp_files_probe        },
612         { "open",       hfsp_files_open         },
613         { "close",      hfsp_files_close        },
614         { "read",       hfsp_files_read         },
615         { "seek",       hfsp_files_seek         },
616         { "load",       hfsp_files_load         },
617         { "dir",        hfsp_files_dir          },
618
619         /* special */
620         { "open-nwrom",         hfsp_files_open_nwrom   },
621         { "get-path",           hfsp_files_get_path     },
622         { "get-fstype",         hfsp_files_get_fstype   },
623         { "volume-name",        hfsp_files_volume_name  },
624
625         { NULL,         hfsp_initializer        },
626 };
627
628 void
629 hfsp_init( void )
630 {
631         REGISTER_NODE( hfsp );
632 }