|
@@ -88,7 +88,7 @@ static unsigned char btrfs_type_by_mode[S_IFMT >> S_SHIFT] = {
|
|
|
[S_IFLNK >> S_SHIFT] = BTRFS_FT_SYMLINK,
|
|
|
};
|
|
|
|
|
|
-static int btrfs_setsize(struct inode *inode, loff_t newsize);
|
|
|
+static int btrfs_setsize(struct inode *inode, struct iattr *attr);
|
|
|
static int btrfs_truncate(struct inode *inode);
|
|
|
static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent);
|
|
|
static noinline int cow_file_range(struct inode *inode,
|
|
@@ -2478,6 +2478,18 @@ int btrfs_orphan_cleanup(struct btrfs_root *root)
|
|
|
continue;
|
|
|
}
|
|
|
nr_truncate++;
|
|
|
+
|
|
|
+ /* 1 for the orphan item deletion. */
|
|
|
+ trans = btrfs_start_transaction(root, 1);
|
|
|
+ if (IS_ERR(trans)) {
|
|
|
+ ret = PTR_ERR(trans);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ ret = btrfs_orphan_add(trans, inode);
|
|
|
+ btrfs_end_transaction(trans, root);
|
|
|
+ if (ret)
|
|
|
+ goto out;
|
|
|
+
|
|
|
ret = btrfs_truncate(inode);
|
|
|
} else {
|
|
|
nr_unlink++;
|
|
@@ -3665,6 +3677,7 @@ int btrfs_cont_expand(struct inode *inode, loff_t oldsize, loff_t size)
|
|
|
block_end - cur_offset, 0);
|
|
|
if (IS_ERR(em)) {
|
|
|
err = PTR_ERR(em);
|
|
|
+ em = NULL;
|
|
|
break;
|
|
|
}
|
|
|
last_byte = min(extent_map_end(em), block_end);
|
|
@@ -3748,16 +3761,27 @@ next:
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
-static int btrfs_setsize(struct inode *inode, loff_t newsize)
|
|
|
+static int btrfs_setsize(struct inode *inode, struct iattr *attr)
|
|
|
{
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
struct btrfs_trans_handle *trans;
|
|
|
loff_t oldsize = i_size_read(inode);
|
|
|
+ loff_t newsize = attr->ia_size;
|
|
|
+ int mask = attr->ia_valid;
|
|
|
int ret;
|
|
|
|
|
|
if (newsize == oldsize)
|
|
|
return 0;
|
|
|
|
|
|
+ /*
|
|
|
+ * The regular truncate() case without ATTR_CTIME and ATTR_MTIME is a
|
|
|
+ * special case where we need to update the times despite not having
|
|
|
+ * these flags set. For all other operations the VFS set these flags
|
|
|
+ * explicitly if it wants a timestamp update.
|
|
|
+ */
|
|
|
+ if (newsize != oldsize && (!(mask & (ATTR_CTIME | ATTR_MTIME))))
|
|
|
+ inode->i_ctime = inode->i_mtime = current_fs_time(inode->i_sb);
|
|
|
+
|
|
|
if (newsize > oldsize) {
|
|
|
truncate_pagecache(inode, oldsize, newsize);
|
|
|
ret = btrfs_cont_expand(inode, oldsize, newsize);
|
|
@@ -3783,9 +3807,34 @@ static int btrfs_setsize(struct inode *inode, loff_t newsize)
|
|
|
set_bit(BTRFS_INODE_ORDERED_DATA_CLOSE,
|
|
|
&BTRFS_I(inode)->runtime_flags);
|
|
|
|
|
|
+ /*
|
|
|
+ * 1 for the orphan item we're going to add
|
|
|
+ * 1 for the orphan item deletion.
|
|
|
+ */
|
|
|
+ trans = btrfs_start_transaction(root, 2);
|
|
|
+ if (IS_ERR(trans))
|
|
|
+ return PTR_ERR(trans);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We need to do this in case we fail at _any_ point during the
|
|
|
+ * actual truncate. Once we do the truncate_setsize we could
|
|
|
+ * invalidate pages which forces any outstanding ordered io to
|
|
|
+ * be instantly completed which will give us extents that need
|
|
|
+ * to be truncated. If we fail to get an orphan inode down we
|
|
|
+ * could have left over extents that were never meant to live,
|
|
|
+ * so we need to garuntee from this point on that everything
|
|
|
+ * will be consistent.
|
|
|
+ */
|
|
|
+ ret = btrfs_orphan_add(trans, inode);
|
|
|
+ btrfs_end_transaction(trans, root);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
/* we don't support swapfiles, so vmtruncate shouldn't fail */
|
|
|
truncate_setsize(inode, newsize);
|
|
|
ret = btrfs_truncate(inode);
|
|
|
+ if (ret && inode->i_nlink)
|
|
|
+ btrfs_orphan_del(NULL, inode);
|
|
|
}
|
|
|
|
|
|
return ret;
|
|
@@ -3805,7 +3854,7 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
|
|
|
return err;
|
|
|
|
|
|
if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) {
|
|
|
- err = btrfs_setsize(inode, attr->ia_size);
|
|
|
+ err = btrfs_setsize(inode, attr);
|
|
|
if (err)
|
|
|
return err;
|
|
|
}
|
|
@@ -5586,10 +5635,13 @@ struct extent_map *btrfs_get_extent_fiemap(struct inode *inode, struct page *pag
|
|
|
return em;
|
|
|
if (em) {
|
|
|
/*
|
|
|
- * if our em maps to a hole, there might
|
|
|
- * actually be delalloc bytes behind it
|
|
|
+ * if our em maps to
|
|
|
+ * - a hole or
|
|
|
+ * - a pre-alloc extent,
|
|
|
+ * there might actually be delalloc bytes behind it.
|
|
|
*/
|
|
|
- if (em->block_start != EXTENT_MAP_HOLE)
|
|
|
+ if (em->block_start != EXTENT_MAP_HOLE &&
|
|
|
+ !test_bit(EXTENT_FLAG_PREALLOC, &em->flags))
|
|
|
return em;
|
|
|
else
|
|
|
hole_em = em;
|
|
@@ -5671,6 +5723,8 @@ struct extent_map *btrfs_get_extent_fiemap(struct inode *inode, struct page *pag
|
|
|
*/
|
|
|
em->block_start = hole_em->block_start;
|
|
|
em->block_len = hole_len;
|
|
|
+ if (test_bit(EXTENT_FLAG_PREALLOC, &hole_em->flags))
|
|
|
+ set_bit(EXTENT_FLAG_PREALLOC, &em->flags);
|
|
|
} else {
|
|
|
em->start = range_start;
|
|
|
em->len = found;
|
|
@@ -6929,11 +6983,9 @@ static int btrfs_truncate(struct inode *inode)
|
|
|
|
|
|
/*
|
|
|
* 1 for the truncate slack space
|
|
|
- * 1 for the orphan item we're going to add
|
|
|
- * 1 for the orphan item deletion
|
|
|
* 1 for updating the inode.
|
|
|
*/
|
|
|
- trans = btrfs_start_transaction(root, 4);
|
|
|
+ trans = btrfs_start_transaction(root, 2);
|
|
|
if (IS_ERR(trans)) {
|
|
|
err = PTR_ERR(trans);
|
|
|
goto out;
|
|
@@ -6944,12 +6996,6 @@ static int btrfs_truncate(struct inode *inode)
|
|
|
min_size);
|
|
|
BUG_ON(ret);
|
|
|
|
|
|
- ret = btrfs_orphan_add(trans, inode);
|
|
|
- if (ret) {
|
|
|
- btrfs_end_transaction(trans, root);
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
/*
|
|
|
* setattr is responsible for setting the ordered_data_close flag,
|
|
|
* but that is only tested during the last file release. That
|
|
@@ -7018,12 +7064,6 @@ static int btrfs_truncate(struct inode *inode)
|
|
|
ret = btrfs_orphan_del(trans, inode);
|
|
|
if (ret)
|
|
|
err = ret;
|
|
|
- } else if (ret && inode->i_nlink > 0) {
|
|
|
- /*
|
|
|
- * Failed to do the truncate, remove us from the in memory
|
|
|
- * orphan list.
|
|
|
- */
|
|
|
- ret = btrfs_orphan_del(NULL, inode);
|
|
|
}
|
|
|
|
|
|
if (trans) {
|