patch-2.4.22 linux-2.4.22/fs/hfsplus/wrapper.c

Next file: linux-2.4.22/fs/inode.c
Previous file: linux-2.4.22/fs/hfsplus/unicode.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.21/fs/hfsplus/wrapper.c linux-2.4.22/fs/hfsplus/wrapper.c
@@ -0,0 +1,148 @@
+/*
+ *  linux/fs/hfsplus/wrapper.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Handling of HFS wrappers around HFS+ volumes
+ */
+
+#include <linux/fs.h>
+#include <linux/blkdev.h>
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+#include <linux/buffer_head.h>
+#endif
+
+#include "hfsplus_fs.h"
+#include "hfsplus_raw.h"
+
+struct hfsplus_wd {
+	u32 ablk_size;
+	u16 ablk_start;
+	u16 embed_start;
+	u16 embed_count;
+};
+
+static int hfsplus_read_mdb(unsigned char *bufptr, struct hfsplus_wd *wd)
+{
+	u32 extent;
+	u16 attrib;
+
+	if (be16_to_cpu(*(u16 *)(bufptr + HFSP_WRAPOFF_EMBEDSIG)) != HFSPLUS_VOLHEAD_SIG)
+		return 0;
+
+	attrib = be16_to_cpu(*(u16 *)(bufptr + HFSP_WRAPOFF_ATTRIB));
+	if (!(attrib & HFSP_WRAP_ATTRIB_SLOCK) ||
+	   !(attrib & HFSP_WRAP_ATTRIB_SPARED))
+		return 0;
+
+	wd->ablk_size = be32_to_cpu(*(u32 *)(bufptr + HFSP_WRAPOFF_ABLKSIZE));
+	if (wd->ablk_size < HFSPLUS_SECTOR_SIZE)
+		return 0;
+	if (wd->ablk_size % HFSPLUS_SECTOR_SIZE)
+		return 0;
+	wd->ablk_start = be16_to_cpu(*(u16 *)(bufptr + HFSP_WRAPOFF_ABLKSTART));
+
+	extent = be32_to_cpu(*(u32 *)(bufptr + HFSP_WRAPOFF_EMBEDEXT));
+	wd->embed_start = (extent >> 16) & 0xFFFF;
+	wd->embed_count = extent & 0xFFFF;
+
+	return 1;
+}
+
+/* Find the volume header and fill in some minimum bits in superblock */
+/* Takes in super block, returns true if good data read */
+int hfsplus_read_wrapper(struct super_block *sb)
+{
+	struct buffer_head *bh;
+	struct hfsplus_vh *vhdr;
+	char *bufptr;
+	unsigned long block, offset, vhsect;
+	struct hfsplus_wd wd;
+	u32 blocksize, blockoffset;
+	u16 sig;
+
+	blocksize = sb_min_blocksize(sb, HFSPLUS_SECTOR_SIZE);
+	if (!blocksize) {
+		printk("HFS+-fs: unable to configure block size\n");
+		return -EINVAL;
+	}
+
+	block = (HFSPLUS_VOLHEAD_SECTOR * HFSPLUS_SECTOR_SIZE) / blocksize;
+	offset = (HFSPLUS_VOLHEAD_SECTOR * HFSPLUS_SECTOR_SIZE) % blocksize;
+
+	bh = sb_bread(sb, block);
+	if (!bh) {
+		printk("HFS+-fs: unable to read VHDR or MDB\n");
+		return -EIO;
+	}
+
+	bufptr = bh->b_data + offset;
+	sig = be16_to_cpu(*(u16 *)(bufptr + HFSP_WRAPOFF_SIG));
+	if (sig == HFSP_WRAP_MAGIC) {
+		if (!hfsplus_read_mdb(bufptr, &wd))
+			goto error;
+		vhsect = (wd.ablk_start + wd.embed_start * (wd.ablk_size >> 9))
+			+ HFSPLUS_VOLHEAD_SECTOR;
+		block = (vhsect * HFSPLUS_SECTOR_SIZE) / blocksize;
+		offset = (vhsect * HFSPLUS_SECTOR_SIZE) % blocksize;
+		brelse(bh);
+		bh = sb_bread(sb, block);
+		if (!bh) {
+			printk("HFS+-fs: unable to read VHDR\n");
+			return -EIO;
+		}
+		HFSPLUS_SB(sb).sect_count = wd.embed_count * (wd.ablk_size >> 9);
+	} else {
+		wd.ablk_start = 0;
+		wd.ablk_size = blocksize;
+		wd.embed_start = 0;
+		HFSPLUS_SB(sb).sect_count = sb->s_bdev->bd_inode->i_size >> 9;
+	}
+	vhdr = (struct hfsplus_vh *)(bh->b_data + offset);
+	if (be16_to_cpu(vhdr->signature) != HFSPLUS_VOLHEAD_SIG)
+		goto error;
+	blocksize = be32_to_cpu(vhdr->blocksize);
+	brelse(bh);
+
+	/* block size must be at least as large as a sector
+	 * and a multiple of 2
+	 */
+	if (blocksize < HFSPLUS_SECTOR_SIZE ||
+	    ((blocksize - 1) & blocksize))
+		return -EINVAL;
+
+	/* block offset must be a multiple of the block size */
+	blockoffset = wd.ablk_start + wd.embed_start * (wd.ablk_size >> 9);
+	if (blockoffset % (blocksize / HFSPLUS_SECTOR_SIZE)) {
+		printk("HFS+-fs: embedded blocks not aligned with wrapper\n");
+		return -EINVAL;
+	}
+	blockoffset /= blocksize / HFSPLUS_SECTOR_SIZE;
+	HFSPLUS_SB(sb).blockoffset = blockoffset;
+
+	if (sb_set_blocksize(sb, blocksize) != blocksize)
+		return -EINVAL;
+
+	block = blockoffset + HFSPLUS_VOLHEAD_SECTOR /
+		(blocksize / HFSPLUS_SECTOR_SIZE);
+	offset = (HFSPLUS_VOLHEAD_SECTOR * HFSPLUS_SECTOR_SIZE) % blocksize;
+	bh = sb_bread(sb, block);
+	if (!bh) {
+		printk("HFS+-fs: unable to read VHDR or MDB\n");
+		return -EIO;
+	}
+	vhdr = (struct hfsplus_vh *)(bh->b_data + offset);
+	/* should still be the same... */
+	if (be16_to_cpu(vhdr->signature) != HFSPLUS_VOLHEAD_SIG)
+		goto error;
+	HFSPLUS_SB(sb).s_vhbh = bh;
+	HFSPLUS_SB(sb).s_vhdr = vhdr;
+
+	return 0;
+ error:
+	brelse(bh);
+	return -EINVAL;
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)