Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / fs / hfs / hfs.c
1 /*
2  * libhfs - library for reading and writing Macintosh HFS volumes
3  * Copyright (C) 1996-1998 Robert Leslie
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
18  * MA 02110-1301, USA.
19  *
20  * $Id: hfs.c,v 1.15 1998/11/02 22:09:00 rob Exp $
21  */
22
23 #include "config.h"
24 #include "libhfs.h"
25 #include "data.h"
26 #include "block.h"
27 #include "medium.h"
28 #include "file.h"
29 #include "btree.h"
30 #include "node.h"
31 #include "record.h"
32 #include "volume.h"
33
34 const char *hfs_error = "no error";     /* static error string */
35
36 hfsvol *hfs_mounts;                     /* linked list of mounted volumes */
37
38 static
39 hfsvol *curvol;                         /* current volume */
40
41
42 /*
43  * NAME:        getvol()
44  * DESCRIPTION: validate a volume reference
45  */
46 static
47 int getvol(hfsvol **vol)
48 {
49   if (*vol == NULL)
50     {
51       if (curvol == NULL)
52         ERROR(EINVAL, "no volume is current");
53
54       *vol = curvol;
55     }
56
57   return 0;
58
59 fail:
60   return -1;
61 }
62
63 /* High-Level Volume Routines ============================================== */
64
65 /*
66  * NAME:        hfs->mount()
67  * DESCRIPTION: open an HFS volume; return volume descriptor or 0 (error)
68  */
69 hfsvol *hfs_mount( int os_fd, int pnum)
70 {
71   hfsvol *vol, *check;
72   int mode = HFS_MODE_RDONLY;
73
74   /* see if the volume is already mounted */
75   for (check = hfs_mounts; check; check = check->next)
76     {
77       if (check->pnum == pnum && v_same(check, os_fd) == 1)
78         {
79             vol = check;
80             goto done;
81         }
82     }
83
84   vol = ALLOC(hfsvol, 1);
85   if (vol == NULL)
86     ERROR(ENOMEM, NULL);
87
88   v_init(vol, mode);
89
90   vol->flags |= HFS_VOL_READONLY;
91   if( v_open(vol, os_fd) == -1 )
92           goto fail;
93
94   /* mount the volume */
95
96   if (v_geometry(vol, pnum) == -1 ||
97       v_mount(vol) == -1)
98     goto fail;
99
100   /* add to linked list of volumes */
101
102   vol->prev = NULL;
103   vol->next = hfs_mounts;
104
105   if (hfs_mounts)
106     hfs_mounts->prev = vol;
107
108   hfs_mounts = vol;
109
110 done:
111   ++vol->refs;
112   curvol = vol;
113
114   return vol;
115
116 fail:
117   if (vol)
118     {
119       v_close(vol);
120       FREE(vol);
121     }
122
123   return NULL;
124 }
125
126
127 /*
128  * NAME:        hfs->umount()
129  * DESCRIPTION: close an HFS volume
130  */
131 int hfs_umount(hfsvol *vol)
132 {
133   int result = 0;
134
135   if (getvol(&vol) == -1)
136     goto fail;
137
138   if (--vol->refs)
139     {
140       goto done;
141     }
142
143   /* close all open files and directories */
144
145   while (vol->files)
146     {
147       if (hfs_close(vol->files) == -1)
148         result = -1;
149     }
150
151   while (vol->dirs)
152     {
153       if (hfs_closedir(vol->dirs) == -1)
154         result = -1;
155     }
156
157   /* close medium */
158
159   if (v_close(vol) == -1)
160     result = -1;
161
162   /* remove from linked list of volumes */
163
164   if (vol->prev)
165     vol->prev->next = vol->next;
166   if (vol->next)
167     vol->next->prev = vol->prev;
168
169   if (vol == hfs_mounts)
170     hfs_mounts = vol->next;
171   if (vol == curvol)
172     curvol = NULL;
173
174   FREE(vol);
175
176 done:
177   return result;
178
179 fail:
180   return -1;
181 }
182
183 /*
184  * NAME:        hfs->umountall()
185  * DESCRIPTION: unmount all mounted volumes
186  */
187 void hfs_umountall(void)
188 {
189   while (hfs_mounts)
190     hfs_umount(hfs_mounts);
191 }
192
193 /*
194  * NAME:        hfs->getvol()
195  * DESCRIPTION: return a pointer to a mounted volume
196  */
197 hfsvol *hfs_getvol(const char *name)
198 {
199   hfsvol *vol;
200
201   if (name == NULL)
202     return curvol;
203
204   for (vol = hfs_mounts; vol; vol = vol->next)
205     {
206       if (d_relstring(name, vol->mdb.drVN) == 0)
207         return vol;
208     }
209
210   return NULL;
211 }
212
213 /*
214  * NAME:        hfs->setvol()
215  * DESCRIPTION: change the current volume
216  */
217 void hfs_setvol(hfsvol *vol)
218 {
219   curvol = vol;
220 }
221
222 /*
223  * NAME:        hfs->vstat()
224  * DESCRIPTION: return volume statistics
225  */
226 int hfs_vstat(hfsvol *vol, hfsvolent *ent)
227 {
228   if (getvol(&vol) == -1)
229     goto fail;
230
231   strcpy(ent->name, vol->mdb.drVN);
232
233   ent->flags     = (vol->flags & HFS_VOL_READONLY) ? HFS_ISLOCKED : 0;
234
235   ent->totbytes  = vol->mdb.drNmAlBlks * vol->mdb.drAlBlkSiz;
236   ent->freebytes = vol->mdb.drFreeBks  * vol->mdb.drAlBlkSiz;
237
238   ent->alblocksz = vol->mdb.drAlBlkSiz;
239   ent->clumpsz   = vol->mdb.drClpSiz;
240
241   ent->numfiles  = vol->mdb.drFilCnt;
242   ent->numdirs   = vol->mdb.drDirCnt;
243
244   ent->crdate    = d_ltime(vol->mdb.drCrDate);
245   ent->mddate    = d_ltime(vol->mdb.drLsMod);
246   ent->bkdate    = d_ltime(vol->mdb.drVolBkUp);
247
248   ent->blessed   = vol->mdb.drFndrInfo[0];
249
250   return 0;
251
252 fail:
253   return -1;
254 }
255
256
257 /* High-Level Directory Routines =========================================== */
258
259 /*
260  * NAME:        hfs->chdir()
261  * DESCRIPTION: change current HFS directory
262  */
263 int hfs_chdir(hfsvol *vol, const char *path)
264 {
265   CatDataRec data;
266
267   if (getvol(&vol) == -1 ||
268       v_resolve(&vol, path, &data, NULL, NULL, NULL) <= 0)
269     goto fail;
270
271   if (data.cdrType != cdrDirRec)
272     ERROR(ENOTDIR, NULL);
273
274   vol->cwd = data.u.dir.dirDirID;
275
276   return 0;
277
278 fail:
279   return -1;
280 }
281
282 /*
283  * NAME:        hfs->getcwd()
284  * DESCRIPTION: return the current working directory ID
285  */
286 unsigned long hfs_getcwd(hfsvol *vol)
287 {
288   if (getvol(&vol) == -1)
289     return 0;
290
291   return vol->cwd;
292 }
293
294 /*
295  * NAME:        hfs->setcwd()
296  * DESCRIPTION: set the current working directory ID
297  */
298 int hfs_setcwd(hfsvol *vol, unsigned long id)
299 {
300   if (getvol(&vol) == -1)
301     goto fail;
302
303   if (id == vol->cwd)
304     goto done;
305
306   /* make sure the directory exists */
307
308   if (v_getdthread(vol, id, NULL, NULL) <= 0)
309     goto fail;
310
311   vol->cwd = id;
312
313 done:
314   return 0;
315
316 fail:
317   return -1;
318 }
319
320 /*
321  * NAME:        hfs->dirinfo()
322  * DESCRIPTION: given a directory ID, return its (name and) parent ID
323  */
324 int hfs_dirinfo(hfsvol *vol, unsigned long *id, char *name)
325 {
326   CatDataRec thread;
327
328   if (getvol(&vol) == -1 ||
329       v_getdthread(vol, *id, &thread, NULL) <= 0)
330     goto fail;
331
332   *id = thread.u.dthd.thdParID;
333
334   if (name)
335     strcpy(name, thread.u.dthd.thdCName);
336
337   return 0;
338
339 fail:
340   return -1;
341 }
342
343 /*
344  * NAME:        hfs->opendir()
345  * DESCRIPTION: prepare to read the contents of a directory
346  */
347 hfsdir *hfs_opendir(hfsvol *vol, const char *path)
348 {
349   hfsdir *dir = NULL;
350   CatKeyRec key;
351   CatDataRec data;
352   byte pkey[HFS_CATKEYLEN];
353
354   if (getvol(&vol) == -1)
355     goto fail;
356
357   dir = ALLOC(hfsdir, 1);
358   if (dir == NULL)
359     ERROR(ENOMEM, NULL);
360
361   dir->vol = vol;
362
363   if (*path == 0)
364     {
365       /* meta-directory containing root dirs from all mounted volumes */
366
367       dir->dirid = 0;
368       dir->vptr  = hfs_mounts;
369     }
370   else
371     {
372       if (v_resolve(&vol, path, &data, NULL, NULL, NULL) <= 0)
373         goto fail;
374
375       if (data.cdrType != cdrDirRec)
376         ERROR(ENOTDIR, NULL);
377
378       dir->dirid = data.u.dir.dirDirID;
379       dir->vptr  = NULL;
380
381       r_makecatkey(&key, dir->dirid, "");
382       r_packcatkey(&key, pkey, NULL);
383
384       if (bt_search(&vol->cat, pkey, &dir->n) <= 0)
385         goto fail;
386     }
387
388   dir->prev = NULL;
389   dir->next = vol->dirs;
390
391   if (vol->dirs)
392     vol->dirs->prev = dir;
393
394   vol->dirs = dir;
395
396   return dir;
397
398 fail:
399   FREE(dir);
400   return NULL;
401 }
402
403 /*
404  * NAME:        hfs->readdir()
405  * DESCRIPTION: return the next entry in the directory
406  */
407 int hfs_readdir(hfsdir *dir, hfsdirent *ent)
408 {
409   CatKeyRec key;
410   CatDataRec data;
411   const byte *ptr;
412
413   if (dir->dirid == 0)
414     {
415       hfsvol *vol;
416       char cname[HFS_MAX_FLEN + 1];
417
418       for (vol = hfs_mounts; vol; vol = vol->next)
419         {
420           if (vol == dir->vptr)
421             break;
422         }
423
424       if (vol == NULL)
425         ERROR(ENOENT, "no more entries");
426
427       if (v_getdthread(vol, HFS_CNID_ROOTDIR, &data, NULL) <= 0 ||
428           v_catsearch(vol, HFS_CNID_ROOTPAR, data.u.dthd.thdCName,
429                       &data, cname, NULL) <= 0)
430         goto fail;
431
432       r_unpackdirent(HFS_CNID_ROOTPAR, cname, &data, ent);
433
434       dir->vptr = vol->next;
435
436       goto done;
437     }
438
439   if (dir->n.rnum == -1)
440     ERROR(ENOENT, "no more entries");
441
442   while (1)
443     {
444       ++dir->n.rnum;
445
446       while (dir->n.rnum >= dir->n.nd.ndNRecs)
447         {
448           if (dir->n.nd.ndFLink == 0)
449             {
450               dir->n.rnum = -1;
451               ERROR(ENOENT, "no more entries");
452             }
453
454           if (bt_getnode(&dir->n, dir->n.bt, dir->n.nd.ndFLink) == -1)
455             {
456               dir->n.rnum = -1;
457               goto fail;
458             }
459
460           dir->n.rnum = 0;
461         }
462
463       ptr = HFS_NODEREC(dir->n, dir->n.rnum);
464
465       r_unpackcatkey(ptr, &key);
466
467       if (key.ckrParID != dir->dirid)
468         {
469           dir->n.rnum = -1;
470           ERROR(ENOENT, "no more entries");
471         }
472
473       r_unpackcatdata(HFS_RECDATA(ptr), &data);
474
475       switch (data.cdrType)
476         {
477         case cdrDirRec:
478         case cdrFilRec:
479           r_unpackdirent(key.ckrParID, key.ckrCName, &data, ent);
480           goto done;
481
482         case cdrThdRec:
483         case cdrFThdRec:
484           break;
485
486         default:
487           dir->n.rnum = -1;
488           ERROR(EIO, "unexpected directory entry found");
489         }
490     }
491
492 done:
493   return 0;
494
495 fail:
496   return -1;
497 }
498
499 /*
500  * NAME:        hfs->closedir()
501  * DESCRIPTION: stop reading a directory
502  */
503 int hfs_closedir(hfsdir *dir)
504 {
505   hfsvol *vol = dir->vol;
506
507   if (dir->prev)
508     dir->prev->next = dir->next;
509   if (dir->next)
510     dir->next->prev = dir->prev;
511   if (dir == vol->dirs)
512     vol->dirs = dir->next;
513
514   FREE(dir);
515
516   return 0;
517 }
518
519 /* High-Level File Routines ================================================ */
520
521 /*
522  * NAME:        hfs->open()
523  * DESCRIPTION: prepare a file for I/O
524  */
525 hfsfile *hfs_open(hfsvol *vol, const char *path)
526 {
527   hfsfile *file = NULL;
528
529   if (getvol(&vol) == -1)
530     goto fail;
531
532   file = ALLOC(hfsfile, 1);
533   if (file == NULL)
534     ERROR(ENOMEM, NULL);
535
536   if (v_resolve(&vol, path, &file->cat, &file->parid, file->name, NULL) <= 0)
537     goto fail;
538
539   if (file->cat.cdrType != cdrFilRec)
540     ERROR(EISDIR, NULL);
541
542   /* package file handle for user */
543
544   file->vol   = vol;
545   file->flags = 0;
546
547   f_selectfork(file, fkData);
548
549   file->prev = NULL;
550   file->next = vol->files;
551
552   if (vol->files)
553     vol->files->prev = file;
554
555   vol->files = file;
556
557   return file;
558
559 fail:
560   FREE(file);
561   return NULL;
562 }
563
564 /*
565  * NAME:        hfs->setfork()
566  * DESCRIPTION: select file fork for I/O operations
567  */
568 int hfs_setfork(hfsfile *file, int fork)
569 {
570   int result = 0;
571
572   f_selectfork(file, fork ? fkRsrc : fkData);
573
574   return result;
575 }
576
577 /*
578  * NAME:        hfs->getfork()
579  * DESCRIPTION: return the current fork for I/O operations
580  */
581 int hfs_getfork(hfsfile *file)
582 {
583   return file->fork != fkData;
584 }
585
586 /*
587  * NAME:        hfs->read()
588  * DESCRIPTION: read from an open file
589  */
590 unsigned long hfs_read(hfsfile *file, void *buf, unsigned long len)
591 {
592   unsigned long *lglen, count;
593   byte *ptr = buf;
594
595   f_getptrs(file, NULL, &lglen, NULL);
596
597   if (file->pos + len > *lglen)
598     len = *lglen - file->pos;
599
600   count = len;
601   while (count)
602     {
603       unsigned long bnum, offs, chunk;
604
605       bnum  = file->pos >> HFS_BLOCKSZ_BITS;
606       offs  = file->pos & (HFS_BLOCKSZ - 1);
607
608       chunk = HFS_BLOCKSZ - offs;
609       if (chunk > count)
610         chunk = count;
611
612       if (offs == 0 && chunk == HFS_BLOCKSZ)
613         {
614           if (f_getblock(file, bnum, (block *) ptr) == -1)
615             goto fail;
616         }
617       else
618         {
619           block b;
620
621           if (f_getblock(file, bnum, &b) == -1)
622             goto fail;
623
624           memcpy(ptr, b + offs, chunk);
625         }
626
627       ptr += chunk;
628
629       file->pos += chunk;
630       count     -= chunk;
631     }
632
633   return len;
634
635 fail:
636   return -1;
637 }
638
639 /*
640  * NAME:        hfs->seek()
641  * DESCRIPTION: change file seek pointer
642  */
643 unsigned long hfs_seek(hfsfile *file, long offset, int from)
644 {
645   unsigned long *lglen, newpos;
646
647   f_getptrs(file, NULL, &lglen, NULL);
648
649   switch (from)
650     {
651     case HFS_SEEK_SET:
652       newpos = (offset < 0) ? 0 : offset;
653       break;
654
655     case HFS_SEEK_CUR:
656       if (offset < 0 && (unsigned long) -offset > file->pos)
657         newpos = 0;
658       else
659         newpos = file->pos + offset;
660       break;
661
662     case HFS_SEEK_END:
663       if (offset < 0 && (unsigned long) -offset > *lglen)
664         newpos = 0;
665       else
666         newpos = *lglen + offset;
667       break;
668
669     default:
670       ERROR(EINVAL, NULL);
671     }
672
673   if (newpos > *lglen)
674     newpos = *lglen;
675
676   file->pos = newpos;
677
678   return newpos;
679
680 fail:
681   return -1;
682 }
683
684 /*
685  * NAME:        hfs->close()
686  * DESCRIPTION: close a file
687  */
688 int hfs_close(hfsfile *file)
689 {
690   hfsvol *vol = file->vol;
691   int result = 0;
692
693   if (file->prev)
694     file->prev->next = file->next;
695   if (file->next)
696     file->next->prev = file->prev;
697   if (file == vol->files)
698     vol->files = file->next;
699
700   FREE(file);
701
702   return result;
703 }
704
705 /* High-Level Catalog Routines ============================================= */
706
707 /*
708  * NAME:        hfs->stat()
709  * DESCRIPTION: return catalog information for an arbitrary path
710  */
711 int hfs_stat(hfsvol *vol, const char *path, hfsdirent *ent)
712 {
713   CatDataRec data;
714   unsigned long parid;
715   char name[HFS_MAX_FLEN + 1];
716
717   if (getvol(&vol) == -1 ||
718       v_resolve(&vol, path, &data, &parid, name, NULL) <= 0)
719     goto fail;
720
721   r_unpackdirent(parid, name, &data, ent);
722
723   return 0;
724
725 fail:
726   return -1;
727 }
728
729 /*
730  * NAME:        hfs->fstat()
731  * DESCRIPTION: return catalog information for an open file
732  */
733 int hfs_fstat(hfsfile *file, hfsdirent *ent)
734 {
735   r_unpackdirent(file->parid, file->name, &file->cat, ent);
736
737   return 0;
738 }
739
740 /*
741  * NAME:        hfs->probe()
742  * DESCRIPTION: return whether a HFS filesystem is present at the given offset
743  */
744 int hfs_probe(int fd, long long offset)
745 {
746   return v_probe(fd, offset);
747 }