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