summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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;
struct hfs_btree;
struct hfsplus_sb_info {
- struct buffer_head *s_vhbh;
struct hfsplus_vh *s_vhdr;
+ struct hfsplus_vh *s_backup_vhdr;
struct hfs_btree *ext_tree;
struct hfs_btree *cat_tree;
struct hfs_btree *attr_tree;
@@ -118,7 +118,8 @@ struct hfsplus_sb_info {
/* Runtime variables */
u32 blockoffset;
- u32 sect_count;
+ sector_t part_start;
+ sector_t sect_count;
int fs_shift;
/* immutable data from the volume header */
@@ -385,8 +386,9 @@ int hfsplus_compare_dentry(struct dentry *dentry, struct qstr *s1, struct qstr *
/* wrapper.c */
int hfsplus_read_wrapper(struct super_block *);
-
int hfs_part_find(struct super_block *, sector_t *, sector_t *);
+int hfsplus_submit_bio(struct block_device *bdev, sector_t sector,
+ void *data, int rw);
/* access macros */
static 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)
{
struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
struct hfsplus_vh *vhdr = sbi->s_vhdr;
+ int write_backup = 0;
+ int error, error2;
dprint(DBG_SUPER, "hfsplus_write_super\n");
- mutex_lock(&sbi->vh_mutex);
- mutex_lock(&sbi->alloc_mutex);
sb->s_dirt = 0;
+ mutex_lock(&sbi->vh_mutex);
+ mutex_lock(&sbi->alloc_mutex);
vhdr->free_blocks = cpu_to_be32(sbi->free_blocks);
vhdr->next_cnid = cpu_to_be32(sbi->next_cnid);
vhdr->folder_count = cpu_to_be32(sbi->folder_count);
vhdr->file_count = cpu_to_be32(sbi->file_count);
- mark_buffer_dirty(sbi->s_vhbh);
if (test_and_clear_bit(HFSPLUS_SB_WRITEBACKUP, &sbi->flags)) {
- if (sbi->sect_count) {
- struct buffer_head *bh;
- u32 block, offset;
-
- block = sbi->blockoffset;
- block += (sbi->sect_count - 2) >> (sb->s_blocksize_bits - 9);
- offset = ((sbi->sect_count - 2) << 9) & (sb->s_blocksize - 1);
- printk(KERN_DEBUG "hfs: backup: %u,%u,%u,%u\n",
- sbi->blockoffset, sbi->sect_count,
- block, offset);
- bh = sb_bread(sb, block);
- if (bh) {
- vhdr = (struct hfsplus_vh *)(bh->b_data + offset);
- if (be16_to_cpu(vhdr->signature) == HFSPLUS_VOLHEAD_SIG) {
- memcpy(vhdr, sbi->s_vhdr, sizeof(*vhdr));
- mark_buffer_dirty(bh);
- brelse(bh);
- } else
- printk(KERN_WARNING "hfs: backup not found!\n");
- }
- }
+ memcpy(sbi->s_backup_vhdr, sbi->s_vhdr, sizeof(*sbi->s_vhdr));
+ write_backup = 1;
}
+
+ error = hfsplus_submit_bio(sb->s_bdev,
+ sbi->part_start + HFSPLUS_VOLHEAD_SECTOR,
+ sbi->s_vhdr, WRITE_SYNC);
+ if (!write_backup)
+ goto out;
+
+ error2 = hfsplus_submit_bio(sb->s_bdev,
+ sbi->part_start + sbi->sect_count - 2,
+ sbi->s_backup_vhdr, WRITE_SYNC);
+ if (!error)
+ error2 = error;
+out:
mutex_unlock(&sbi->alloc_mutex);
mutex_unlock(&sbi->vh_mutex);
- return 0;
+ return error;
}
static void hfsplus_write_super(struct super_block *sb)
@@ -229,7 +224,8 @@ static void hfsplus_put_super(struct super_block *sb)
hfs_btree_close(sbi->ext_tree);
iput(sbi->alloc_file);
iput(sbi->hidden_dir);
- brelse(sbi->s_vhbh);
+ kfree(sbi->s_vhdr);
+ kfree(sbi->s_backup_vhdr);
unload_nls(sbi->nls);
kfree(sb->s_fs_info);
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 {
u16 embed_count;
};
+static void hfsplus_end_io_sync(struct bio *bio, int err)
+{
+ if (err)
+ clear_bit(BIO_UPTODATE, &bio->bi_flags);
+ complete(bio->bi_private);
+}
+
+int hfsplus_submit_bio(struct block_device *bdev, sector_t sector,
+ void *data, int rw)
+{
+ DECLARE_COMPLETION_ONSTACK(wait);
+ struct bio *bio;
+
+ bio = bio_alloc(GFP_NOIO, 1);
+ bio->bi_sector = sector;
+ bio->bi_bdev = bdev;
+ bio->bi_end_io = hfsplus_end_io_sync;
+ bio->bi_private = &wait;
+
+ /*
+ * We always submit one sector at a time, so bio_add_page must not fail.
+ */
+ if (bio_add_page(bio, virt_to_page(data), HFSPLUS_SECTOR_SIZE,
+ offset_in_page(data)) != HFSPLUS_SECTOR_SIZE)
+ BUG();
+
+ submit_bio(rw, bio);
+ wait_for_completion(&wait);
+
+ if (!bio_flagged(bio, BIO_UPTODATE))
+ return -EIO;
+ return 0;
+}
+
static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd)
{
u32 extent;
@@ -88,100 +122,111 @@ static int hfsplus_get_last_session(struct super_block *sb,
int hfsplus_read_wrapper(struct super_block *sb)
{
struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
- struct buffer_head *bh;
- struct hfsplus_vh *vhdr;
struct hfsplus_wd wd;
sector_t part_start, part_size;
u32 blocksize;
+ int error = 0;
+ error = -EINVAL;
blocksize = sb_min_blocksize(sb, HFSPLUS_SECTOR_SIZE);
if (!blocksize)
- return -EINVAL;
+ goto out;
if (hfsplus_get_last_session(sb, &part_start, &part_size))
- return -EINVAL;
+ goto out;
if ((u64)part_start + part_size > 0x100000000ULL) {
pr_err("hfs: volumes larger than 2TB are not supported yet\n");
- return -EINVAL;
+ goto out;
}
- while (1) {
- bh = sb_bread512(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, vhdr);
- if (!bh)
- return -EIO;
-
- if (vhdr->signature == cpu_to_be16(HFSP_WRAP_MAGIC)) {
- if (!hfsplus_read_mdb(vhdr, &wd))
- goto error;
- wd.ablk_size >>= HFSPLUS_SECTOR_SHIFT;
- part_start += wd.ablk_start + wd.embed_start * wd.ablk_size;
- part_size = wd.embed_count * wd.ablk_size;
- brelse(bh);
- bh = sb_bread512(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, vhdr);
- if (!bh)
- return -EIO;
- }
- if (vhdr->signature == cpu_to_be16(HFSPLUS_VOLHEAD_SIG))
- break;
- if (vhdr->signature == cpu_to_be16(HFSPLUS_VOLHEAD_SIGX)) {
- set_bit(HFSPLUS_SB_HFSX, &sbi->flags);
- break;
- }
- brelse(bh);
- /* check for a partition block
+ error = -ENOMEM;
+ sbi->s_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL);
+ if (!sbi->s_vhdr)
+ goto out;
+ sbi->s_backup_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL);
+ if (!sbi->s_backup_vhdr)
+ goto out_free_vhdr;
+
+reread:
+ error = hfsplus_submit_bio(sb->s_bdev,
+ part_start + HFSPLUS_VOLHEAD_SECTOR,
+ sbi->s_vhdr, READ);
+ if (error)
+ goto out_free_backup_vhdr;
+
+ error = -EINVAL;
+ switch (sbi->s_vhdr->signature) {
+ case cpu_to_be16(HFSPLUS_VOLHEAD_SIGX):
+ set_bit(HFSPLUS_SB_HFSX, &sbi->flags);
+ /*FALLTHRU*/
+ case cpu_to_be16(HFSPLUS_VOLHEAD_SIG):
+ break;
+ case cpu_to_be16(HFSP_WRAP_MAGIC):
+ if (!hfsplus_read_mdb(sbi->s_vhdr, &wd))
+ goto out;
+ wd.ablk_size >>= HFSPLUS_SECTOR_SHIFT;
+ part_start += wd.ablk_start + wd.embed_start * wd.ablk_size;
+ part_size = wd.embed_count * wd.ablk_size;
+ goto reread;
+ default:
+ /*
+ * Check for a partition block.
+ *
* (should do this only for cdrom/loop though)
*/
if (hfs_part_find(sb, &part_start, &part_size))
- return -EINVAL;
+ goto out;
+ goto reread;
+ }
+
+ error = hfsplus_submit_bio(sb->s_bdev,
+ part_start + part_size - 2,
+ sbi->s_backup_vhdr, READ);
+ if (error)
+ goto out_free_backup_vhdr;
+
+ error = -EINVAL;
+ if (sbi->s_backup_vhdr->signature != sbi->s_vhdr->signature) {
+ printk(KERN_WARNING
+ "hfs: invalid secondary volume header\n");
+ goto out_free_backup_vhdr;
}
- blocksize = be32_to_cpu(vhdr->blocksize);
- brelse(bh);
+ blocksize = be32_to_cpu(sbi->s_vhdr->blocksize);
- /* block size must be at least as large as a sector
- * and a multiple of 2
+ /*
+ * 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;
+ if (blocksize < HFSPLUS_SECTOR_SIZE || ((blocksize - 1) & blocksize))
+ goto out_free_backup_vhdr;
sbi->alloc_blksz = blocksize;
sbi->alloc_blksz_shift = 0;
while ((blocksize >>= 1) != 0)
sbi->alloc_blksz_shift++;
blocksize = min(sbi->alloc_blksz, (u32)PAGE_SIZE);
- /* align block size to block offset */
+ /*
+ * Align block size to block offset.
+ */
while (part_start & ((blocksize >> HFSPLUS_SECTOR_SHIFT) - 1))
blocksize >>= 1;
if (sb_set_blocksize(sb, blocksize) != blocksize) {
printk(KERN_ERR "hfs: unable to set blocksize to %u!\n", blocksize);
- return -EINVAL;
+ goto out_free_backup_vhdr;
}
sbi->blockoffset =
part_start >> (sb->s_blocksize_bits - HFSPLUS_SECTOR_SHIFT);
+ sbi->part_start = part_start;
sbi->sect_count = part_size;
sbi->fs_shift = sbi->alloc_blksz_shift - sb->s_blocksize_bits;
-
- bh = sb_bread512(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, vhdr);
- if (!bh)
- return -EIO;
-
- /* should still be the same... */
- if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags)) {
- if (vhdr->signature != cpu_to_be16(HFSPLUS_VOLHEAD_SIGX))
- goto error;
- } else {
- if (vhdr->signature != cpu_to_be16(HFSPLUS_VOLHEAD_SIG))
- goto error;
- }
-
- sbi->s_vhbh = bh;
- sbi->s_vhdr = vhdr;
-
return 0;
- error:
- brelse(bh);
- return -EINVAL;
+
+out_free_backup_vhdr:
+ kfree(sbi->s_backup_vhdr);
+out_free_vhdr:
+ kfree(sbi->s_vhdr);
+out:
+ return error;
}