1 /*
2 * linux/fs/hfsplus/wrapper.c
3 *
4 * Copyright (C) 2001
5 * Brad Boyer (flar@allandria.com)
6 * (C) 2003 Ardis Technologies <roman@ardistech.com>
7 *
8 * Handling of HFS wrappers around HFS+ volumes
9 */
10
11 #include <linux/fs.h>
12 #include <linux/blkdev.h>
13 #include <linux/version.h>
14 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
15 #include <linux/buffer_head.h>
16 #endif
17
18 #include "hfsplus_fs.h"
19 #include "hfsplus_raw.h"
20
21 struct hfsplus_wd {
22 u32 ablk_size;
23 u16 ablk_start;
24 u16 embed_start;
25 u16 embed_count;
26 };
27
28 static int hfsplus_read_mdb(unsigned char *bufptr, struct hfsplus_wd *wd)
29 {
30 u32 extent;
31 u16 attrib;
32
33 if (be16_to_cpu(*(u16 *)(bufptr + HFSP_WRAPOFF_EMBEDSIG)) != HFSPLUS_VOLHEAD_SIG)
34 return 0;
35
36 attrib = be16_to_cpu(*(u16 *)(bufptr + HFSP_WRAPOFF_ATTRIB));
37 if (!(attrib & HFSP_WRAP_ATTRIB_SLOCK) ||
38 !(attrib & HFSP_WRAP_ATTRIB_SPARED))
39 return 0;
40
41 wd->ablk_size = be32_to_cpu(*(u32 *)(bufptr + HFSP_WRAPOFF_ABLKSIZE));
42 if (wd->ablk_size < HFSPLUS_SECTOR_SIZE)
43 return 0;
44 if (wd->ablk_size % HFSPLUS_SECTOR_SIZE)
45 return 0;
46 wd->ablk_start = be16_to_cpu(*(u16 *)(bufptr + HFSP_WRAPOFF_ABLKSTART));
47
48 extent = be32_to_cpu(*(u32 *)(bufptr + HFSP_WRAPOFF_EMBEDEXT));
49 wd->embed_start = (extent >> 16) & 0xFFFF;
50 wd->embed_count = extent & 0xFFFF;
51
52 return 1;
53 }
54
55 /* Find the volume header and fill in some minimum bits in superblock */
56 /* Takes in super block, returns true if good data read */
57 int hfsplus_read_wrapper(struct super_block *sb)
58 {
59 struct buffer_head *bh;
60 struct hfsplus_vh *vhdr;
61 char *bufptr;
62 unsigned long block, offset, vhsect;
63 struct hfsplus_wd wd;
64 u32 blocksize, blockoffset;
65 u16 sig;
66
67 blocksize = sb_min_blocksize(sb, HFSPLUS_SECTOR_SIZE);
68 if (!blocksize) {
69 printk("HFS+-fs: unable to configure block size\n");
70 return -EINVAL;
71 }
72
73 block = (HFSPLUS_VOLHEAD_SECTOR * HFSPLUS_SECTOR_SIZE) / blocksize;
74 offset = (HFSPLUS_VOLHEAD_SECTOR * HFSPLUS_SECTOR_SIZE) % blocksize;
75
76 bh = sb_bread(sb, block);
77 if (!bh) {
78 printk("HFS+-fs: unable to read VHDR or MDB\n");
79 return -EIO;
80 }
81
82 bufptr = bh->b_data + offset;
83 sig = be16_to_cpu(*(u16 *)(bufptr + HFSP_WRAPOFF_SIG));
84 if (sig == HFSP_WRAP_MAGIC) {
85 if (!hfsplus_read_mdb(bufptr, &wd))
86 goto error;
87 vhsect = (wd.ablk_start + wd.embed_start * (wd.ablk_size >> 9))
88 + HFSPLUS_VOLHEAD_SECTOR;
89 block = (vhsect * HFSPLUS_SECTOR_SIZE) / blocksize;
90 offset = (vhsect * HFSPLUS_SECTOR_SIZE) % blocksize;
91 brelse(bh);
92 bh = sb_bread(sb, block);
93 if (!bh) {
94 printk("HFS+-fs: unable to read VHDR\n");
95 return -EIO;
96 }
97 HFSPLUS_SB(sb).sect_count = wd.embed_count * (wd.ablk_size >> 9);
98 } else {
99 wd.ablk_start = 0;
100 wd.ablk_size = blocksize;
101 wd.embed_start = 0;
102 HFSPLUS_SB(sb).sect_count = sb->s_bdev->bd_inode->i_size >> 9;
103 }
104 vhdr = (struct hfsplus_vh *)(bh->b_data + offset);
105 if (be16_to_cpu(vhdr->signature) != HFSPLUS_VOLHEAD_SIG)
106 goto error;
107 blocksize = be32_to_cpu(vhdr->blocksize);
108 brelse(bh);
109
110 /* block size must be at least as large as a sector
111 * and a multiple of 2
112 */
113 if (blocksize < HFSPLUS_SECTOR_SIZE ||
114 ((blocksize - 1) & blocksize))
115 return -EINVAL;
116
117 /* block offset must be a multiple of the block size */
118 blockoffset = wd.ablk_start + wd.embed_start * (wd.ablk_size >> 9);
119 if (blockoffset % (blocksize / HFSPLUS_SECTOR_SIZE)) {
120 printk("HFS+-fs: embedded blocks not aligned with wrapper\n");
121 return -EINVAL;
122 }
123 blockoffset /= blocksize / HFSPLUS_SECTOR_SIZE;
124 HFSPLUS_SB(sb).blockoffset = blockoffset;
125
126 if (sb_set_blocksize(sb, blocksize) != blocksize)
127 return -EINVAL;
128
129 block = blockoffset + HFSPLUS_VOLHEAD_SECTOR /
130 (blocksize / HFSPLUS_SECTOR_SIZE);
131 offset = (HFSPLUS_VOLHEAD_SECTOR * HFSPLUS_SECTOR_SIZE) % blocksize;
132 bh = sb_bread(sb, block);
133 if (!bh) {
134 printk("HFS+-fs: unable to read VHDR or MDB\n");
135 return -EIO;
136 }
137 vhdr = (struct hfsplus_vh *)(bh->b_data + offset);
138 /* should still be the same... */
139 if (be16_to_cpu(vhdr->signature) != HFSPLUS_VOLHEAD_SIG)
140 goto error;
141 HFSPLUS_SB(sb).s_vhbh = bh;
142 HFSPLUS_SB(sb).s_vhdr = vhdr;
143
144 return 0;
145 error:
146 brelse(bh);
147 return -EINVAL;
148 }
Cache object: 8dadea8fafe2d2d34284b00cee2b3dd5
|