From 4722c8a01d186a08183de49759dc8b7b74cf41c9 Mon Sep 17 00:00:00 2001
From: Steven Noonan <steven@uplinklabs.net>
Date: Sun, 19 Apr 2009 00:46:17 -0700
Subject: [PATCH] hfsplus: now properly reads Apple Partition Map blocks

The HFS+ reader can now handle reading from the top of
an Apple Partition Map header. This can happen when
booting Mac OS X discs.

Mac OS X cannot yet boot because there is no BootX
loader implemented. This will be in a later patch.

Signed-off-by: Steven Noonan <steven@uplinklabs.net>
---
 fs/hfsplus/include/hfsp.h    |    3 ++-
 fs/hfsplus/include/libhfsp.h |    1 +
 fs/hfsplus/volume.c          |   30 ++++++++++++++++++++++++++++--
 fs/ioglue.c                  |   13 +++++++++++--
 fs/os.h                      |    5 +++++
 5 files changed, 47 insertions(+), 5 deletions(-)
 mode change 100644 => 100755 config/scripts/crosscflags

diff --git a/config/scripts/crosscflags b/config/scripts/crosscflags
old mode 100644
new mode 100755
diff --git a/fs/hfsplus/include/hfsp.h b/fs/hfsplus/include/hfsp.h
index e916473..7df4b04 100644
--- a/fs/hfsplus/include/hfsp.h
+++ b/fs/hfsplus/include/hfsp.h
@@ -30,7 +30,8 @@
 
 #define HFSP_BLOCKSZ            512	/* A sector for Apple is always 512 bytes */
 #define HFSP_BLOCKSZ_BITS       9	/* 1<<9 == 512  */
-#define	HFSP_VOLHEAD_SIG	0x482B	/* 'H+'	*/
+#define HFSP_VOLHEAD_SIG	0x482B	/* 'H+'	*/
+#define APPLE_MAP_SIG           0x504d  /* 'PM' */
 
 /* HFS+ includes POSIX permissions , although marked as reserved they will be
  * used as such. Is ignored by MacOS 8-9 but probably not by MacOS X.
diff --git a/fs/hfsplus/include/libhfsp.h b/fs/hfsplus/include/libhfsp.h
index 912cfbd..f8dec76 100644
--- a/fs/hfsplus/include/libhfsp.h
+++ b/fs/hfsplus/include/libhfsp.h
@@ -159,6 +159,7 @@ typedef struct volume
     UInt16	blksize_bits;   /* blocksize of device = 1 << blksize_bits */
     UInt16	filler;
     UInt32	blksize;	/* always 1 << blksize_bits */
+    UInt32	offset;
     UInt32	startblock;
 	/* Offset from physical to logical blocks,
 	   eventually intodruced by HFS wrapper */
diff --git a/fs/hfsplus/volume.c b/fs/hfsplus/volume.c
index 9b59b03..e25b537 100644
--- a/fs/hfsplus/volume.c
+++ b/fs/hfsplus/volume.c
@@ -45,6 +45,7 @@ volume_readinbuf(volume * vol,void* buf, long block)
 
 	blksize_bits = vol->blksize_bits;
 	block	+= vol->startblock;
+	os_set_offset(vol->offset * HFSP_BLOCKSZ);
 	if( os_seek(vol->os_fd, block, blksize_bits) == block)
 		if( 1 == os_read(vol->os_fd, buf, 1, blksize_bits))
 			return 0;
@@ -163,6 +164,25 @@ volume_read(volume * vol, hfsp_vh* vh, UInt32 block)
         return volume_readbuf(vh, buf);
 }
 
+static int
+volume_read_map(volume *vol)
+{
+	UInt16  signature;
+	char	buf[vol->blksize];
+	char    *p = buf;
+
+	if( volume_readinbuf(vol, buf, 2) ) // Wrapper or volume header starts here
+		return -1;
+
+	signature = bswabU16_inc(p);
+	if( signature == APPLE_MAP_SIG ) {
+		p = &buf[8];
+		vol->offset = bswabU32_inc(p);
+	}
+
+	return 0;
+}
+
 /* Find out wether the volume is wrapped and unwrap it eventually */
 static int
 volume_read_wrapper(volume * vol, hfsp_vh* vh)
@@ -201,7 +221,10 @@ volume_read_wrapper(volume * vol, hfsp_vh* vh)
 	}
 	else if( signature == HFSP_VOLHEAD_SIG) { /* Native HFS+ volume */
 		p = buf; // Restore to begin of block
-                return volume_readbuf(vh, p);
+		if( volume_readbuf(vh, p) )
+			return -1;
+		vol->maxblocks = (vh->total_blocks * vh->blocksize) / HFSP_BLOCKSZ;
+		return 0;
 	} else
 		 HFSP_ERROR(-1, "Neither Wrapper nor native HFS+ volume header found");
 fail:
@@ -218,10 +241,11 @@ volume_open( volume* vol, int os_fd )
 	long	sect_per_block;
 	int	shift;
 
+	vol->offset			= 0;
 	vol->blksize_bits	= HFSP_BLOCKSZ_BITS;
 	vol->blksize		= HFSP_BLOCKSZ;
 	vol->startblock		= 0;
-	vol->maxblocks		= 3;
+	vol->maxblocks		= 8;
 		/* this should be enough until we find the volume descriptor */
 	vol->extents		= NULL; /* Thanks to Jeremias Sauceda */
 
@@ -231,6 +255,8 @@ volume_open( volume* vol, int 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
 
+	volume_read_map(vol);
+
 	if( volume_read_wrapper(vol, &vol->vol))
 		return -1;
 	if( volume_read(vol, &backup, vol->maxblocks - 2))
diff --git a/fs/ioglue.c b/fs/ioglue.c
index 6df8d0f..ec0be8d 100644
--- a/fs/ioglue.c
+++ b/fs/ioglue.c
@@ -22,9 +22,12 @@
 #include "hfs_mdb.h"
 
 /************************************************************************/
-/*	functionsions used by the various filesystems			*/
+/*	functions used by the various filesystems			*/
 /************************************************************************/
 
+// XXX: This is sloppy... Make the os_* functions have a private state structure.
+static ulong s_offset = 0;
+
 char *
 get_hfs_vol_name( int fd, char *buf, int size )
 {
@@ -47,6 +50,12 @@ get_hfs_vol_name( int fd, char *buf, int size )
 	return buf;
 }
 
+void
+os_set_offset(ulong offset)
+{
+	s_offset = offset;
+}
+
 ulong
 os_read( int fd, void *buf, ulong len, int blksize_bits )
 {
@@ -60,7 +69,7 @@ ulong
 os_seek( int fd, ulong blknum, int blksize_bits )
 {
 	/* printk("os_seek %d\n", blknum ); */
-	llong offs = (llong)blknum << blksize_bits;
+	llong offs = ((llong)blknum << blksize_bits) + s_offset;
 
 	/* offset == -1 means seek to EOF */
 	if( (int)blknum == -1 )
diff --git a/fs/os.h b/fs/os.h
index 51eb073..fc22694 100644
--- a/fs/os.h
+++ b/fs/os.h
@@ -30,6 +30,11 @@
 int os_same( int fd1, int fd2 );
 
 /*
+ * sets a constant offset to be used when using os_seek()
+ */
+void os_set_offset(unsigned long offset);
+
+/*
  * NAME:	os->seek()
  * DESCRIPTION:	set a descriptor's seek pointer (offset in blocks)
  */
-- 
1.6.2.1

