|
@@ -59,6 +59,7 @@
|
|
|
#include "xattr.h"
|
|
|
#include "acl.h"
|
|
|
#include "quota.h"
|
|
|
+#include "refcounttree.h"
|
|
|
|
|
|
#include "buffer_head_io.h"
|
|
|
|
|
@@ -1656,6 +1657,70 @@ static long ocfs2_fallocate(struct inode *inode, int mode, loff_t offset,
|
|
|
OCFS2_IOC_RESVSP64, &sr, change_size);
|
|
|
}
|
|
|
|
|
|
+int ocfs2_check_range_for_refcount(struct inode *inode, loff_t pos,
|
|
|
+ size_t count)
|
|
|
+{
|
|
|
+ int ret = 0;
|
|
|
+ unsigned int extent_flags;
|
|
|
+ u32 cpos, clusters, extent_len, phys_cpos;
|
|
|
+ struct super_block *sb = inode->i_sb;
|
|
|
+
|
|
|
+ if (!ocfs2_refcount_tree(OCFS2_SB(inode->i_sb)) ||
|
|
|
+ !(OCFS2_I(inode)->ip_dyn_features & OCFS2_HAS_REFCOUNT_FL))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ cpos = pos >> OCFS2_SB(sb)->s_clustersize_bits;
|
|
|
+ clusters = ocfs2_clusters_for_bytes(sb, pos + count) - cpos;
|
|
|
+
|
|
|
+ while (clusters) {
|
|
|
+ ret = ocfs2_get_clusters(inode, cpos, &phys_cpos, &extent_len,
|
|
|
+ &extent_flags);
|
|
|
+ if (ret < 0) {
|
|
|
+ mlog_errno(ret);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (phys_cpos && (extent_flags & OCFS2_EXT_REFCOUNTED)) {
|
|
|
+ ret = 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (extent_len > clusters)
|
|
|
+ extent_len = clusters;
|
|
|
+
|
|
|
+ clusters -= extent_len;
|
|
|
+ cpos += extent_len;
|
|
|
+ }
|
|
|
+out:
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int ocfs2_prepare_inode_for_refcount(struct inode *inode,
|
|
|
+ loff_t pos, size_t count,
|
|
|
+ int *meta_level)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+ struct buffer_head *di_bh = NULL;
|
|
|
+ u32 cpos = pos >> OCFS2_SB(inode->i_sb)->s_clustersize_bits;
|
|
|
+ u32 clusters =
|
|
|
+ ocfs2_clusters_for_bytes(inode->i_sb, pos + count) - cpos;
|
|
|
+
|
|
|
+ ret = ocfs2_inode_lock(inode, &di_bh, 1);
|
|
|
+ if (ret) {
|
|
|
+ mlog_errno(ret);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ *meta_level = 1;
|
|
|
+
|
|
|
+ ret = ocfs2_refcount_cow(inode, di_bh, cpos, clusters);
|
|
|
+ if (ret)
|
|
|
+ mlog_errno(ret);
|
|
|
+out:
|
|
|
+ brelse(di_bh);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static int ocfs2_prepare_inode_for_write(struct dentry *dentry,
|
|
|
loff_t *ppos,
|
|
|
size_t count,
|
|
@@ -1712,6 +1777,22 @@ static int ocfs2_prepare_inode_for_write(struct dentry *dentry,
|
|
|
|
|
|
end = saved_pos + count;
|
|
|
|
|
|
+ ret = ocfs2_check_range_for_refcount(inode, saved_pos, count);
|
|
|
+ if (ret == 1) {
|
|
|
+ ocfs2_inode_unlock(inode, meta_level);
|
|
|
+ meta_level = -1;
|
|
|
+
|
|
|
+ ret = ocfs2_prepare_inode_for_refcount(inode,
|
|
|
+ saved_pos,
|
|
|
+ count,
|
|
|
+ &meta_level);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ret < 0) {
|
|
|
+ mlog_errno(ret);
|
|
|
+ goto out_unlock;
|
|
|
+ }
|
|
|
+
|
|
|
/*
|
|
|
* Skip the O_DIRECT checks if we don't need
|
|
|
* them.
|
|
@@ -1758,7 +1839,8 @@ static int ocfs2_prepare_inode_for_write(struct dentry *dentry,
|
|
|
*ppos = saved_pos;
|
|
|
|
|
|
out_unlock:
|
|
|
- ocfs2_inode_unlock(inode, meta_level);
|
|
|
+ if (meta_level >= 0)
|
|
|
+ ocfs2_inode_unlock(inode, meta_level);
|
|
|
|
|
|
out:
|
|
|
return ret;
|