From 6f80dfe55fcbde1e588bc2cfa1273403b710872d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 7 Nov 2010 23:01:17 +0100 Subject: hfsplus: fix option parsing during remount hfsplus only actually uses the force option during remount, but it uses the full option parser with a fake superblock to do so. This means remount will fail if any nls option is set (which happens frequently with older mount tools), even if it is the same. Fix this by adding a simpler version of the parser that only parses the force option for remount. Signed-off-by: Christoph Hellwig --- fs/hfsplus/super.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'fs/hfsplus/super.c') diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 52cc746..1c356a2 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -263,11 +263,9 @@ static int hfsplus_remount(struct super_block *sb, int *flags, char *data) return 0; if (!(*flags & MS_RDONLY)) { struct hfsplus_vh *vhdr = HFSPLUS_SB(sb)->s_vhdr; - struct hfsplus_sb_info sbi; + int force = 0; - memset(&sbi, 0, sizeof(struct hfsplus_sb_info)); - sbi.nls = HFSPLUS_SB(sb)->nls; - if (!hfsplus_parse_options(data, &sbi)) + if (!hfsplus_parse_options_remount(data, &force)) return -EINVAL; if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) { @@ -275,7 +273,7 @@ static int hfsplus_remount(struct super_block *sb, int *flags, char *data) "running fsck.hfsplus is recommended. leaving read-only.\n"); sb->s_flags |= MS_RDONLY; *flags |= MS_RDONLY; - } else if (test_bit(HFSPLUS_SB_FORCE, &sbi.flags)) { + } else if (force) { /* nothing */ } else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) { printk(KERN_WARNING "hfs: filesystem is marked locked, leaving read-only.\n"); -- cgit v1.1 From 6d1bbfc4c0458c514126ccf7d6ce9232d9dbc872 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 23 Nov 2010 14:37:40 +0100 Subject: hfsplus: silence a few debug printks Turn a few noisy debug printks that show up during xfstests into complied out debug print statements. Signed-off-by: Christoph Hellwig --- fs/hfsplus/super.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'fs/hfsplus/super.c') diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 1c356a2..9bda9fd 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -451,8 +451,6 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) sync_dirty_buffer(sbi->s_vhbh); if (!sbi->hidden_dir) { - printk(KERN_DEBUG "hfs: create hidden dir...\n"); - mutex_lock(&sbi->vh_mutex); sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR); hfsplus_create_cat(sbi->hidden_dir->i_ino, sb->s_root->d_inode, -- cgit v1.1 From 3b5ce8ae31e3c66655207907527476bbd3e5063b Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 23 Nov 2010 14:37:43 +0100 Subject: hfsplus: always use hfsplus_sync_fs to write the volume header Remove opencoded writing of the volume header in hfsplus_fill_super and hfsplus_put_super and offload it to hfsplus_sync_fs. In the put_super case this means we only write the superblock once instead of twice. Signed-off-by: Christoph Hellwig --- fs/hfsplus/super.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'fs/hfsplus/super.c') diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 9bda9fd..6a23490 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -215,16 +215,14 @@ static void hfsplus_put_super(struct super_block *sb) if (!sb->s_fs_info) return; - if (sb->s_dirt) - hfsplus_write_super(sb); if (!(sb->s_flags & MS_RDONLY) && sbi->s_vhdr) { struct hfsplus_vh *vhdr = sbi->s_vhdr; vhdr->modify_date = hfsp_now2mt(); vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_UNMNT); vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_INCNSTNT); - mark_buffer_dirty(sbi->s_vhbh); - sync_dirty_buffer(sbi->s_vhbh); + + hfsplus_sync_fs(sb, 1); } hfs_btree_close(sbi->cat_tree); @@ -447,8 +445,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) be32_add_cpu(&vhdr->write_count, 1); vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT); vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT); - mark_buffer_dirty(sbi->s_vhbh); - sync_dirty_buffer(sbi->s_vhbh); + hfsplus_sync_fs(sb, 1); if (!sbi->hidden_dir) { mutex_lock(&sbi->vh_mutex); -- cgit v1.1 From 52399b171dfaea02b6944cd6feba49b624147126 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 23 Nov 2010 14:37:47 +0100 Subject: 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 --- fs/hfsplus/super.c | 48 ++++++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 26 deletions(-) (limited to 'fs/hfsplus/super.c') diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 6a23490..fe8f7bf 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; -- cgit v1.1 From 7dc4f001123f9ebe3b010a6c26acd18698ad205f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 23 Nov 2010 14:37:57 +0100 Subject: hfsplus: make sure sync writes out all metadata hfsplus stores all metadata except for the volume headers in special inodes. While these are marked hashed and periodically written out by the flusher threads, we can't rely on that for sync. For the case of a data integrity sync the VM has life-lock avoidance code that avoids writing inodes again that are redirtied during the sync, which is something that can happen easily for hfsplus. So make sure we explicitly write out the metadata inodes at the beginning of hfsplus_sync_fs. Signed-off-by: Christoph Hellwig --- fs/hfsplus/super.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'fs/hfsplus/super.c') diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index fe8f7bf..df6bea0 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -164,6 +164,22 @@ int hfsplus_sync_fs(struct super_block *sb, int wait) sb->s_dirt = 0; + /* + * Explicitly write out the special metadata inodes. + * + * While these special inodes are marked as hashed and written + * out peridocically by the flusher threads we redirty them + * during writeout of normal inodes, and thus the life lock + * prevents us from getting the latest state to disk. + */ + error = filemap_write_and_wait(sbi->cat_tree->inode->i_mapping); + error2 = filemap_write_and_wait(sbi->ext_tree->inode->i_mapping); + if (!error) + error = error2; + error2 = filemap_write_and_wait(sbi->alloc_file->i_mapping); + if (!error) + error = error2; + mutex_lock(&sbi->vh_mutex); mutex_lock(&sbi->alloc_mutex); vhdr->free_blocks = cpu_to_be32(sbi->free_blocks); @@ -176,9 +192,11 @@ int hfsplus_sync_fs(struct super_block *sb, int wait) write_backup = 1; } - error = hfsplus_submit_bio(sb->s_bdev, + error2 = hfsplus_submit_bio(sb->s_bdev, sbi->part_start + HFSPLUS_VOLHEAD_SECTOR, sbi->s_vhdr, WRITE_SYNC); + if (!error) + error = error2; if (!write_backup) goto out; -- cgit v1.1 From f02e26f8d90f8cde98314c72c2e890bc281a8346 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 23 Nov 2010 14:38:02 +0100 Subject: hfsplus: avoid useless work in hfsplus_sync_fs There is no reason to write out the metadata inodes or volume headers during a non-blocking sync, as we are almost guaranteed to dirty them again during the inode writeouts. Signed-off-by: Christoph Hellwig --- fs/hfsplus/super.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'fs/hfsplus/super.c') diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index df6bea0..56e6cf8 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -160,6 +160,9 @@ int hfsplus_sync_fs(struct super_block *sb, int wait) int write_backup = 0; int error, error2; + if (!wait) + return 0; + dprint(DBG_SUPER, "hfsplus_write_super\n"); sb->s_dirt = 0; -- cgit v1.1 From b33b7921db14abcd10c30d0ccfc68e364f5ef7fe Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 23 Nov 2010 14:38:13 +0100 Subject: hfsplus: split up inode flags Split the flags field in the hfsplus inode into an extent_state flag that is locked by the extent_lock, and a new flags field that uses atomic bitops. The second will grow more flags in the next patch. Signed-off-by: Christoph Hellwig --- fs/hfsplus/super.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs/hfsplus/super.c') diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 56e6cf8..9854237 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -66,6 +66,7 @@ struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino) INIT_LIST_HEAD(&HFSPLUS_I(inode)->open_dir_list); mutex_init(&HFSPLUS_I(inode)->extents_lock); HFSPLUS_I(inode)->flags = 0; + HFSPLUS_I(inode)->extent_state = 0; HFSPLUS_I(inode)->rsrc_inode = NULL; atomic_set(&HFSPLUS_I(inode)->opencnt, 0); -- cgit v1.1 From e34947056076ca5467ee8256d2d9cbc594a79b37 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 23 Nov 2010 14:38:15 +0100 Subject: hfsplus: optimize fsync Avoid doing unessecary work in fsync. Do nothing unless the inode was marked dirty, and only write the various metadata inodes out if they contain any dirty state from this inode. This is archived by adding three new dirty bits to the hfsplus-specific inode which are set in the correct places. Signed-off-by: Christoph Hellwig --- fs/hfsplus/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/hfsplus/super.c') diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 9854237..0366501 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -472,7 +472,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) &str, sbi->hidden_dir); mutex_unlock(&sbi->vh_mutex); - mark_inode_dirty(sbi->hidden_dir); + hfsplus_mark_inode_dirty(sbi->hidden_dir, HFSPLUS_I_CAT_DIRTY); } out: unload_nls(sbi->nls); -- cgit v1.1 From 34a2d313c51f47cae50ccb89f4196462665f2c48 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 23 Nov 2010 14:38:21 +0100 Subject: hfsplus: flush disk caches in sync and fsync Flush the disk cache in fsync and sync to make sure data actually is on disk on completion of these system calls. There is a nobarrier mount option to disable this behaviour. It's slightly misnamed now that barrier actually are gone, but it matches the name used by all major filesystems. Signed-off-by: Christoph Hellwig --- fs/hfsplus/super.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'fs/hfsplus/super.c') diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 0366501..154478c 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -212,6 +213,10 @@ int hfsplus_sync_fs(struct super_block *sb, int wait) out: mutex_unlock(&sbi->alloc_mutex); mutex_unlock(&sbi->vh_mutex); + + if (!test_bit(HFSPLUS_SB_NOBARRIER, &sbi->flags)) + blkdev_issue_flush(sb->s_bdev, GFP_KERNEL, NULL); + return error; } -- cgit v1.1 From 2753cc281c9a0e8a0a45ee2b8110866a9fe63bdd Mon Sep 17 00:00:00 2001 From: Anton Salikhmetov Date: Thu, 16 Dec 2010 18:08:38 +0200 Subject: hfsplus: over 80 character lines clean-up Match coding style line length limitation where checkpatch.pl reported over-80-character-line warnings. Signed-off-by: Anton Salikhmetov Signed-off-by: Christoph Hellwig --- fs/hfsplus/super.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) (limited to 'fs/hfsplus/super.c') diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 154478c..3c9f30e 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -290,18 +290,25 @@ static int hfsplus_remount(struct super_block *sb, int *flags, char *data) return -EINVAL; if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) { - printk(KERN_WARNING "hfs: filesystem was not cleanly unmounted, " - "running fsck.hfsplus is recommended. leaving read-only.\n"); + printk(KERN_WARNING "hfs: filesystem was " + "not cleanly unmounted, " + "running fsck.hfsplus is recommended. " + "leaving read-only.\n"); sb->s_flags |= MS_RDONLY; *flags |= MS_RDONLY; } else if (force) { /* nothing */ - } else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) { - printk(KERN_WARNING "hfs: filesystem is marked locked, leaving read-only.\n"); + } else if (vhdr->attributes & + cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) { + printk(KERN_WARNING "hfs: filesystem is marked locked, " + "leaving read-only.\n"); sb->s_flags |= MS_RDONLY; *flags |= MS_RDONLY; - } else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) { - printk(KERN_WARNING "hfs: filesystem is marked journaled, leaving read-only.\n"); + } else if (vhdr->attributes & + cpu_to_be32(HFSPLUS_VOL_JOURNALED)) { + printk(KERN_WARNING "hfs: filesystem is " + "marked journaled, " + "leaving read-only.\n"); sb->s_flags |= MS_RDONLY; *flags |= MS_RDONLY; } @@ -391,17 +398,22 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) sb->s_maxbytes = MAX_LFS_FILESIZE; if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) { - printk(KERN_WARNING "hfs: Filesystem was not cleanly unmounted, " - "running fsck.hfsplus is recommended. mounting read-only.\n"); + printk(KERN_WARNING "hfs: Filesystem was " + "not cleanly unmounted, " + "running fsck.hfsplus is recommended. " + "mounting read-only.\n"); sb->s_flags |= MS_RDONLY; } else if (test_and_clear_bit(HFSPLUS_SB_FORCE, &sbi->flags)) { /* nothing */ } else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) { printk(KERN_WARNING "hfs: Filesystem is marked locked, mounting read-only.\n"); sb->s_flags |= MS_RDONLY; - } else if ((vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) && !(sb->s_flags & MS_RDONLY)) { - printk(KERN_WARNING "hfs: write access to a journaled filesystem is not supported, " - "use the force option at your own risk, mounting read-only.\n"); + } else if ((vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) && + !(sb->s_flags & MS_RDONLY)) { + printk(KERN_WARNING "hfs: write access to " + "a journaled filesystem is not supported, " + "use the force option at your own risk, " + "mounting read-only.\n"); sb->s_flags |= MS_RDONLY; } -- cgit v1.1