Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / fs / hfs / volume.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: volume.c,v 1.12 1998/11/02 22:09:10 rob Exp $
21  */
22
23 #include "config.h"
24 #include "libhfs.h"
25 #include "volume.h"
26 #include "data.h"
27 #include "block.h"
28 #include "low.h"
29 #include "medium.h"
30 #include "file.h"
31 #include "btree.h"
32 #include "record.h"
33 #include "os.h"
34
35 #include "libc/byteorder.h"
36
37 /*
38  * NAME:        vol->init()
39  * DESCRIPTION: initialize volume structure
40  */
41 void v_init(hfsvol *vol, int flags)
42 {
43   btree *ext = &vol->ext;
44   btree *cat = &vol->cat;
45
46   vol->os_fd       = 0;
47   vol->flags      = flags & HFS_VOL_OPT_MASK;
48
49   vol->pnum       = -1;
50   vol->vstart     = 0;
51   vol->vlen       = 0;
52   vol->lpa        = 0;
53
54   vol->cache      = NULL;
55
56   vol->vbm        = NULL;
57   vol->vbmsz      = 0;
58
59   f_init(&ext->f, vol, HFS_CNID_EXT, "extents overflow");
60
61   ext->map        = NULL;
62   ext->mapsz      = 0;
63   ext->flags      = 0;
64
65   ext->keyunpack  = (keyunpackfunc)  r_unpackextkey;
66   ext->keycompare = (keycomparefunc) r_compareextkeys;
67
68   f_init(&cat->f, vol, HFS_CNID_CAT, "catalog");
69
70   cat->map        = NULL;
71   cat->mapsz      = 0;
72   cat->flags      = 0;
73
74   cat->keyunpack  = (keyunpackfunc)  r_unpackcatkey;
75   cat->keycompare = (keycomparefunc) r_comparecatkeys;
76
77   vol->cwd        = HFS_CNID_ROOTDIR;
78
79   vol->refs       = 0;
80   vol->files      = NULL;
81   vol->dirs       = NULL;
82
83   vol->prev       = NULL;
84   vol->next       = NULL;
85 }
86
87 /*
88  * NAME:        vol->open()
89  * DESCRIPTION: open volume source and lock against concurrent updates
90  */
91 int v_open(hfsvol *vol, int os_fd )
92 {
93   if (vol->flags & HFS_VOL_OPEN)
94     ERROR(EINVAL, "volume already open");
95
96   vol->flags |= HFS_VOL_OPEN;
97   vol->os_fd = os_fd;
98
99   /* initialize volume block cache (OK to fail) */
100
101   if (! (vol->flags & HFS_OPT_NOCACHE) &&
102       b_init(vol) != -1)
103     vol->flags |= HFS_VOL_USINGCACHE;
104
105   return 0;
106
107 fail:
108   return -1;
109 }
110
111 /*
112  * NAME:        vol->close()
113  * DESCRIPTION: close access path to volume source
114  */
115 int v_close(hfsvol *vol)
116 {
117   int result = 0;
118
119   if (! (vol->flags & HFS_VOL_OPEN))
120     goto done;
121
122   if ((vol->flags & HFS_VOL_USINGCACHE) &&
123       b_finish(vol) == -1)
124     result = -1;
125
126   vol->flags &= ~(HFS_VOL_OPEN | HFS_VOL_MOUNTED | HFS_VOL_USINGCACHE);
127
128   /* free dynamically allocated structures */
129
130   FREE(vol->vbm);
131
132   vol->vbm   = NULL;
133   vol->vbmsz = 0;
134
135   FREE(vol->ext.map);
136   FREE(vol->cat.map);
137
138   vol->ext.map = NULL;
139   vol->cat.map = NULL;
140
141 done:
142   return result;
143 }
144
145 /*
146  * NAME:        vol->same()
147  * DESCRIPTION: return 1 iff path is same as open volume
148  */
149 int v_same(hfsvol *vol, int os_fd )
150 {
151   return vol->os_fd == os_fd;
152 }
153
154 /*
155  * NAME:        vol->geometry()
156  * DESCRIPTION: determine volume location and size (possibly in a partition)
157  */
158 int v_geometry(hfsvol *vol, int pnum)
159 {
160   Partition map;
161   unsigned long bnum = 0;
162   int found;
163
164   vol->pnum = pnum;
165
166   if (pnum == 0)
167     {
168       vol->vstart = 0;
169       vol->vlen   = b_size(vol);
170
171       if (vol->vlen == 0)
172         goto fail;
173     }
174   else
175     {
176       while (pnum--)
177         {
178           found = m_findpmentry(vol, "Apple_HFS", &map, &bnum);
179           if (found == -1 || ! found)
180             goto fail;
181         }
182
183       vol->vstart = map.pmPyPartStart;
184       vol->vlen   = map.pmPartBlkCnt;
185
186       if (map.pmDataCnt)
187         {
188           if ((unsigned long) map.pmLgDataStart +
189               (unsigned long) map.pmDataCnt > vol->vlen)
190             ERROR(EINVAL, "partition data overflows partition");
191
192           vol->vstart += (unsigned long) map.pmLgDataStart;
193           vol->vlen    = map.pmDataCnt;
194         }
195
196       if (vol->vlen == 0)
197         ERROR(EINVAL, "volume partition is empty");
198     }
199
200   if (vol->vlen < 800 * (1024 >> HFS_BLOCKSZ_BITS))
201     ERROR(EINVAL, "volume is smaller than 800K");
202
203   return 0;
204
205 fail:
206   return -1;
207 }
208
209 /*
210  * NAME:        vol->readmdb()
211  * DESCRIPTION: load Master Directory Block into memory
212  */
213 int v_readmdb(hfsvol *vol)
214 {
215   if (l_getmdb(vol, &vol->mdb, 0) == -1)
216     goto fail;
217
218   if (vol->mdb.drSigWord != HFS_SIGWORD)
219     {
220       if (vol->mdb.drSigWord == HFS_SIGWORD_MFS)
221         ERROR(EINVAL, "MFS volume format not supported");
222       else
223         ERROR(EINVAL, "not a Macintosh HFS volume");
224     }
225
226   if (vol->mdb.drAlBlkSiz % HFS_BLOCKSZ != 0)
227     ERROR(EINVAL, "bad volume allocation block size");
228
229   vol->lpa = vol->mdb.drAlBlkSiz >> HFS_BLOCKSZ_BITS;
230
231   /* extents pseudo-file structs */
232
233   vol->ext.f.cat.u.fil.filStBlk = vol->mdb.drXTExtRec[0].xdrStABN;
234   vol->ext.f.cat.u.fil.filLgLen = vol->mdb.drXTFlSize;
235   vol->ext.f.cat.u.fil.filPyLen = vol->mdb.drXTFlSize;
236
237   vol->ext.f.cat.u.fil.filCrDat = vol->mdb.drCrDate;
238   vol->ext.f.cat.u.fil.filMdDat = vol->mdb.drLsMod;
239
240   memcpy(&vol->ext.f.cat.u.fil.filExtRec,
241          &vol->mdb.drXTExtRec, sizeof(ExtDataRec));
242
243   f_selectfork(&vol->ext.f, fkData);
244
245   /* catalog pseudo-file structs */
246
247   vol->cat.f.cat.u.fil.filStBlk = vol->mdb.drCTExtRec[0].xdrStABN;
248   vol->cat.f.cat.u.fil.filLgLen = vol->mdb.drCTFlSize;
249   vol->cat.f.cat.u.fil.filPyLen = vol->mdb.drCTFlSize;
250
251   vol->cat.f.cat.u.fil.filCrDat = vol->mdb.drCrDate;
252   vol->cat.f.cat.u.fil.filMdDat = vol->mdb.drLsMod;
253
254   memcpy(&vol->cat.f.cat.u.fil.filExtRec,
255          &vol->mdb.drCTExtRec, sizeof(ExtDataRec));
256
257   f_selectfork(&vol->cat.f, fkData);
258
259   return 0;
260
261 fail:
262   return -1;
263 }
264
265 /*
266  * NAME:        vol->readvbm()
267  * DESCRIPTION: read volume bitmap into memory
268  */
269 int v_readvbm(hfsvol *vol)
270 {
271   unsigned int vbmst = vol->mdb.drVBMSt;
272   unsigned int vbmsz = (vol->mdb.drNmAlBlks + 0x0fff) >> 12;
273   block *bp;
274
275   ASSERT(vol->vbm == 0);
276
277   if (vol->mdb.drAlBlSt - vbmst < vbmsz)
278     ERROR(EIO, "volume bitmap collides with volume data");
279
280   vol->vbm = ALLOC(block, vbmsz);
281   if (vol->vbm == NULL)
282     ERROR(ENOMEM, NULL);
283
284   vol->vbmsz = vbmsz;
285
286   for (bp = vol->vbm; vbmsz--; ++bp)
287     {
288       if (b_readlb(vol, vbmst++, bp) == -1)
289         goto fail;
290     }
291
292   return 0;
293
294 fail:
295   FREE(vol->vbm);
296
297   vol->vbm   = NULL;
298   vol->vbmsz = 0;
299
300   return -1;
301 }
302
303 /*
304  * NAME:        vol->mount()
305  * DESCRIPTION: load volume information into memory
306  */
307 int v_mount(hfsvol *vol)
308 {
309   /* read the MDB, volume bitmap, and extents/catalog B*-tree headers */
310
311   if (v_readmdb(vol) == -1 ||
312       v_readvbm(vol) == -1 ||
313       bt_readhdr(&vol->ext) == -1 ||
314       bt_readhdr(&vol->cat) == -1)
315     goto fail;
316
317   if (vol->mdb.drAtrb & HFS_ATRB_SLOCKED)
318     vol->flags |= HFS_VOL_READONLY;
319   else if (vol->flags & HFS_VOL_READONLY)
320     vol->mdb.drAtrb |= HFS_ATRB_HLOCKED;
321   else
322     vol->mdb.drAtrb &= ~HFS_ATRB_HLOCKED;
323
324   vol->flags |= HFS_VOL_MOUNTED;
325
326   return 0;
327
328 fail:
329   return -1;
330 }
331
332 /*
333  * NAME:        vol->catsearch()
334  * DESCRIPTION: search catalog tree
335  */
336 int v_catsearch(hfsvol *vol, unsigned long parid, const char *name,
337                 CatDataRec *data, char *cname, node *np)
338 {
339   CatKeyRec key;
340   byte pkey[HFS_CATKEYLEN];
341   const byte *ptr;
342   node n;
343   int found;
344
345   if (np == NULL)
346     np = &n;
347
348   r_makecatkey(&key, parid, name);
349   r_packcatkey(&key, pkey, NULL);
350
351   found = bt_search(&vol->cat, pkey, np);
352   if (found <= 0)
353     return found;
354
355   ptr = HFS_NODEREC(*np, np->rnum);
356
357   if (cname)
358     {
359       r_unpackcatkey(ptr, &key);
360       strcpy(cname, key.ckrCName);
361     }
362
363   if (data)
364     r_unpackcatdata(HFS_RECDATA(ptr), data);
365
366   return 1;
367 }
368
369 /*
370  * NAME:        vol->extsearch()
371  * DESCRIPTION: search extents tree
372  */
373 int v_extsearch(hfsfile *file, unsigned int fabn,
374                 ExtDataRec *data, node *np)
375 {
376   ExtKeyRec key;
377   ExtDataRec extsave;
378   unsigned int fabnsave;
379   byte pkey[HFS_EXTKEYLEN];
380   const byte *ptr;
381   node n;
382   int found;
383
384   if (np == NULL)
385     np = &n;
386
387   r_makeextkey(&key, file->fork, file->cat.u.fil.filFlNum, fabn);
388   r_packextkey(&key, pkey, NULL);
389
390   /* in case bt_search() clobbers these */
391
392   memcpy(&extsave, &file->ext, sizeof(ExtDataRec));
393   fabnsave = file->fabn;
394
395   found = bt_search(&file->vol->ext, pkey, np);
396
397   memcpy(&file->ext, &extsave, sizeof(ExtDataRec));
398   file->fabn = fabnsave;
399
400   if (found <= 0)
401     return found;
402
403   if (data)
404     {
405       ptr = HFS_NODEREC(*np, np->rnum);
406       r_unpackextdata(HFS_RECDATA(ptr), data);
407     }
408
409   return 1;
410 }
411
412 /*
413  * NAME:        vol->getthread()
414  * DESCRIPTION: retrieve catalog thread information for a file or directory
415  */
416 int v_getthread(hfsvol *vol, unsigned long id,
417                 CatDataRec *thread, node *np, int type)
418 {
419   CatDataRec rec;
420   int found;
421
422   if (thread == NULL)
423     thread = &rec;
424
425   found = v_catsearch(vol, id, "", thread, NULL, np);
426   if (found == 1 && thread->cdrType != type)
427     ERROR(EIO, "bad thread record");
428
429   return found;
430
431 fail:
432   return -1;
433 }
434
435
436 /*
437  * NAME:        vol->resolve()
438  * DESCRIPTION: translate a pathname; return catalog information
439  */
440 int v_resolve(hfsvol **vol, const char *path,
441               CatDataRec *data, unsigned long *parid, char *fname, node *np)
442 {
443   unsigned long dirid;
444   char name[HFS_MAX_FLEN + 1], *nptr;
445   int found = 0;
446
447   if (*path == 0)
448     ERROR(ENOENT, "empty path");
449
450   if (parid)
451     *parid = 0;
452
453   nptr = strchr(path, ':');
454
455   if (*path == ':' || nptr == NULL)
456     {
457       dirid = (*vol)->cwd;  /* relative path */
458
459       if (*path == ':')
460         ++path;
461
462       if (*path == 0)
463         {
464           found = v_getdthread(*vol, dirid, data, NULL);
465           if (found == -1)
466             goto fail;
467
468           if (found)
469             {
470               if (parid)
471                 *parid = data->u.dthd.thdParID;
472
473               found = v_catsearch(*vol, data->u.dthd.thdParID,
474                                   data->u.dthd.thdCName, data, fname, np);
475               if (found == -1)
476                 goto fail;
477             }
478
479           goto done;
480         }
481     }
482   else
483     {
484       hfsvol *check;
485
486       dirid = HFS_CNID_ROOTPAR;  /* absolute path */
487
488       if (nptr - path > HFS_MAX_VLEN)
489         ERROR(ENAMETOOLONG, NULL);
490
491       strncpy(name, path, nptr - path);
492       name[nptr - path] = 0;
493
494       for (check = hfs_mounts; check; check = check->next)
495         {
496           if (d_relstring(check->mdb.drVN, name) == 0)
497             {
498               *vol = check;
499               break;
500             }
501         }
502     }
503
504   while (1)
505     {
506       while (*path == ':')
507         {
508           ++path;
509
510           found = v_getdthread(*vol, dirid, data, NULL);
511           if (found == -1)
512             goto fail;
513           else if (! found)
514             goto done;
515
516           dirid = data->u.dthd.thdParID;
517         }
518
519       if (*path == 0)
520         {
521           found = v_getdthread(*vol, dirid, data, NULL);
522           if (found == -1)
523             goto fail;
524
525           if (found)
526             {
527               if (parid)
528                 *parid = data->u.dthd.thdParID;
529
530               found = v_catsearch(*vol, data->u.dthd.thdParID,
531                                   data->u.dthd.thdCName, data, fname, np);
532               if (found == -1)
533                 goto fail;
534             }
535
536           goto done;
537         }
538
539       nptr = name;
540       while (nptr < name + sizeof(name) - 1 && *path && *path != ':')
541         *nptr++ = *path++;
542
543       if (*path && *path != ':')
544         ERROR(ENAMETOOLONG, NULL);
545
546       *nptr = 0;
547       if (*path == ':')
548         ++path;
549
550       if (parid)
551         *parid = dirid;
552
553       found = v_catsearch(*vol, dirid, name, data, fname, np);
554       if (found == -1)
555         goto fail;
556
557       if (! found)
558         {
559           if (*path && parid)
560             *parid = 0;
561
562           if (*path == 0 && fname)
563             strcpy(fname, name);
564
565           goto done;
566         }
567
568       switch (data->cdrType)
569         {
570         case cdrDirRec:
571           if (*path == 0)
572             goto done;
573
574           dirid = data->u.dir.dirDirID;
575           break;
576
577         case cdrFilRec:
578           if (*path == 0)
579             goto done;
580
581           ERROR(ENOTDIR, "invalid pathname");
582
583         default:
584           ERROR(EIO, "unexpected catalog record");
585         }
586     }
587
588 done:
589   return found;
590
591 fail:
592   return -1;
593 }
594
595 /* Determine whether the volume is a HFS volume */
596 int
597 v_probe(int fd, long long offset)
598 {
599         MDB *mdb;
600
601         mdb = (MDB*)malloc(2 * 512);
602         os_seek_offset( fd, 2 * 512 + offset );
603         os_read(fd, mdb, 2, 9);
604
605         if (__be16_to_cpu(mdb->drSigWord) != HFS_SIGWORD) {
606                 free(mdb);
607                 return 0;
608         }
609
610         free(mdb);
611         return -1;
612 }