Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / fs / hfs / hfs.c
diff --git a/qemu/roms/openbios/fs/hfs/hfs.c b/qemu/roms/openbios/fs/hfs/hfs.c
new file mode 100644 (file)
index 0000000..0c5fefb
--- /dev/null
@@ -0,0 +1,747 @@
+/*
+ * 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: hfs.c,v 1.15 1998/11/02 22:09:00 rob Exp $
+ */
+
+#include "config.h"
+#include "libhfs.h"
+#include "data.h"
+#include "block.h"
+#include "medium.h"
+#include "file.h"
+#include "btree.h"
+#include "node.h"
+#include "record.h"
+#include "volume.h"
+
+const char *hfs_error = "no error";    /* static error string */
+
+hfsvol *hfs_mounts;                    /* linked list of mounted volumes */
+
+static
+hfsvol *curvol;                                /* current volume */
+
+
+/*
+ * NAME:       getvol()
+ * DESCRIPTION:        validate a volume reference
+ */
+static
+int getvol(hfsvol **vol)
+{
+  if (*vol == NULL)
+    {
+      if (curvol == NULL)
+       ERROR(EINVAL, "no volume is current");
+
+      *vol = curvol;
+    }
+
+  return 0;
+
+fail:
+  return -1;
+}
+
+/* High-Level Volume Routines ============================================== */
+
+/*
+ * NAME:       hfs->mount()
+ * DESCRIPTION:        open an HFS volume; return volume descriptor or 0 (error)
+ */
+hfsvol *hfs_mount( int os_fd, int pnum)
+{
+  hfsvol *vol, *check;
+  int mode = HFS_MODE_RDONLY;
+
+  /* see if the volume is already mounted */
+  for (check = hfs_mounts; check; check = check->next)
+    {
+      if (check->pnum == pnum && v_same(check, os_fd) == 1)
+       {
+           vol = check;
+           goto done;
+       }
+    }
+
+  vol = ALLOC(hfsvol, 1);
+  if (vol == NULL)
+    ERROR(ENOMEM, NULL);
+
+  v_init(vol, mode);
+
+  vol->flags |= HFS_VOL_READONLY;
+  if( v_open(vol, os_fd) == -1 )
+         goto fail;
+
+  /* mount the volume */
+
+  if (v_geometry(vol, pnum) == -1 ||
+      v_mount(vol) == -1)
+    goto fail;
+
+  /* add to linked list of volumes */
+
+  vol->prev = NULL;
+  vol->next = hfs_mounts;
+
+  if (hfs_mounts)
+    hfs_mounts->prev = vol;
+
+  hfs_mounts = vol;
+
+done:
+  ++vol->refs;
+  curvol = vol;
+
+  return vol;
+
+fail:
+  if (vol)
+    {
+      v_close(vol);
+      FREE(vol);
+    }
+
+  return NULL;
+}
+
+
+/*
+ * NAME:       hfs->umount()
+ * DESCRIPTION:        close an HFS volume
+ */
+int hfs_umount(hfsvol *vol)
+{
+  int result = 0;
+
+  if (getvol(&vol) == -1)
+    goto fail;
+
+  if (--vol->refs)
+    {
+      goto done;
+    }
+
+  /* close all open files and directories */
+
+  while (vol->files)
+    {
+      if (hfs_close(vol->files) == -1)
+       result = -1;
+    }
+
+  while (vol->dirs)
+    {
+      if (hfs_closedir(vol->dirs) == -1)
+       result = -1;
+    }
+
+  /* close medium */
+
+  if (v_close(vol) == -1)
+    result = -1;
+
+  /* remove from linked list of volumes */
+
+  if (vol->prev)
+    vol->prev->next = vol->next;
+  if (vol->next)
+    vol->next->prev = vol->prev;
+
+  if (vol == hfs_mounts)
+    hfs_mounts = vol->next;
+  if (vol == curvol)
+    curvol = NULL;
+
+  FREE(vol);
+
+done:
+  return result;
+
+fail:
+  return -1;
+}
+
+/*
+ * NAME:       hfs->umountall()
+ * DESCRIPTION:        unmount all mounted volumes
+ */
+void hfs_umountall(void)
+{
+  while (hfs_mounts)
+    hfs_umount(hfs_mounts);
+}
+
+/*
+ * NAME:       hfs->getvol()
+ * DESCRIPTION:        return a pointer to a mounted volume
+ */
+hfsvol *hfs_getvol(const char *name)
+{
+  hfsvol *vol;
+
+  if (name == NULL)
+    return curvol;
+
+  for (vol = hfs_mounts; vol; vol = vol->next)
+    {
+      if (d_relstring(name, vol->mdb.drVN) == 0)
+       return vol;
+    }
+
+  return NULL;
+}
+
+/*
+ * NAME:       hfs->setvol()
+ * DESCRIPTION:        change the current volume
+ */
+void hfs_setvol(hfsvol *vol)
+{
+  curvol = vol;
+}
+
+/*
+ * NAME:       hfs->vstat()
+ * DESCRIPTION:        return volume statistics
+ */
+int hfs_vstat(hfsvol *vol, hfsvolent *ent)
+{
+  if (getvol(&vol) == -1)
+    goto fail;
+
+  strcpy(ent->name, vol->mdb.drVN);
+
+  ent->flags     = (vol->flags & HFS_VOL_READONLY) ? HFS_ISLOCKED : 0;
+
+  ent->totbytes  = vol->mdb.drNmAlBlks * vol->mdb.drAlBlkSiz;
+  ent->freebytes = vol->mdb.drFreeBks  * vol->mdb.drAlBlkSiz;
+
+  ent->alblocksz = vol->mdb.drAlBlkSiz;
+  ent->clumpsz   = vol->mdb.drClpSiz;
+
+  ent->numfiles  = vol->mdb.drFilCnt;
+  ent->numdirs   = vol->mdb.drDirCnt;
+
+  ent->crdate    = d_ltime(vol->mdb.drCrDate);
+  ent->mddate    = d_ltime(vol->mdb.drLsMod);
+  ent->bkdate    = d_ltime(vol->mdb.drVolBkUp);
+
+  ent->blessed   = vol->mdb.drFndrInfo[0];
+
+  return 0;
+
+fail:
+  return -1;
+}
+
+
+/* High-Level Directory Routines =========================================== */
+
+/*
+ * NAME:       hfs->chdir()
+ * DESCRIPTION:        change current HFS directory
+ */
+int hfs_chdir(hfsvol *vol, const char *path)
+{
+  CatDataRec data;
+
+  if (getvol(&vol) == -1 ||
+      v_resolve(&vol, path, &data, NULL, NULL, NULL) <= 0)
+    goto fail;
+
+  if (data.cdrType != cdrDirRec)
+    ERROR(ENOTDIR, NULL);
+
+  vol->cwd = data.u.dir.dirDirID;
+
+  return 0;
+
+fail:
+  return -1;
+}
+
+/*
+ * NAME:       hfs->getcwd()
+ * DESCRIPTION:        return the current working directory ID
+ */
+unsigned long hfs_getcwd(hfsvol *vol)
+{
+  if (getvol(&vol) == -1)
+    return 0;
+
+  return vol->cwd;
+}
+
+/*
+ * NAME:       hfs->setcwd()
+ * DESCRIPTION:        set the current working directory ID
+ */
+int hfs_setcwd(hfsvol *vol, unsigned long id)
+{
+  if (getvol(&vol) == -1)
+    goto fail;
+
+  if (id == vol->cwd)
+    goto done;
+
+  /* make sure the directory exists */
+
+  if (v_getdthread(vol, id, NULL, NULL) <= 0)
+    goto fail;
+
+  vol->cwd = id;
+
+done:
+  return 0;
+
+fail:
+  return -1;
+}
+
+/*
+ * NAME:       hfs->dirinfo()
+ * DESCRIPTION:        given a directory ID, return its (name and) parent ID
+ */
+int hfs_dirinfo(hfsvol *vol, unsigned long *id, char *name)
+{
+  CatDataRec thread;
+
+  if (getvol(&vol) == -1 ||
+      v_getdthread(vol, *id, &thread, NULL) <= 0)
+    goto fail;
+
+  *id = thread.u.dthd.thdParID;
+
+  if (name)
+    strcpy(name, thread.u.dthd.thdCName);
+
+  return 0;
+
+fail:
+  return -1;
+}
+
+/*
+ * NAME:       hfs->opendir()
+ * DESCRIPTION:        prepare to read the contents of a directory
+ */
+hfsdir *hfs_opendir(hfsvol *vol, const char *path)
+{
+  hfsdir *dir = NULL;
+  CatKeyRec key;
+  CatDataRec data;
+  byte pkey[HFS_CATKEYLEN];
+
+  if (getvol(&vol) == -1)
+    goto fail;
+
+  dir = ALLOC(hfsdir, 1);
+  if (dir == NULL)
+    ERROR(ENOMEM, NULL);
+
+  dir->vol = vol;
+
+  if (*path == 0)
+    {
+      /* meta-directory containing root dirs from all mounted volumes */
+
+      dir->dirid = 0;
+      dir->vptr  = hfs_mounts;
+    }
+  else
+    {
+      if (v_resolve(&vol, path, &data, NULL, NULL, NULL) <= 0)
+       goto fail;
+
+      if (data.cdrType != cdrDirRec)
+        ERROR(ENOTDIR, NULL);
+
+      dir->dirid = data.u.dir.dirDirID;
+      dir->vptr  = NULL;
+
+      r_makecatkey(&key, dir->dirid, "");
+      r_packcatkey(&key, pkey, NULL);
+
+      if (bt_search(&vol->cat, pkey, &dir->n) <= 0)
+       goto fail;
+    }
+
+  dir->prev = NULL;
+  dir->next = vol->dirs;
+
+  if (vol->dirs)
+    vol->dirs->prev = dir;
+
+  vol->dirs = dir;
+
+  return dir;
+
+fail:
+  FREE(dir);
+  return NULL;
+}
+
+/*
+ * NAME:       hfs->readdir()
+ * DESCRIPTION:        return the next entry in the directory
+ */
+int hfs_readdir(hfsdir *dir, hfsdirent *ent)
+{
+  CatKeyRec key;
+  CatDataRec data;
+  const byte *ptr;
+
+  if (dir->dirid == 0)
+    {
+      hfsvol *vol;
+      char cname[HFS_MAX_FLEN + 1];
+
+      for (vol = hfs_mounts; vol; vol = vol->next)
+       {
+         if (vol == dir->vptr)
+           break;
+       }
+
+      if (vol == NULL)
+       ERROR(ENOENT, "no more entries");
+
+      if (v_getdthread(vol, HFS_CNID_ROOTDIR, &data, NULL) <= 0 ||
+         v_catsearch(vol, HFS_CNID_ROOTPAR, data.u.dthd.thdCName,
+                      &data, cname, NULL) <= 0)
+       goto fail;
+
+      r_unpackdirent(HFS_CNID_ROOTPAR, cname, &data, ent);
+
+      dir->vptr = vol->next;
+
+      goto done;
+    }
+
+  if (dir->n.rnum == -1)
+    ERROR(ENOENT, "no more entries");
+
+  while (1)
+    {
+      ++dir->n.rnum;
+
+      while (dir->n.rnum >= dir->n.nd.ndNRecs)
+       {
+         if (dir->n.nd.ndFLink == 0)
+           {
+             dir->n.rnum = -1;
+             ERROR(ENOENT, "no more entries");
+           }
+
+         if (bt_getnode(&dir->n, dir->n.bt, dir->n.nd.ndFLink) == -1)
+           {
+             dir->n.rnum = -1;
+             goto fail;
+           }
+
+         dir->n.rnum = 0;
+       }
+
+      ptr = HFS_NODEREC(dir->n, dir->n.rnum);
+
+      r_unpackcatkey(ptr, &key);
+
+      if (key.ckrParID != dir->dirid)
+       {
+         dir->n.rnum = -1;
+         ERROR(ENOENT, "no more entries");
+       }
+
+      r_unpackcatdata(HFS_RECDATA(ptr), &data);
+
+      switch (data.cdrType)
+       {
+       case cdrDirRec:
+       case cdrFilRec:
+         r_unpackdirent(key.ckrParID, key.ckrCName, &data, ent);
+         goto done;
+
+       case cdrThdRec:
+       case cdrFThdRec:
+         break;
+
+       default:
+         dir->n.rnum = -1;
+         ERROR(EIO, "unexpected directory entry found");
+       }
+    }
+
+done:
+  return 0;
+
+fail:
+  return -1;
+}
+
+/*
+ * NAME:       hfs->closedir()
+ * DESCRIPTION:        stop reading a directory
+ */
+int hfs_closedir(hfsdir *dir)
+{
+  hfsvol *vol = dir->vol;
+
+  if (dir->prev)
+    dir->prev->next = dir->next;
+  if (dir->next)
+    dir->next->prev = dir->prev;
+  if (dir == vol->dirs)
+    vol->dirs = dir->next;
+
+  FREE(dir);
+
+  return 0;
+}
+
+/* High-Level File Routines ================================================ */
+
+/*
+ * NAME:       hfs->open()
+ * DESCRIPTION:        prepare a file for I/O
+ */
+hfsfile *hfs_open(hfsvol *vol, const char *path)
+{
+  hfsfile *file = NULL;
+
+  if (getvol(&vol) == -1)
+    goto fail;
+
+  file = ALLOC(hfsfile, 1);
+  if (file == NULL)
+    ERROR(ENOMEM, NULL);
+
+  if (v_resolve(&vol, path, &file->cat, &file->parid, file->name, NULL) <= 0)
+    goto fail;
+
+  if (file->cat.cdrType != cdrFilRec)
+    ERROR(EISDIR, NULL);
+
+  /* package file handle for user */
+
+  file->vol   = vol;
+  file->flags = 0;
+
+  f_selectfork(file, fkData);
+
+  file->prev = NULL;
+  file->next = vol->files;
+
+  if (vol->files)
+    vol->files->prev = file;
+
+  vol->files = file;
+
+  return file;
+
+fail:
+  FREE(file);
+  return NULL;
+}
+
+/*
+ * NAME:       hfs->setfork()
+ * DESCRIPTION:        select file fork for I/O operations
+ */
+int hfs_setfork(hfsfile *file, int fork)
+{
+  int result = 0;
+
+  f_selectfork(file, fork ? fkRsrc : fkData);
+
+  return result;
+}
+
+/*
+ * NAME:       hfs->getfork()
+ * DESCRIPTION:        return the current fork for I/O operations
+ */
+int hfs_getfork(hfsfile *file)
+{
+  return file->fork != fkData;
+}
+
+/*
+ * NAME:       hfs->read()
+ * DESCRIPTION:        read from an open file
+ */
+unsigned long hfs_read(hfsfile *file, void *buf, unsigned long len)
+{
+  unsigned long *lglen, count;
+  byte *ptr = buf;
+
+  f_getptrs(file, NULL, &lglen, NULL);
+
+  if (file->pos + len > *lglen)
+    len = *lglen - file->pos;
+
+  count = len;
+  while (count)
+    {
+      unsigned long bnum, offs, chunk;
+
+      bnum  = file->pos >> HFS_BLOCKSZ_BITS;
+      offs  = file->pos & (HFS_BLOCKSZ - 1);
+
+      chunk = HFS_BLOCKSZ - offs;
+      if (chunk > count)
+       chunk = count;
+
+      if (offs == 0 && chunk == HFS_BLOCKSZ)
+       {
+         if (f_getblock(file, bnum, (block *) ptr) == -1)
+           goto fail;
+       }
+      else
+       {
+         block b;
+
+         if (f_getblock(file, bnum, &b) == -1)
+           goto fail;
+
+         memcpy(ptr, b + offs, chunk);
+       }
+
+      ptr += chunk;
+
+      file->pos += chunk;
+      count     -= chunk;
+    }
+
+  return len;
+
+fail:
+  return -1;
+}
+
+/*
+ * NAME:       hfs->seek()
+ * DESCRIPTION:        change file seek pointer
+ */
+unsigned long hfs_seek(hfsfile *file, long offset, int from)
+{
+  unsigned long *lglen, newpos;
+
+  f_getptrs(file, NULL, &lglen, NULL);
+
+  switch (from)
+    {
+    case HFS_SEEK_SET:
+      newpos = (offset < 0) ? 0 : offset;
+      break;
+
+    case HFS_SEEK_CUR:
+      if (offset < 0 && (unsigned long) -offset > file->pos)
+       newpos = 0;
+      else
+       newpos = file->pos + offset;
+      break;
+
+    case HFS_SEEK_END:
+      if (offset < 0 && (unsigned long) -offset > *lglen)
+       newpos = 0;
+      else
+       newpos = *lglen + offset;
+      break;
+
+    default:
+      ERROR(EINVAL, NULL);
+    }
+
+  if (newpos > *lglen)
+    newpos = *lglen;
+
+  file->pos = newpos;
+
+  return newpos;
+
+fail:
+  return -1;
+}
+
+/*
+ * NAME:       hfs->close()
+ * DESCRIPTION:        close a file
+ */
+int hfs_close(hfsfile *file)
+{
+  hfsvol *vol = file->vol;
+  int result = 0;
+
+  if (file->prev)
+    file->prev->next = file->next;
+  if (file->next)
+    file->next->prev = file->prev;
+  if (file == vol->files)
+    vol->files = file->next;
+
+  FREE(file);
+
+  return result;
+}
+
+/* High-Level Catalog Routines ============================================= */
+
+/*
+ * NAME:       hfs->stat()
+ * DESCRIPTION:        return catalog information for an arbitrary path
+ */
+int hfs_stat(hfsvol *vol, const char *path, hfsdirent *ent)
+{
+  CatDataRec data;
+  unsigned long parid;
+  char name[HFS_MAX_FLEN + 1];
+
+  if (getvol(&vol) == -1 ||
+      v_resolve(&vol, path, &data, &parid, name, NULL) <= 0)
+    goto fail;
+
+  r_unpackdirent(parid, name, &data, ent);
+
+  return 0;
+
+fail:
+  return -1;
+}
+
+/*
+ * NAME:       hfs->fstat()
+ * DESCRIPTION:        return catalog information for an open file
+ */
+int hfs_fstat(hfsfile *file, hfsdirent *ent)
+{
+  r_unpackdirent(file->parid, file->name, &file->cat, ent);
+
+  return 0;
+}
+
+/*
+ * NAME:       hfs->probe()
+ * DESCRIPTION:        return whether a HFS filesystem is present at the given offset
+ */
+int hfs_probe(int fd, long long offset)
+{
+  return v_probe(fd, offset);
+}