summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/hfsplus/catalog.c10
-rw-r--r--fs/hfsplus/extents.c30
-rw-r--r--fs/hfsplus/hfsplus_fs.h37
-rw-r--r--fs/hfsplus/inode.c27
-rw-r--r--fs/hfsplus/ioctl.c4
-rw-r--r--fs/hfsplus/super.c2
6 files changed, 78 insertions, 32 deletions
diff --git a/fs/hfsplus/catalog.c b/fs/hfsplus/catalog.c
index 8af45fc5b05..0aa40b36a9e 100644
--- a/fs/hfsplus/catalog.c
+++ b/fs/hfsplus/catalog.c
@@ -227,7 +227,8 @@ int hfsplus_create_cat(u32 cnid, struct inode *dir, struct qstr *str, struct ino
dir->i_size++;
dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
- mark_inode_dirty(dir);
+ hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY);
+
hfs_find_exit(&fd);
return 0;
@@ -308,7 +309,7 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str)
dir->i_size--;
dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
- mark_inode_dirty(dir);
+ hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY);
out:
hfs_find_exit(&fd);
@@ -353,7 +354,6 @@ int hfsplus_rename_cat(u32 cnid,
goto out;
dst_dir->i_size++;
dst_dir->i_mtime = dst_dir->i_ctime = CURRENT_TIME_SEC;
- mark_inode_dirty(dst_dir);
/* finally remove the old entry */
hfsplus_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name);
@@ -365,7 +365,6 @@ int hfsplus_rename_cat(u32 cnid,
goto out;
src_dir->i_size--;
src_dir->i_mtime = src_dir->i_ctime = CURRENT_TIME_SEC;
- mark_inode_dirty(src_dir);
/* remove old thread entry */
hfsplus_cat_build_key(sb, src_fd.search_key, cnid, NULL);
@@ -387,6 +386,9 @@ int hfsplus_rename_cat(u32 cnid,
goto out;
}
err = hfs_brec_insert(&dst_fd, &entry, entry_size);
+
+ hfsplus_mark_inode_dirty(dst_dir, HFSPLUS_I_CAT_DIRTY);
+ hfsplus_mark_inode_dirty(src_dir, HFSPLUS_I_CAT_DIRTY);
out:
hfs_bnode_put(dst_fd.bnode);
hfs_find_exit(&src_fd);
diff --git a/fs/hfsplus/extents.c b/fs/hfsplus/extents.c
index 06b4fc3151a..b1127ef2675 100644
--- a/fs/hfsplus/extents.c
+++ b/fs/hfsplus/extents.c
@@ -108,6 +108,14 @@ static void __hfsplus_ext_write_extent(struct inode *inode, struct hfs_find_data
fd->entryoffset, fd->entrylength);
hip->extent_state &= ~HFSPLUS_EXT_DIRTY;
}
+
+ /*
+ * We can't just use hfsplus_mark_inode_dirty here, because we
+ * also get called from hfsplus_write_inode, which should not
+ * redirty the inode. Instead the callers have to be careful
+ * to explicily mark the inode dirty, too.
+ */
+ set_bit(HFSPLUS_I_EXT_DIRTY, &hip->flags);
}
static void hfsplus_ext_write_extent_locked(struct inode *inode)
@@ -197,6 +205,7 @@ int hfsplus_get_block(struct inode *inode, sector_t iblock,
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
int res = -EIO;
u32 ablock, dblock, mask;
+ int was_dirty = 0;
int shift;
/* Convert inode block to disk allocation block */
@@ -223,14 +232,20 @@ int hfsplus_get_block(struct inode *inode, sector_t iblock,
return -EIO;
mutex_lock(&hip->extents_lock);
+
+ /*
+ * hfsplus_ext_read_extent will write out a cached extent into
+ * the extents btree. In that case we may have to mark the inode
+ * dirty even for a pure read of an extent here.
+ */
+ was_dirty = (hip->extent_state & HFSPLUS_EXT_DIRTY);
res = hfsplus_ext_read_extent(inode, ablock);
- if (!res) {
- dblock = hfsplus_ext_find_block(hip->cached_extents,
- ablock - hip->cached_start);
- } else {
+ if (res) {
mutex_unlock(&hip->extents_lock);
return -EIO;
}
+ dblock = hfsplus_ext_find_block(hip->cached_extents,
+ ablock - hip->cached_start);
mutex_unlock(&hip->extents_lock);
done:
@@ -242,8 +257,9 @@ done:
hip->phys_size += sb->s_blocksize;
hip->fs_blocks++;
inode_add_bytes(inode, sb->s_blocksize);
- mark_inode_dirty(inode);
}
+ if (create || was_dirty)
+ mark_inode_dirty(inode);
return 0;
}
@@ -438,7 +454,7 @@ out:
mutex_unlock(&hip->extents_lock);
if (!res) {
hip->alloc_blocks += len;
- mark_inode_dirty(inode);
+ hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ALLOC_DIRTY);
}
return res;
@@ -529,5 +545,5 @@ out:
hip->phys_size = inode->i_size;
hip->fs_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
inode_set_bytes(inode, hip->fs_blocks << sb->s_blocksize_bits);
- mark_inode_dirty(inode);
+ hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ALLOC_DIRTY);
}
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
index 9889d0033b8..65c698f78ef 100644
--- a/fs/hfsplus/hfsplus_fs.h
+++ b/fs/hfsplus/hfsplus_fs.h
@@ -157,6 +157,11 @@ struct hfsplus_sb_info {
#define HFSPLUS_SB_HFSX 3
#define HFSPLUS_SB_CASEFOLD 4
+static inline struct hfsplus_sb_info *HFSPLUS_SB(struct super_block *sb)
+{
+ return sb->s_fs_info;
+}
+
struct hfsplus_inode_info {
atomic_t opencnt;
@@ -205,10 +210,31 @@ struct hfsplus_inode_info {
#define HFSPLUS_EXT_NEW 0x0002
#define HFSPLUS_I_RSRC 0 /* represents a resource fork */
+#define HFSPLUS_I_CAT_DIRTY 1 /* has changes in the catalog tree */
+#define HFSPLUS_I_EXT_DIRTY 2 /* has changes in the extent tree */
+#define HFSPLUS_I_ALLOC_DIRTY 3 /* has changes in the allocation file */
#define HFSPLUS_IS_RSRC(inode) \
test_bit(HFSPLUS_I_RSRC, &HFSPLUS_I(inode)->flags)
+static inline struct hfsplus_inode_info *HFSPLUS_I(struct inode *inode)
+{
+ return list_entry(inode, struct hfsplus_inode_info, vfs_inode);
+}
+
+/*
+ * Mark an inode dirty, and also mark the btree in which the
+ * specific type of metadata is stored.
+ * For data or metadata that gets written back by into the catalog btree
+ * by hfsplus_write_inode a plain mark_inode_dirty call is enough.
+ */
+static inline void hfsplus_mark_inode_dirty(struct inode *inode,
+ unsigned int flag)
+{
+ set_bit(flag, &HFSPLUS_I(inode)->flags);
+ mark_inode_dirty(inode);
+}
+
struct hfs_find_data {
/* filled by caller */
hfsplus_btree_key *search_key;
@@ -397,17 +423,6 @@ 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)
-{
- return sb->s_fs_info;
-}
-
-static inline struct hfsplus_inode_info *HFSPLUS_I(struct inode *inode)
-{
- return list_entry(inode, struct hfsplus_inode_info, vfs_inode);
-}
-
/* time macros */
#define __hfsp_mt2ut(t) (be32_to_cpu(t) - 2082844800U)
#define __hfsp_ut2mt(t) (cpu_to_be32(t + 2082844800U))
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
index 0946e2cdca5..bf6535b7326 100644
--- a/fs/hfsplus/inode.c
+++ b/fs/hfsplus/inode.c
@@ -307,8 +307,9 @@ static int hfsplus_setattr(struct dentry *dentry, struct iattr *attr)
int hfsplus_file_fsync(struct file *file, int datasync)
{
struct inode *inode = file->f_mapping->host;
+ struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb);
- int error, error2;
+ int error = 0, error2;
/*
* Sync inode metadata into the catalog and extent trees.
@@ -318,13 +319,21 @@ int hfsplus_file_fsync(struct file *file, int datasync)
/*
* And explicitly write out the btrees.
*/
- 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;
+ if (test_and_clear_bit(HFSPLUS_I_CAT_DIRTY, &hip->flags))
+ error = filemap_write_and_wait(sbi->cat_tree->inode->i_mapping);
+
+ if (test_and_clear_bit(HFSPLUS_I_EXT_DIRTY, &hip->flags)) {
+ error2 = filemap_write_and_wait(sbi->ext_tree->inode->i_mapping);
+ if (!error)
+ error = error2;
+ }
+
+ if (test_and_clear_bit(HFSPLUS_I_ALLOC_DIRTY, &hip->flags)) {
+ error2 = filemap_write_and_wait(sbi->alloc_file->i_mapping);
+ if (!error)
+ error = error2;
+ }
+
return error;
}
@@ -590,6 +599,8 @@ int hfsplus_cat_write_inode(struct inode *inode)
hfs_bnode_write(fd.bnode, &entry, fd.entryoffset,
sizeof(struct hfsplus_cat_file));
}
+
+ set_bit(HFSPLUS_I_CAT_DIRTY, &HFSPLUS_I(inode)->flags);
out:
hfs_find_exit(&fd);
return 0;
diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c
index 40a85a3ded6..f5a7224f4cb 100644
--- a/fs/hfsplus/ioctl.c
+++ b/fs/hfsplus/ioctl.c
@@ -147,9 +147,11 @@ int hfsplus_setxattr(struct dentry *dentry, const char *name,
res = -ERANGE;
} else
res = -EOPNOTSUPP;
- if (!res)
+ if (!res) {
hfs_bnode_write(fd.bnode, &entry, fd.entryoffset,
sizeof(struct hfsplus_cat_file));
+ hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
+ }
out:
hfs_find_exit(&fd);
return res;
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c
index 985423728e1..036650123c4 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);