diff --git a/fs/hfs/hfs_fs.h b/fs/hfs/hfs_fs.h index 3df23f1f091b..e250f87a5e33 100644 --- a/fs/hfs/hfs_fs.h +++ b/fs/hfs/hfs_fs.h @@ -64,14 +64,22 @@ struct hfs_inode_info { * The HFS-specific part of a Linux (struct super_block) */ struct hfs_sb_info { + struct mutex mdb_lock; /* MDB operations lock */ struct buffer_head *mdb_bh; /* The hfs_buffer holding the real superblock (aka VIB or MDB) */ - struct hfs_mdb *mdb; + unsigned int mdb_offset; /* byte offset of the MDB + sector within mdb_bh's + data */ + struct hfs_mdb *mdb; /* in-memory copy of the MDB */ struct buffer_head *alt_mdb_bh; /* The hfs_buffer holding the alternate superblock */ - struct hfs_mdb *alt_mdb; + unsigned int alt_mdb_offset; /* byte offset of the alternate + MDB sector within + alt_mdb_bh's data */ + struct hfs_mdb *alt_mdb; /* in-memory copy of the + alternate MDB */ __be32 *bitmap; /* The page holding the allocation bitmap */ struct hfs_btree *ext_tree; /* Information about diff --git a/fs/hfs/mdb.c b/fs/hfs/mdb.c index f4578f634302..277de712f9d4 100644 --- a/fs/hfs/mdb.c +++ b/fs/hfs/mdb.c @@ -85,6 +85,39 @@ bool is_hfs_cnid_counts_valid(struct super_block *sb) return !corrupted; } +/* + * hfs_sect_offset() - get byte offset within the buffer_head. + */ +static unsigned int hfs_sect_offset(struct super_block *sb, sector_t sec) +{ + loff_t start = (loff_t)sec << HFS_SECTOR_SIZE_BITS; + + return start & (sb->s_blocksize - 1); +} + +/* + * hfs_mdb_publish() - copy the in-memory primary MDB to the on-disk buffer. + */ +static void hfs_mdb_publish(struct hfs_sb_info *sbi) +{ + lock_buffer(sbi->mdb_bh); + memcpy(sbi->mdb_bh->b_data + sbi->mdb_offset, sbi->mdb, HFS_SECTOR_SIZE); + mark_buffer_dirty(sbi->mdb_bh); + unlock_buffer(sbi->mdb_bh); +} + +/* + * hfs_alt_mdb_publish() - copy the in-memory alternate MDB to its buffer. + */ +static void hfs_alt_mdb_publish(struct hfs_sb_info *sbi) +{ + lock_buffer(sbi->alt_mdb_bh); + memcpy(sbi->alt_mdb_bh->b_data + sbi->alt_mdb_offset, sbi->alt_mdb, + HFS_SECTOR_SIZE); + mark_buffer_dirty(sbi->alt_mdb_bh); + unlock_buffer(sbi->alt_mdb_bh); +} + /* * hfs_mdb_get() * @@ -94,7 +127,7 @@ bool is_hfs_cnid_counts_valid(struct super_block *sb) int hfs_mdb_get(struct super_block *sb) { struct buffer_head *bh; - struct hfs_mdb *mdb, *mdb2; + struct hfs_mdb *mdb, *alt_mdb; unsigned int block; char *ptr; int off2, len, size, sect; @@ -158,7 +191,14 @@ int hfs_mdb_get(struct super_block *sb) return -EIO; } + mdb = kmemdup(mdb, HFS_SECTOR_SIZE, GFP_KERNEL); + if (!mdb) { + brelse(bh); + return -ENOMEM; + } + HFS_SB(sb)->mdb_bh = bh; + HFS_SB(sb)->mdb_offset = hfs_sect_offset(sb, part_start + HFS_MDB_BLK); HFS_SB(sb)->mdb = mdb; /* These parameters are read from the MDB, and never written */ @@ -187,11 +227,18 @@ int hfs_mdb_get(struct super_block *sb) /* TRY to get the alternate (backup) MDB. */ sect = part_start + part_size - 2; - bh = sb_bread512(sb, sect, mdb2); + bh = sb_bread512(sb, sect, alt_mdb); if (bh) { - if (mdb2->drSigWord == cpu_to_be16(HFS_SUPER_MAGIC)) { - HFS_SB(sb)->alt_mdb_bh = bh; - HFS_SB(sb)->alt_mdb = mdb2; + if (alt_mdb->drSigWord == cpu_to_be16(HFS_SUPER_MAGIC)) { + alt_mdb = kmemdup(alt_mdb, HFS_SECTOR_SIZE, GFP_KERNEL); + if (alt_mdb) { + HFS_SB(sb)->alt_mdb_bh = bh; + HFS_SB(sb)->alt_mdb_offset = + hfs_sect_offset(sb, sect); + HFS_SB(sb)->alt_mdb = alt_mdb; + } else { + brelse(bh); + } } else brelse(bh); } @@ -253,7 +300,7 @@ int hfs_mdb_get(struct super_block *sb) be32_add_cpu(&mdb->drWrCnt, 1); mdb->drLsMod = hfs_mtime(); - mark_buffer_dirty(HFS_SB(sb)->mdb_bh); + hfs_mdb_publish(HFS_SB(sb)); sync_dirty_buffer(HFS_SB(sb)->mdb_bh); } @@ -300,7 +347,6 @@ int hfs_mdb_commit(struct super_block *sb) return -EIO; } - lock_buffer(HFS_SB(sb)->mdb_bh); if (test_and_clear_bit(HFS_FLG_MDB_DIRTY, &HFS_SB(sb)->flags)) { /* These parameters may have been modified, so write them back */ mdb->drLsMod = hfs_mtime(); @@ -314,8 +360,14 @@ int hfs_mdb_commit(struct super_block *sb) mdb->drDirCnt = cpu_to_be32((u32)atomic64_read(&HFS_SB(sb)->folder_count)); + hfs_inode_write_fork(HFS_SB(sb)->ext_tree->inode, mdb->drXTExtRec, + &mdb->drXTFlSize, NULL); + hfs_inode_write_fork(HFS_SB(sb)->cat_tree->inode, mdb->drCTExtRec, + &mdb->drCTFlSize, NULL); + /* write MDB to disk */ - mark_buffer_dirty(HFS_SB(sb)->mdb_bh); + hfs_mdb_publish(HFS_SB(sb)); + sync_dirty_buffer(HFS_SB(sb)->mdb_bh); } /* write the backup MDB, not returning until it is written. @@ -330,18 +382,11 @@ int hfs_mdb_commit(struct super_block *sb) goto out; } - hfs_inode_write_fork(HFS_SB(sb)->ext_tree->inode, mdb->drXTExtRec, - &mdb->drXTFlSize, NULL); - hfs_inode_write_fork(HFS_SB(sb)->cat_tree->inode, mdb->drCTExtRec, - &mdb->drCTFlSize, NULL); - - lock_buffer(HFS_SB(sb)->alt_mdb_bh); - memcpy(HFS_SB(sb)->alt_mdb, HFS_SB(sb)->mdb, HFS_SECTOR_SIZE); + memcpy(HFS_SB(sb)->alt_mdb, mdb, HFS_SECTOR_SIZE); HFS_SB(sb)->alt_mdb->drAtrb |= cpu_to_be16(HFS_SB_ATTRIB_UNMNT); HFS_SB(sb)->alt_mdb->drAtrb &= cpu_to_be16(~HFS_SB_ATTRIB_INCNSTNT); - unlock_buffer(HFS_SB(sb)->alt_mdb_bh); - mark_buffer_dirty(HFS_SB(sb)->alt_mdb_bh); + hfs_alt_mdb_publish(HFS_SB(sb)); sync_dirty_buffer(HFS_SB(sb)->alt_mdb_bh); } @@ -377,7 +422,6 @@ int hfs_mdb_commit(struct super_block *sb) } } out: - unlock_buffer(HFS_SB(sb)->mdb_bh); return ret; } @@ -392,7 +436,7 @@ void hfs_mdb_close(struct super_block *sb) HFS_SB(sb)->mdb->drAtrb |= cpu_to_be16(HFS_SB_ATTRIB_UNMNT); HFS_SB(sb)->mdb->drAtrb &= cpu_to_be16(~HFS_SB_ATTRIB_INCNSTNT); - mark_buffer_dirty(HFS_SB(sb)->mdb_bh); + hfs_mdb_publish(HFS_SB(sb)); } /* @@ -408,6 +452,8 @@ void hfs_mdb_put(struct super_block *sb) /* free the buffers holding the primary and alternate MDBs */ brelse(HFS_SB(sb)->mdb_bh); brelse(HFS_SB(sb)->alt_mdb_bh); + kfree(HFS_SB(sb)->mdb); + kfree(HFS_SB(sb)->alt_mdb); unload_nls(HFS_SB(sb)->nls_io); unload_nls(HFS_SB(sb)->nls_disk); diff --git a/fs/hfs/super.c b/fs/hfs/super.c index f203e3240fae..0713969b8af2 100644 --- a/fs/hfs/super.c +++ b/fs/hfs/super.c @@ -34,8 +34,14 @@ MODULE_LICENSE("GPL"); static int hfs_sync_fs(struct super_block *sb, int wait) { + int ret; + + mutex_lock(&HFS_SB(sb)->mdb_lock); is_hfs_cnid_counts_valid(sb); - return hfs_mdb_commit(sb); + ret = hfs_mdb_commit(sb); + mutex_unlock(&HFS_SB(sb)->mdb_lock); + + return ret; } /* @@ -65,9 +71,10 @@ static void flush_mdb(struct work_struct *work) sbi->work_queued = 0; spin_unlock(&sbi->work_lock); + mutex_lock(&sbi->mdb_lock); is_hfs_cnid_counts_valid(sb); - hfs_mdb_commit(sb); + mutex_unlock(&sbi->mdb_lock); } void hfs_mark_mdb_dirty(struct super_block *sb) @@ -338,9 +345,12 @@ static int hfs_fill_super(struct super_block *sb, struct fs_context *fc) sb->s_op = &hfs_super_operations; sb->s_xattr = hfs_xattr_handlers; sb->s_flags |= SB_NOATIME | SB_NODIRATIME; + mutex_init(&sbi->mdb_lock); mutex_init(&sbi->bitmap_lock); + mutex_lock(&sbi->mdb_lock); res = hfs_mdb_get(sb); + mutex_unlock(&sbi->mdb_lock); if (res) { if (!silent) pr_warn("can't find a HFS filesystem on dev %s\n",