aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@tuxera.com>2010-11-23 08:37:47 -0500
committerChristoph Hellwig <hch@lst.de>2010-11-23 08:37:47 -0500
commit52399b171dfaea02b6944cd6feba49b624147126 (patch)
tree199eda8c91a51fa1d4e3c792abd97d9c0a50ae26
parent3b5ce8ae31e3c66655207907527476bbd3e5063b (diff)
hfsplus: use raw bio access for the volume headers
The hfsplus backup volume header is located two blocks from the end of the device. In case of device sizes that are not 4k aligned this means we can't access it using buffer_heads when using the default 4k block size. Switch to using raw bios to read/write all buffer headers. We were not relying on any caching behaviour of the buffer heads anyway. Additionally always read in the backup volume header during mount to verify that we can actually read it. Signed-off-by: Christoph Hellwig <hch@tuxera.com>
-rw-r--r--fs/hfsplus/hfsplus_fs.h8
-rw-r--r--fs/hfsplus/super.c48
-rw-r--r--fs/hfsplus/wrapper.c163
3 files changed, 131 insertions, 88 deletions
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
index f07aa640c27d..276ddb0fd0fd 100644
--- a/fs/hfsplus/hfsplus_fs.h
+++ b/fs/hfsplus/hfsplus_fs.h
@@ -107,8 +107,8 @@ struct hfsplus_vh;
107struct hfs_btree; 107struct hfs_btree;
108 108
109struct hfsplus_sb_info { 109struct hfsplus_sb_info {
110 struct buffer_head *s_vhbh;
111 struct hfsplus_vh *s_vhdr; 110 struct hfsplus_vh *s_vhdr;
111 struct hfsplus_vh *s_backup_vhdr;
112 struct hfs_btree *ext_tree; 112 struct hfs_btree *ext_tree;
113 struct hfs_btree *cat_tree; 113 struct hfs_btree *cat_tree;
114 struct hfs_btree *attr_tree; 114 struct hfs_btree *attr_tree;
@@ -118,7 +118,8 @@ struct hfsplus_sb_info {
118 118
119 /* Runtime variables */ 119 /* Runtime variables */
120 u32 blockoffset; 120 u32 blockoffset;
121 u32 sect_count; 121 sector_t part_start;
122 sector_t sect_count;
122 int fs_shift; 123 int fs_shift;
123 124
124 /* immutable data from the volume header */ 125 /* immutable data from the volume header */
@@ -385,8 +386,9 @@ int hfsplus_compare_dentry(struct dentry *dentry, struct qstr *s1, struct qstr *
385 386
386/* wrapper.c */ 387/* wrapper.c */
387int hfsplus_read_wrapper(struct super_block *); 388int hfsplus_read_wrapper(struct super_block *);
388
389int hfs_part_find(struct super_block *, sector_t *, sector_t *); 389int hfs_part_find(struct super_block *, sector_t *, sector_t *);
390int hfsplus_submit_bio(struct block_device *bdev, sector_t sector,
391 void *data, int rw);
390 392
391/* access macros */ 393/* access macros */
392static inline struct hfsplus_sb_info *HFSPLUS_SB(struct super_block *sb) 394static inline struct hfsplus_sb_info *HFSPLUS_SB(struct super_block *sb)
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c
index 6a2349058618..fe8f7bffbea5 100644
--- a/fs/hfsplus/super.c
+++ b/fs/hfsplus/super.c
@@ -157,45 +157,40 @@ int hfsplus_sync_fs(struct super_block *sb, int wait)
157{ 157{
158 struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); 158 struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
159 struct hfsplus_vh *vhdr = sbi->s_vhdr; 159 struct hfsplus_vh *vhdr = sbi->s_vhdr;
160 int write_backup = 0;
161 int error, error2;
160 162
161 dprint(DBG_SUPER, "hfsplus_write_super\n"); 163 dprint(DBG_SUPER, "hfsplus_write_super\n");
162 164
163 mutex_lock(&sbi->vh_mutex);
164 mutex_lock(&sbi->alloc_mutex);
165 sb->s_dirt = 0; 165 sb->s_dirt = 0;
166 166
167 mutex_lock(&sbi->vh_mutex);
168 mutex_lock(&sbi->alloc_mutex);
167 vhdr->free_blocks = cpu_to_be32(sbi->free_blocks); 169 vhdr->free_blocks = cpu_to_be32(sbi->free_blocks);
168 vhdr->next_cnid = cpu_to_be32(sbi->next_cnid); 170 vhdr->next_cnid = cpu_to_be32(sbi->next_cnid);
169 vhdr->folder_count = cpu_to_be32(sbi->folder_count); 171 vhdr->folder_count = cpu_to_be32(sbi->folder_count);
170 vhdr->file_count = cpu_to_be32(sbi->file_count); 172 vhdr->file_count = cpu_to_be32(sbi->file_count);
171 173
172 mark_buffer_dirty(sbi->s_vhbh);
173 if (test_and_clear_bit(HFSPLUS_SB_WRITEBACKUP, &sbi->flags)) { 174 if (test_and_clear_bit(HFSPLUS_SB_WRITEBACKUP, &sbi->flags)) {
174 if (sbi->sect_count) { 175 memcpy(sbi->s_backup_vhdr, sbi->s_vhdr, sizeof(*sbi->s_vhdr));
175 struct buffer_head *bh; 176 write_backup = 1;
176 u32 block, offset;
177
178 block = sbi->blockoffset;
179 block += (sbi->sect_count - 2) >> (sb->s_blocksize_bits - 9);
180 offset = ((sbi->sect_count - 2) << 9) & (sb->s_blocksize - 1);
181 printk(KERN_DEBUG "hfs: backup: %u,%u,%u,%u\n",
182 sbi->blockoffset, sbi->sect_count,
183 block, offset);
184 bh = sb_bread(sb, block);
185 if (bh) {
186 vhdr = (struct hfsplus_vh *)(bh->b_data + offset);
187 if (be16_to_cpu(vhdr->signature) == HFSPLUS_VOLHEAD_SIG) {
188 memcpy(vhdr, sbi->s_vhdr, sizeof(*vhdr));
189 mark_buffer_dirty(bh);
190 brelse(bh);
191 } else
192 printk(KERN_WARNING "hfs: backup not found!\n");
193 }
194 }
195 } 177 }
178
179 error = hfsplus_submit_bio(sb->s_bdev,
180 sbi->part_start + HFSPLUS_VOLHEAD_SECTOR,
181 sbi->s_vhdr, WRITE_SYNC);
182 if (!write_backup)
183 goto out;
184
185 error2 = hfsplus_submit_bio(sb->s_bdev,
186 sbi->part_start + sbi->sect_count - 2,
187 sbi->s_backup_vhdr, WRITE_SYNC);
188 if (!error)
189 error2 = error;
190out:
196 mutex_unlock(&sbi->alloc_mutex); 191 mutex_unlock(&sbi->alloc_mutex);
197 mutex_unlock(&sbi->vh_mutex); 192 mutex_unlock(&sbi->vh_mutex);
198 return 0; 193 return error;
199} 194}
200 195
201static void hfsplus_write_super(struct super_block *sb) 196static void hfsplus_write_super(struct super_block *sb)
@@ -229,7 +224,8 @@ static void hfsplus_put_super(struct super_block *sb)
229 hfs_btree_close(sbi->ext_tree); 224 hfs_btree_close(sbi->ext_tree);
230 iput(sbi->alloc_file); 225 iput(sbi->alloc_file);
231 iput(sbi->hidden_dir); 226 iput(sbi->hidden_dir);
232 brelse(sbi->s_vhbh); 227 kfree(sbi->s_vhdr);
228 kfree(sbi->s_backup_vhdr);
233 unload_nls(sbi->nls); 229 unload_nls(sbi->nls);
234 kfree(sb->s_fs_info); 230 kfree(sb->s_fs_info);
235 sb->s_fs_info = NULL; 231 sb->s_fs_info = NULL;
diff --git a/fs/hfsplus/wrapper.c b/fs/hfsplus/wrapper.c
index 8972c20b3216..15e0eabb489e 100644
--- a/fs/hfsplus/wrapper.c
+++ b/fs/hfsplus/wrapper.c
@@ -24,6 +24,40 @@ struct hfsplus_wd {
24 u16 embed_count; 24 u16 embed_count;
25}; 25};
26 26
27static void hfsplus_end_io_sync(struct bio *bio, int err)
28{
29 if (err)
30 clear_bit(BIO_UPTODATE, &bio->bi_flags);
31 complete(bio->bi_private);
32}
33
34int hfsplus_submit_bio(struct block_device *bdev, sector_t sector,
35 void *data, int rw)
36{
37 DECLARE_COMPLETION_ONSTACK(wait);
38 struct bio *bio;
39
40 bio = bio_alloc(GFP_NOIO, 1);
41 bio->bi_sector = sector;
42 bio->bi_bdev = bdev;
43 bio->bi_end_io = hfsplus_end_io_sync;
44 bio->bi_private = &wait;
45
46 /*
47 * We always submit one sector at a time, so bio_add_page must not fail.
48 */
49 if (bio_add_page(bio, virt_to_page(data), HFSPLUS_SECTOR_SIZE,
50 offset_in_page(data)) != HFSPLUS_SECTOR_SIZE)
51 BUG();
52
53 submit_bio(rw, bio);
54 wait_for_completion(&wait);
55
56 if (!bio_flagged(bio, BIO_UPTODATE))
57 return -EIO;
58 return 0;
59}
60
27static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd) 61static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd)
28{ 62{
29 u32 extent; 63 u32 extent;
@@ -88,100 +122,111 @@ static int hfsplus_get_last_session(struct super_block *sb,
88int hfsplus_read_wrapper(struct super_block *sb) 122int hfsplus_read_wrapper(struct super_block *sb)
89{ 123{
90 struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); 124 struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
91 struct buffer_head *bh;
92 struct hfsplus_vh *vhdr;
93 struct hfsplus_wd wd; 125 struct hfsplus_wd wd;
94 sector_t part_start, part_size; 126 sector_t part_start, part_size;
95 u32 blocksize; 127 u32 blocksize;
128 int error = 0;
96 129
130 error = -EINVAL;
97 blocksize = sb_min_blocksize(sb, HFSPLUS_SECTOR_SIZE); 131 blocksize = sb_min_blocksize(sb, HFSPLUS_SECTOR_SIZE);
98 if (!blocksize) 132 if (!blocksize)
99 return -EINVAL; 133 goto out;
100 134
101 if (hfsplus_get_last_session(sb, &part_start, &part_size)) 135 if (hfsplus_get_last_session(sb, &part_start, &part_size))
102 return -EINVAL; 136 goto out;
103 if ((u64)part_start + part_size > 0x100000000ULL) { 137 if ((u64)part_start + part_size > 0x100000000ULL) {
104 pr_err("hfs: volumes larger than 2TB are not supported yet\n"); 138 pr_err("hfs: volumes larger than 2TB are not supported yet\n");
105 return -EINVAL; 139 goto out;
106 } 140 }
107 while (1) {
108 bh = sb_bread512(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, vhdr);
109 if (!bh)
110 return -EIO;
111
112 if (vhdr->signature == cpu_to_be16(HFSP_WRAP_MAGIC)) {
113 if (!hfsplus_read_mdb(vhdr, &wd))
114 goto error;
115 wd.ablk_size >>= HFSPLUS_SECTOR_SHIFT;
116 part_start += wd.ablk_start + wd.embed_start * wd.ablk_size;
117 part_size = wd.embed_count * wd.ablk_size;
118 brelse(bh);
119 bh = sb_bread512(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, vhdr);
120 if (!bh)
121 return -EIO;
122 }
123 if (vhdr->signature == cpu_to_be16(HFSPLUS_VOLHEAD_SIG))
124 break;
125 if (vhdr->signature == cpu_to_be16(HFSPLUS_VOLHEAD_SIGX)) {
126 set_bit(HFSPLUS_SB_HFSX, &sbi->flags);
127 break;
128 }
129 brelse(bh);
130 141
131 /* check for a partition block 142 error = -ENOMEM;
143 sbi->s_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL);
144 if (!sbi->s_vhdr)
145 goto out;
146 sbi->s_backup_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL);
147 if (!sbi->s_backup_vhdr)
148 goto out_free_vhdr;
149
150reread:
151 error = hfsplus_submit_bio(sb->s_bdev,
152 part_start + HFSPLUS_VOLHEAD_SECTOR,
153 sbi->s_vhdr, READ);
154 if (error)
155 goto out_free_backup_vhdr;
156
157 error = -EINVAL;
158 switch (sbi->s_vhdr->signature) {
159 case cpu_to_be16(HFSPLUS_VOLHEAD_SIGX):
160 set_bit(HFSPLUS_SB_HFSX, &sbi->flags);
161 /*FALLTHRU*/
162 case cpu_to_be16(HFSPLUS_VOLHEAD_SIG):
163 break;
164 case cpu_to_be16(HFSP_WRAP_MAGIC):
165 if (!hfsplus_read_mdb(sbi->s_vhdr, &wd))
166 goto out;
167 wd.ablk_size >>= HFSPLUS_SECTOR_SHIFT;
168 part_start += wd.ablk_start + wd.embed_start * wd.ablk_size;
169 part_size = wd.embed_count * wd.ablk_size;
170 goto reread;
171 default:
172 /*
173 * Check for a partition block.
174 *
132 * (should do this only for cdrom/loop though) 175 * (should do this only for cdrom/loop though)
133 */ 176 */
134 if (hfs_part_find(sb, &part_start, &part_size)) 177 if (hfs_part_find(sb, &part_start, &part_size))
135 return -EINVAL; 178 goto out;
179 goto reread;
180 }
181
182 error = hfsplus_submit_bio(sb->s_bdev,
183 part_start + part_size - 2,
184 sbi->s_backup_vhdr, READ);
185 if (error)
186 goto out_free_backup_vhdr;
187
188 error = -EINVAL;
189 if (sbi->s_backup_vhdr->signature != sbi->s_vhdr->signature) {
190 printk(KERN_WARNING
191 "hfs: invalid secondary volume header\n");
192 goto out_free_backup_vhdr;
136 } 193 }
137 194
138 blocksize = be32_to_cpu(vhdr->blocksize); 195 blocksize = be32_to_cpu(sbi->s_vhdr->blocksize);
139 brelse(bh);
140 196
141 /* block size must be at least as large as a sector 197 /*
142 * and a multiple of 2 198 * Block size must be at least as large as a sector and a multiple of 2.
143 */ 199 */
144 if (blocksize < HFSPLUS_SECTOR_SIZE || 200 if (blocksize < HFSPLUS_SECTOR_SIZE || ((blocksize - 1) & blocksize))
145 ((blocksize - 1) & blocksize)) 201 goto out_free_backup_vhdr;
146 return -EINVAL;
147 sbi->alloc_blksz = blocksize; 202 sbi->alloc_blksz = blocksize;
148 sbi->alloc_blksz_shift = 0; 203 sbi->alloc_blksz_shift = 0;
149 while ((blocksize >>= 1) != 0) 204 while ((blocksize >>= 1) != 0)
150 sbi->alloc_blksz_shift++; 205 sbi->alloc_blksz_shift++;
151 blocksize = min(sbi->alloc_blksz, (u32)PAGE_SIZE); 206 blocksize = min(sbi->alloc_blksz, (u32)PAGE_SIZE);
152 207
153 /* align block size to block offset */ 208 /*
209 * Align block size to block offset.
210 */
154 while (part_start & ((blocksize >> HFSPLUS_SECTOR_SHIFT) - 1)) 211 while (part_start & ((blocksize >> HFSPLUS_SECTOR_SHIFT) - 1))
155 blocksize >>= 1; 212 blocksize >>= 1;
156 213
157 if (sb_set_blocksize(sb, blocksize) != blocksize) { 214 if (sb_set_blocksize(sb, blocksize) != blocksize) {
158 printk(KERN_ERR "hfs: unable to set blocksize to %u!\n", blocksize); 215 printk(KERN_ERR "hfs: unable to set blocksize to %u!\n", blocksize);
159 return -EINVAL; 216 goto out_free_backup_vhdr;
160 } 217 }
161 218
162 sbi->blockoffset = 219 sbi->blockoffset =
163 part_start >> (sb->s_blocksize_bits - HFSPLUS_SECTOR_SHIFT); 220 part_start >> (sb->s_blocksize_bits - HFSPLUS_SECTOR_SHIFT);
221 sbi->part_start = part_start;
164 sbi->sect_count = part_size; 222 sbi->sect_count = part_size;
165 sbi->fs_shift = sbi->alloc_blksz_shift - sb->s_blocksize_bits; 223 sbi->fs_shift = sbi->alloc_blksz_shift - sb->s_blocksize_bits;
166
167 bh = sb_bread512(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, vhdr);
168 if (!bh)
169 return -EIO;
170
171 /* should still be the same... */
172 if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags)) {
173 if (vhdr->signature != cpu_to_be16(HFSPLUS_VOLHEAD_SIGX))
174 goto error;
175 } else {
176 if (vhdr->signature != cpu_to_be16(HFSPLUS_VOLHEAD_SIG))
177 goto error;
178 }
179
180 sbi->s_vhbh = bh;
181 sbi->s_vhdr = vhdr;
182
183 return 0; 224 return 0;
184 error: 225
185 brelse(bh); 226out_free_backup_vhdr:
186 return -EINVAL; 227 kfree(sbi->s_backup_vhdr);
228out_free_vhdr:
229 kfree(sbi->s_vhdr);
230out:
231 return error;
187} 232}