Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / fs / hfs / volume.c
diff --git a/qemu/roms/openbios/fs/hfs/volume.c b/qemu/roms/openbios/fs/hfs/volume.c
new file mode 100644 (file)
index 0000000..4faf7e6
--- /dev/null
@@ -0,0 +1,612 @@
+/*
+ * libhfs - library for reading and writing Macintosh HFS volumes
+ * Copyright (C) 1996-1998 Robert Leslie
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * $Id: volume.c,v 1.12 1998/11/02 22:09:10 rob Exp $
+ */
+
+#include "config.h"
+#include "libhfs.h"
+#include "volume.h"
+#include "data.h"
+#include "block.h"
+#include "low.h"
+#include "medium.h"
+#include "file.h"
+#include "btree.h"
+#include "record.h"
+#include "os.h"
+
+#include "libc/byteorder.h"
+
+/*
+ * NAME:       vol->init()
+ * DESCRIPTION:        initialize volume structure
+ */
+void v_init(hfsvol *vol, int flags)
+{
+  btree *ext = &vol->ext;
+  btree *cat = &vol->cat;
+
+  vol->os_fd       = 0;
+  vol->flags      = flags & HFS_VOL_OPT_MASK;
+
+  vol->pnum       = -1;
+  vol->vstart     = 0;
+  vol->vlen       = 0;
+  vol->lpa        = 0;
+
+  vol->cache      = NULL;
+
+  vol->vbm        = NULL;
+  vol->vbmsz      = 0;
+
+  f_init(&ext->f, vol, HFS_CNID_EXT, "extents overflow");
+
+  ext->map        = NULL;
+  ext->mapsz      = 0;
+  ext->flags      = 0;
+
+  ext->keyunpack  = (keyunpackfunc)  r_unpackextkey;
+  ext->keycompare = (keycomparefunc) r_compareextkeys;
+
+  f_init(&cat->f, vol, HFS_CNID_CAT, "catalog");
+
+  cat->map        = NULL;
+  cat->mapsz      = 0;
+  cat->flags      = 0;
+
+  cat->keyunpack  = (keyunpackfunc)  r_unpackcatkey;
+  cat->keycompare = (keycomparefunc) r_comparecatkeys;
+
+  vol->cwd        = HFS_CNID_ROOTDIR;
+
+  vol->refs       = 0;
+  vol->files      = NULL;
+  vol->dirs       = NULL;
+
+  vol->prev       = NULL;
+  vol->next       = NULL;
+}
+
+/*
+ * NAME:       vol->open()
+ * DESCRIPTION:        open volume source and lock against concurrent updates
+ */
+int v_open(hfsvol *vol, int os_fd )
+{
+  if (vol->flags & HFS_VOL_OPEN)
+    ERROR(EINVAL, "volume already open");
+
+  vol->flags |= HFS_VOL_OPEN;
+  vol->os_fd = os_fd;
+
+  /* initialize volume block cache (OK to fail) */
+
+  if (! (vol->flags & HFS_OPT_NOCACHE) &&
+      b_init(vol) != -1)
+    vol->flags |= HFS_VOL_USINGCACHE;
+
+  return 0;
+
+fail:
+  return -1;
+}
+
+/*
+ * NAME:       vol->close()
+ * DESCRIPTION:        close access path to volume source
+ */
+int v_close(hfsvol *vol)
+{
+  int result = 0;
+
+  if (! (vol->flags & HFS_VOL_OPEN))
+    goto done;
+
+  if ((vol->flags & HFS_VOL_USINGCACHE) &&
+      b_finish(vol) == -1)
+    result = -1;
+
+  vol->flags &= ~(HFS_VOL_OPEN | HFS_VOL_MOUNTED | HFS_VOL_USINGCACHE);
+
+  /* free dynamically allocated structures */
+
+  FREE(vol->vbm);
+
+  vol->vbm   = NULL;
+  vol->vbmsz = 0;
+
+  FREE(vol->ext.map);
+  FREE(vol->cat.map);
+
+  vol->ext.map = NULL;
+  vol->cat.map = NULL;
+
+done:
+  return result;
+}
+
+/*
+ * NAME:       vol->same()
+ * DESCRIPTION:        return 1 iff path is same as open volume
+ */
+int v_same(hfsvol *vol, int os_fd )
+{
+  return vol->os_fd == os_fd;
+}
+
+/*
+ * NAME:       vol->geometry()
+ * DESCRIPTION:        determine volume location and size (possibly in a partition)
+ */
+int v_geometry(hfsvol *vol, int pnum)
+{
+  Partition map;
+  unsigned long bnum = 0;
+  int found;
+
+  vol->pnum = pnum;
+
+  if (pnum == 0)
+    {
+      vol->vstart = 0;
+      vol->vlen   = b_size(vol);
+
+      if (vol->vlen == 0)
+       goto fail;
+    }
+  else
+    {
+      while (pnum--)
+       {
+         found = m_findpmentry(vol, "Apple_HFS", &map, &bnum);
+         if (found == -1 || ! found)
+           goto fail;
+       }
+
+      vol->vstart = map.pmPyPartStart;
+      vol->vlen   = map.pmPartBlkCnt;
+
+      if (map.pmDataCnt)
+       {
+         if ((unsigned long) map.pmLgDataStart +
+             (unsigned long) map.pmDataCnt > vol->vlen)
+           ERROR(EINVAL, "partition data overflows partition");
+
+         vol->vstart += (unsigned long) map.pmLgDataStart;
+         vol->vlen    = map.pmDataCnt;
+       }
+
+      if (vol->vlen == 0)
+       ERROR(EINVAL, "volume partition is empty");
+    }
+
+  if (vol->vlen < 800 * (1024 >> HFS_BLOCKSZ_BITS))
+    ERROR(EINVAL, "volume is smaller than 800K");
+
+  return 0;
+
+fail:
+  return -1;
+}
+
+/*
+ * NAME:       vol->readmdb()
+ * DESCRIPTION:        load Master Directory Block into memory
+ */
+int v_readmdb(hfsvol *vol)
+{
+  if (l_getmdb(vol, &vol->mdb, 0) == -1)
+    goto fail;
+
+  if (vol->mdb.drSigWord != HFS_SIGWORD)
+    {
+      if (vol->mdb.drSigWord == HFS_SIGWORD_MFS)
+       ERROR(EINVAL, "MFS volume format not supported");
+      else
+       ERROR(EINVAL, "not a Macintosh HFS volume");
+    }
+
+  if (vol->mdb.drAlBlkSiz % HFS_BLOCKSZ != 0)
+    ERROR(EINVAL, "bad volume allocation block size");
+
+  vol->lpa = vol->mdb.drAlBlkSiz >> HFS_BLOCKSZ_BITS;
+
+  /* extents pseudo-file structs */
+
+  vol->ext.f.cat.u.fil.filStBlk = vol->mdb.drXTExtRec[0].xdrStABN;
+  vol->ext.f.cat.u.fil.filLgLen = vol->mdb.drXTFlSize;
+  vol->ext.f.cat.u.fil.filPyLen = vol->mdb.drXTFlSize;
+
+  vol->ext.f.cat.u.fil.filCrDat = vol->mdb.drCrDate;
+  vol->ext.f.cat.u.fil.filMdDat = vol->mdb.drLsMod;
+
+  memcpy(&vol->ext.f.cat.u.fil.filExtRec,
+        &vol->mdb.drXTExtRec, sizeof(ExtDataRec));
+
+  f_selectfork(&vol->ext.f, fkData);
+
+  /* catalog pseudo-file structs */
+
+  vol->cat.f.cat.u.fil.filStBlk = vol->mdb.drCTExtRec[0].xdrStABN;
+  vol->cat.f.cat.u.fil.filLgLen = vol->mdb.drCTFlSize;
+  vol->cat.f.cat.u.fil.filPyLen = vol->mdb.drCTFlSize;
+
+  vol->cat.f.cat.u.fil.filCrDat = vol->mdb.drCrDate;
+  vol->cat.f.cat.u.fil.filMdDat = vol->mdb.drLsMod;
+
+  memcpy(&vol->cat.f.cat.u.fil.filExtRec,
+        &vol->mdb.drCTExtRec, sizeof(ExtDataRec));
+
+  f_selectfork(&vol->cat.f, fkData);
+
+  return 0;
+
+fail:
+  return -1;
+}
+
+/*
+ * NAME:       vol->readvbm()
+ * DESCRIPTION:        read volume bitmap into memory
+ */
+int v_readvbm(hfsvol *vol)
+{
+  unsigned int vbmst = vol->mdb.drVBMSt;
+  unsigned int vbmsz = (vol->mdb.drNmAlBlks + 0x0fff) >> 12;
+  block *bp;
+
+  ASSERT(vol->vbm == 0);
+
+  if (vol->mdb.drAlBlSt - vbmst < vbmsz)
+    ERROR(EIO, "volume bitmap collides with volume data");
+
+  vol->vbm = ALLOC(block, vbmsz);
+  if (vol->vbm == NULL)
+    ERROR(ENOMEM, NULL);
+
+  vol->vbmsz = vbmsz;
+
+  for (bp = vol->vbm; vbmsz--; ++bp)
+    {
+      if (b_readlb(vol, vbmst++, bp) == -1)
+       goto fail;
+    }
+
+  return 0;
+
+fail:
+  FREE(vol->vbm);
+
+  vol->vbm   = NULL;
+  vol->vbmsz = 0;
+
+  return -1;
+}
+
+/*
+ * NAME:       vol->mount()
+ * DESCRIPTION:        load volume information into memory
+ */
+int v_mount(hfsvol *vol)
+{
+  /* read the MDB, volume bitmap, and extents/catalog B*-tree headers */
+
+  if (v_readmdb(vol) == -1 ||
+      v_readvbm(vol) == -1 ||
+      bt_readhdr(&vol->ext) == -1 ||
+      bt_readhdr(&vol->cat) == -1)
+    goto fail;
+
+  if (vol->mdb.drAtrb & HFS_ATRB_SLOCKED)
+    vol->flags |= HFS_VOL_READONLY;
+  else if (vol->flags & HFS_VOL_READONLY)
+    vol->mdb.drAtrb |= HFS_ATRB_HLOCKED;
+  else
+    vol->mdb.drAtrb &= ~HFS_ATRB_HLOCKED;
+
+  vol->flags |= HFS_VOL_MOUNTED;
+
+  return 0;
+
+fail:
+  return -1;
+}
+
+/*
+ * NAME:       vol->catsearch()
+ * DESCRIPTION:        search catalog tree
+ */
+int v_catsearch(hfsvol *vol, unsigned long parid, const char *name,
+               CatDataRec *data, char *cname, node *np)
+{
+  CatKeyRec key;
+  byte pkey[HFS_CATKEYLEN];
+  const byte *ptr;
+  node n;
+  int found;
+
+  if (np == NULL)
+    np = &n;
+
+  r_makecatkey(&key, parid, name);
+  r_packcatkey(&key, pkey, NULL);
+
+  found = bt_search(&vol->cat, pkey, np);
+  if (found <= 0)
+    return found;
+
+  ptr = HFS_NODEREC(*np, np->rnum);
+
+  if (cname)
+    {
+      r_unpackcatkey(ptr, &key);
+      strcpy(cname, key.ckrCName);
+    }
+
+  if (data)
+    r_unpackcatdata(HFS_RECDATA(ptr), data);
+
+  return 1;
+}
+
+/*
+ * NAME:       vol->extsearch()
+ * DESCRIPTION:        search extents tree
+ */
+int v_extsearch(hfsfile *file, unsigned int fabn,
+               ExtDataRec *data, node *np)
+{
+  ExtKeyRec key;
+  ExtDataRec extsave;
+  unsigned int fabnsave;
+  byte pkey[HFS_EXTKEYLEN];
+  const byte *ptr;
+  node n;
+  int found;
+
+  if (np == NULL)
+    np = &n;
+
+  r_makeextkey(&key, file->fork, file->cat.u.fil.filFlNum, fabn);
+  r_packextkey(&key, pkey, NULL);
+
+  /* in case bt_search() clobbers these */
+
+  memcpy(&extsave, &file->ext, sizeof(ExtDataRec));
+  fabnsave = file->fabn;
+
+  found = bt_search(&file->vol->ext, pkey, np);
+
+  memcpy(&file->ext, &extsave, sizeof(ExtDataRec));
+  file->fabn = fabnsave;
+
+  if (found <= 0)
+    return found;
+
+  if (data)
+    {
+      ptr = HFS_NODEREC(*np, np->rnum);
+      r_unpackextdata(HFS_RECDATA(ptr), data);
+    }
+
+  return 1;
+}
+
+/*
+ * NAME:       vol->getthread()
+ * DESCRIPTION:        retrieve catalog thread information for a file or directory
+ */
+int v_getthread(hfsvol *vol, unsigned long id,
+               CatDataRec *thread, node *np, int type)
+{
+  CatDataRec rec;
+  int found;
+
+  if (thread == NULL)
+    thread = &rec;
+
+  found = v_catsearch(vol, id, "", thread, NULL, np);
+  if (found == 1 && thread->cdrType != type)
+    ERROR(EIO, "bad thread record");
+
+  return found;
+
+fail:
+  return -1;
+}
+
+
+/*
+ * NAME:       vol->resolve()
+ * DESCRIPTION:        translate a pathname; return catalog information
+ */
+int v_resolve(hfsvol **vol, const char *path,
+              CatDataRec *data, unsigned long *parid, char *fname, node *np)
+{
+  unsigned long dirid;
+  char name[HFS_MAX_FLEN + 1], *nptr;
+  int found = 0;
+
+  if (*path == 0)
+    ERROR(ENOENT, "empty path");
+
+  if (parid)
+    *parid = 0;
+
+  nptr = strchr(path, ':');
+
+  if (*path == ':' || nptr == NULL)
+    {
+      dirid = (*vol)->cwd;  /* relative path */
+
+      if (*path == ':')
+       ++path;
+
+      if (*path == 0)
+       {
+          found = v_getdthread(*vol, dirid, data, NULL);
+         if (found == -1)
+           goto fail;
+
+         if (found)
+           {
+             if (parid)
+               *parid = data->u.dthd.thdParID;
+
+             found = v_catsearch(*vol, data->u.dthd.thdParID,
+                                 data->u.dthd.thdCName, data, fname, np);
+             if (found == -1)
+               goto fail;
+           }
+
+         goto done;
+       }
+    }
+  else
+    {
+      hfsvol *check;
+
+      dirid = HFS_CNID_ROOTPAR;  /* absolute path */
+
+      if (nptr - path > HFS_MAX_VLEN)
+        ERROR(ENAMETOOLONG, NULL);
+
+      strncpy(name, path, nptr - path);
+      name[nptr - path] = 0;
+
+      for (check = hfs_mounts; check; check = check->next)
+       {
+         if (d_relstring(check->mdb.drVN, name) == 0)
+           {
+             *vol = check;
+             break;
+           }
+       }
+    }
+
+  while (1)
+    {
+      while (*path == ':')
+       {
+         ++path;
+
+          found = v_getdthread(*vol, dirid, data, NULL);
+         if (found == -1)
+           goto fail;
+         else if (! found)
+           goto done;
+
+         dirid = data->u.dthd.thdParID;
+       }
+
+      if (*path == 0)
+       {
+          found = v_getdthread(*vol, dirid, data, NULL);
+         if (found == -1)
+           goto fail;
+
+         if (found)
+           {
+             if (parid)
+               *parid = data->u.dthd.thdParID;
+
+             found = v_catsearch(*vol, data->u.dthd.thdParID,
+                                 data->u.dthd.thdCName, data, fname, np);
+             if (found == -1)
+               goto fail;
+           }
+
+         goto done;
+       }
+
+      nptr = name;
+      while (nptr < name + sizeof(name) - 1 && *path && *path != ':')
+       *nptr++ = *path++;
+
+      if (*path && *path != ':')
+        ERROR(ENAMETOOLONG, NULL);
+
+      *nptr = 0;
+      if (*path == ':')
+       ++path;
+
+      if (parid)
+       *parid = dirid;
+
+      found = v_catsearch(*vol, dirid, name, data, fname, np);
+      if (found == -1)
+       goto fail;
+
+      if (! found)
+       {
+         if (*path && parid)
+           *parid = 0;
+
+         if (*path == 0 && fname)
+           strcpy(fname, name);
+
+         goto done;
+       }
+
+      switch (data->cdrType)
+       {
+       case cdrDirRec:
+         if (*path == 0)
+           goto done;
+
+         dirid = data->u.dir.dirDirID;
+         break;
+
+       case cdrFilRec:
+         if (*path == 0)
+           goto done;
+
+         ERROR(ENOTDIR, "invalid pathname");
+
+       default:
+         ERROR(EIO, "unexpected catalog record");
+       }
+    }
+
+done:
+  return found;
+
+fail:
+  return -1;
+}
+
+/* Determine whether the volume is a HFS volume */
+int
+v_probe(int fd, long long offset)
+{
+       MDB *mdb;
+
+       mdb = (MDB*)malloc(2 * 512);
+       os_seek_offset( fd, 2 * 512 + offset );
+       os_read(fd, mdb, 2, 9);
+
+       if (__be16_to_cpu(mdb->drSigWord) != HFS_SIGWORD) {
+               free(mdb);
+               return 0;
+       }
+
+       free(mdb);
+       return -1;
+}