Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / fs / hfsplus / hfsp_volume.c
diff --git a/qemu/roms/openbios/fs/hfsplus/hfsp_volume.c b/qemu/roms/openbios/fs/hfsplus/hfsp_volume.c
new file mode 100644 (file)
index 0000000..2d624e2
--- /dev/null
@@ -0,0 +1,323 @@
+/*
+ * libhfs - library for reading and writing Macintosh HFS volumes
+ *
+ * Code to acces the basic volume information of a HFS+ volume.
+ *
+ * Copyright (C) 2000 Klaus Halfmann <khalfmann@libra.de>
+ * Original work by 1996-1998 Robert Leslie <rob@mars.org>
+ * other work 2000 from Brad Boyer (flar@pants.nu)
+ *
+ * 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.21 2000/10/25 05:43:04 hasi Exp $
+ */
+
+#include "config.h"
+#include "libhfsp.h"
+#include "volume.h"
+#include "record.h"
+#include "btree.h"
+#include "blockiter.h"
+#include "os.h"
+#include "swab.h"
+#include "hfstime.h"
+
+
+/* Fill a given buffer with the given block in volume.
+ */
+int
+volume_readinbuf(volume * vol,void* buf, long block)
+{
+       UInt16 blksize_bits;
+       ASSERT( block < vol->maxblocks);
+
+       blksize_bits = vol->blksize_bits;
+       block   += vol->startblock;
+       if( os_seek(vol->os_fd, block, blksize_bits) == block)
+               if( 1 == os_read(vol->os_fd, buf, 1, blksize_bits))
+                       return 0;
+       return -1;
+}
+
+/* read multiple blocks into given memory.
+ *
+ * returns given pinter or NULL on failure.
+ */
+void*
+volume_readfromfork(volume* vol, void* buf,
+               hfsp_fork_raw* f, UInt32 block,
+               UInt32 count, UInt8 forktype, UInt32 fileId)
+{
+       blockiter iter;
+       char *cbuf = buf;
+
+       blockiter_init(&iter, vol, f, forktype, fileId);
+       if( blockiter_skip(&iter, block))
+               return NULL;
+
+       while( count > 0) {
+               --count;
+               if( volume_readinbuf(vol, cbuf, blockiter_curr(&iter)))
+                       return NULL;
+               cbuf += vol->blksize;
+               if( count > 0 && blockiter_next(&iter))
+                       return NULL;
+       }
+       return buf;
+}
+
+
+/* Read a raw hfsp_extent_rec from memory.
+ *
+ * return pointer right after the structure.
+ */
+void*
+volume_readextent(void *p, hfsp_extent_rec er)
+{
+       int             i;
+       hfsp_extent     *e;
+
+       for( i=0; i < 8; i++) {
+               e = &er[i];
+               e->start_block = bswabU32_inc(p);
+               e->block_count = bswabU32_inc(p);
+       }
+       return p;
+}
+
+/* Read a raw hfsp_fork from memory.
+ *
+ * return pointer right after the structure.
+ */
+void*
+volume_readfork(void *p, hfsp_fork_raw* f)
+{
+       f->total_size   = bswabU64_inc(p);
+       f->clump_size   = bswabU32_inc(p);
+       f->total_blocks = bswabU32_inc(p);
+
+       return volume_readextent(p, f->extents);
+}
+
+/* Read the volume from the given buffer and swap the bytes.
+ *
+ * ToDo: add more consitency checks.
+ */
+static int
+volume_readbuf(hfsp_vh* vh, char * p)
+{
+       if(  (vh->signature = bswabU16_inc(p)) != HFSP_VOLHEAD_SIG)
+               HFSP_ERROR(-1, "This is not a HFS+ volume");
+
+       vh->version             = bswabU16_inc(p);
+       vh->attributes          = bswabU32_inc(p);
+       vh->last_mount_vers     = bswabU32_inc(p);
+       vh->reserved            = bswabU32_inc(p);
+       vh->create_date         = bswabU32_inc(p);
+       vh->modify_date         = bswabU32_inc(p);
+       vh->backup_date         = bswabU32_inc(p);
+       vh->checked_date        = bswabU32_inc(p);
+       vh->file_count          = bswabU32_inc(p);
+       vh->folder_count        = bswabU32_inc(p);
+       vh->blocksize           = bswabU32_inc(p);
+       vh->total_blocks        = bswabU32_inc(p);
+       vh->free_blocks         = bswabU32_inc(p);
+       vh->next_alloc          = bswabU32_inc(p);
+       vh->rsrc_clump_sz       = bswabU32_inc(p);
+       vh->data_clump_sz       = bswabU32_inc(p);
+       vh->next_cnid           = bswabU32_inc(p);
+       vh->write_count         = bswabU32_inc(p);
+       vh->encodings_bmp       = bswabU64_inc(p);
+       memcpy(vh->finder_info, p, 32);
+       p += 32; // So finderinfo must be swapped later, ***
+       p = volume_readfork(p, &vh->alloc_file );
+       p = volume_readfork(p, &vh->ext_file   );
+       p = volume_readfork(p, &vh->cat_file   );
+       p = volume_readfork(p, &vh->attr_file  );
+        volume_readfork(p, &vh->start_file );
+       return 0;
+  fail:
+       return -1;
+}
+
+/* Read the volume from the given block */
+static int
+volume_read(volume * vol, hfsp_vh* vh, UInt32 block)
+{
+       char buf[vol->blksize];
+
+       if( volume_readinbuf(vol, buf, block))
+               return -1;
+        return volume_readbuf(vh, buf);
+}
+
+/* Find out wether the volume is wrapped and unwrap it eventually */
+static int
+volume_read_wrapper(volume * vol, hfsp_vh* vh)
+{
+       UInt16  signature;
+       char    buf[vol->blksize];
+        char    *p = buf;
+       int     ret;
+       UInt64  vol_size;
+       
+       if( volume_readinbuf(vol, buf, 2) ) // Wrapper or volume header starts here
+               return -1;
+
+       signature = bswabU16_inc(p);
+       if( signature == HFS_VOLHEAD_SIG) {             /* Wrapper */
+               UInt32  drAlBlkSiz;                     /* size (in bytes) of allocation blocks */
+               UInt32  sect_per_block;                 /* how may block build an hfs sector */
+               UInt16  drAlBlSt;                       /* first allocation block in volume */
+               UInt16  embeds, embedl;                 /* Start/lenght of embedded area in blocks */
+
+               p += 0x12;                      /* skip unneded HFS vol fields */
+               drAlBlkSiz = bswabU32_inc(p);           /* offset 0x14 */
+               p += 0x4;                       /* skip unneded HFS vol fields */
+               drAlBlSt = bswabU16_inc(p);             /* offset 0x1C */
+
+               p += 0x5E;                      /* skip unneded HFS vol fields */
+               signature = bswabU16_inc(p);            /* offset 0x7C, drEmbedSigWord */
+               if( signature != HFSP_VOLHEAD_SIG)
+                       HFSP_ERROR(-1, "This looks like a normal HFS volume");
+               embeds = bswabU16_inc(p);
+               embedl = bswabU16_inc(p);
+               sect_per_block =  (drAlBlkSiz / HFSP_BLOCKSZ);
+               // end is absolute (not relative to HFS+ start)
+               vol->maxblocks = embedl * sect_per_block;
+               vol->startblock = drAlBlSt + embeds * sect_per_block;
+               /* Now we can try to read the embedded HFS+ volume header */
+               return volume_read(vol,vh,2);
+       }
+       else if( signature == HFSP_VOLHEAD_SIG) { /* Native HFS+ volume */
+               p = buf; // Restore to begin of block
+                ret = volume_readbuf(vh, p);
+               if( !ret ) {
+                   /* When reading the initial partition we must use 512 byte blocks */
+                   vol_size = (uint64_t)vh->blocksize * vh->total_blocks;
+                   vol->maxblocks = vol_size / HFSP_BLOCKSZ;
+               }
+               
+               return ret;
+       } else
+                HFSP_ERROR(-1, "Neither Wrapper nor native HFS+ volume header found");
+fail:
+       return -1;
+}
+
+
+/* Open the device, read and verify the volume header
+   (and its backup) */
+int
+volume_open( volume* vol, int os_fd )
+{
+       hfsp_vh backup; /* backup volume found at second to last block */
+       long    sect_per_block;
+       int     shift;
+
+       vol->blksize_bits       = HFSP_BLOCKSZ_BITS;
+       vol->blksize            = HFSP_BLOCKSZ;
+       vol->startblock         = 0;
+       vol->maxblocks          = 3;
+               /* this should be enough until we find the volume descriptor */
+       vol->extents            = NULL; /* Thanks to Jeremias Sauceda */
+
+       btree_reset(&vol->catalog);
+       vol->os_fd = os_fd;
+
+       // vol->maxblocks = os_seek(vol->os_fd, -1, HFSP_BLOCKSZ_BITS);
+       // This wont work for /dev/... but we do not really need it
+
+       if( volume_read_wrapper(vol, &vol->vol))
+               return -1;
+       if( volume_read(vol, &backup, vol->maxblocks - 2))
+               return -1;
+
+       /* Now switch blksize from HFSP_BLOCKSZ (512) to value given in header
+          and adjust depend values accordingly, after that a block always
+          means a HFS+ allocation size */
+
+       /* Usually 4096 / 512  == 8 */
+       sect_per_block = vol->vol.blocksize / HFSP_BLOCKSZ;
+       shift = 0;
+       if( sect_per_block > 1) {
+               shift = 1;
+               while( sect_per_block > 2) {
+                       sect_per_block >>=1;
+                       shift++;
+               }               /* shift = 3 */
+       }
+       vol -> blksize_bits += shift;
+       vol -> blksize = 1 << vol->blksize_bits;
+       vol -> startblock >>= shift;
+       vol -> maxblocks = vol->vol.total_blocks;       /* cant calculate via shift ? */
+
+       if( btree_init_cat(&vol->catalog, vol, &vol->vol.cat_file))
+               return -1;
+
+       return 0;
+}
+
+/* Write back all data eventually cached and close the device */
+int
+volume_close(volume* vol)
+{
+       btree_close(&vol->catalog);
+       if( vol->extents) {
+               btree_close(vol->extents);
+               FREE(vol->extents);
+       }
+       return 0;
+}
+
+/* internal fucntion used to create the extents btree,
+   is called by inline function when needed */
+void
+volume_create_extents_tree(volume* vol)
+{
+       btree* result = (btree*) ALLOC(btree*, sizeof(btree));
+       if( !result)
+               HFSP_ERROR(ENOMEM, "No memory for extents btree");
+       if( !btree_init_extent(result, vol, &vol->vol.ext_file)) {
+               vol->extents = result;
+               return;
+       }
+  fail:
+       vol->extents = NULL;
+}
+
+/* Determine whether the volume is a HFS-plus volume */
+int
+volume_probe(int fd, long long offset)
+{
+       UInt16 *vol;
+       int ret = 0;
+
+       vol = (UInt16 *)malloc(2 * 1 << HFSP_BLOCKSZ_BITS);
+       os_seek_offset( fd, 2 * (1 << HFSP_BLOCKSZ_BITS) + offset );
+       os_read(fd, vol, 2, HFSP_BLOCKSZ_BITS);
+
+       if (__be16_to_cpu(vol[0]) == HFS_VOLHEAD_SIG &&
+               __be16_to_cpu(vol[0x3e]) == HFSP_VOLHEAD_SIG) {
+               ret = -1;
+       } else if (__be16_to_cpu(vol[0]) == HFSP_VOLHEAD_SIG) {
+               ret = -1;
+       }
+
+       free(vol);
+       return ret;
+}
+