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