|
@@ -490,8 +490,20 @@ insert:
|
|
src_item = (struct btrfs_inode_item *)src_ptr;
|
|
src_item = (struct btrfs_inode_item *)src_ptr;
|
|
dst_item = (struct btrfs_inode_item *)dst_ptr;
|
|
dst_item = (struct btrfs_inode_item *)dst_ptr;
|
|
|
|
|
|
- if (btrfs_inode_generation(eb, src_item) == 0)
|
|
|
|
|
|
+ if (btrfs_inode_generation(eb, src_item) == 0) {
|
|
|
|
+ struct extent_buffer *dst_eb = path->nodes[0];
|
|
|
|
+
|
|
|
|
+ if (S_ISREG(btrfs_inode_mode(eb, src_item)) &&
|
|
|
|
+ S_ISREG(btrfs_inode_mode(dst_eb, dst_item))) {
|
|
|
|
+ struct btrfs_map_token token;
|
|
|
|
+ u64 ino_size = btrfs_inode_size(eb, src_item);
|
|
|
|
+
|
|
|
|
+ btrfs_init_map_token(&token);
|
|
|
|
+ btrfs_set_token_inode_size(dst_eb, dst_item,
|
|
|
|
+ ino_size, &token);
|
|
|
|
+ }
|
|
goto no_copy;
|
|
goto no_copy;
|
|
|
|
+ }
|
|
|
|
|
|
if (overwrite_root &&
|
|
if (overwrite_root &&
|
|
S_ISDIR(btrfs_inode_mode(eb, src_item)) &&
|
|
S_ISDIR(btrfs_inode_mode(eb, src_item)) &&
|
|
@@ -3250,7 +3262,8 @@ static int drop_objectid_items(struct btrfs_trans_handle *trans,
|
|
static void fill_inode_item(struct btrfs_trans_handle *trans,
|
|
static void fill_inode_item(struct btrfs_trans_handle *trans,
|
|
struct extent_buffer *leaf,
|
|
struct extent_buffer *leaf,
|
|
struct btrfs_inode_item *item,
|
|
struct btrfs_inode_item *item,
|
|
- struct inode *inode, int log_inode_only)
|
|
|
|
|
|
+ struct inode *inode, int log_inode_only,
|
|
|
|
+ u64 logged_isize)
|
|
{
|
|
{
|
|
struct btrfs_map_token token;
|
|
struct btrfs_map_token token;
|
|
|
|
|
|
@@ -3263,7 +3276,7 @@ static void fill_inode_item(struct btrfs_trans_handle *trans,
|
|
* to say 'update this inode with these values'
|
|
* to say 'update this inode with these values'
|
|
*/
|
|
*/
|
|
btrfs_set_token_inode_generation(leaf, item, 0, &token);
|
|
btrfs_set_token_inode_generation(leaf, item, 0, &token);
|
|
- btrfs_set_token_inode_size(leaf, item, 0, &token);
|
|
|
|
|
|
+ btrfs_set_token_inode_size(leaf, item, logged_isize, &token);
|
|
} else {
|
|
} else {
|
|
btrfs_set_token_inode_generation(leaf, item,
|
|
btrfs_set_token_inode_generation(leaf, item,
|
|
BTRFS_I(inode)->generation,
|
|
BTRFS_I(inode)->generation,
|
|
@@ -3315,7 +3328,7 @@ static int log_inode_item(struct btrfs_trans_handle *trans,
|
|
return ret;
|
|
return ret;
|
|
inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0],
|
|
inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0],
|
|
struct btrfs_inode_item);
|
|
struct btrfs_inode_item);
|
|
- fill_inode_item(trans, path->nodes[0], inode_item, inode, 0);
|
|
|
|
|
|
+ fill_inode_item(trans, path->nodes[0], inode_item, inode, 0, 0);
|
|
btrfs_release_path(path);
|
|
btrfs_release_path(path);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -3324,7 +3337,8 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,
|
|
struct inode *inode,
|
|
struct inode *inode,
|
|
struct btrfs_path *dst_path,
|
|
struct btrfs_path *dst_path,
|
|
struct btrfs_path *src_path, u64 *last_extent,
|
|
struct btrfs_path *src_path, u64 *last_extent,
|
|
- int start_slot, int nr, int inode_only)
|
|
|
|
|
|
+ int start_slot, int nr, int inode_only,
|
|
|
|
+ u64 logged_isize)
|
|
{
|
|
{
|
|
unsigned long src_offset;
|
|
unsigned long src_offset;
|
|
unsigned long dst_offset;
|
|
unsigned long dst_offset;
|
|
@@ -3381,7 +3395,8 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,
|
|
dst_path->slots[0],
|
|
dst_path->slots[0],
|
|
struct btrfs_inode_item);
|
|
struct btrfs_inode_item);
|
|
fill_inode_item(trans, dst_path->nodes[0], inode_item,
|
|
fill_inode_item(trans, dst_path->nodes[0], inode_item,
|
|
- inode, inode_only == LOG_INODE_EXISTS);
|
|
|
|
|
|
+ inode, inode_only == LOG_INODE_EXISTS,
|
|
|
|
+ logged_isize);
|
|
} else {
|
|
} else {
|
|
copy_extent_buffer(dst_path->nodes[0], src, dst_offset,
|
|
copy_extent_buffer(dst_path->nodes[0], src, dst_offset,
|
|
src_offset, ins_sizes[i]);
|
|
src_offset, ins_sizes[i]);
|
|
@@ -3933,6 +3948,33 @@ process:
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int logged_inode_size(struct btrfs_root *log, struct inode *inode,
|
|
|
|
+ struct btrfs_path *path, u64 *size_ret)
|
|
|
|
+{
|
|
|
|
+ struct btrfs_key key;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ key.objectid = btrfs_ino(inode);
|
|
|
|
+ key.type = BTRFS_INODE_ITEM_KEY;
|
|
|
|
+ key.offset = 0;
|
|
|
|
+
|
|
|
|
+ ret = btrfs_search_slot(NULL, log, &key, path, 0, 0);
|
|
|
|
+ if (ret < 0) {
|
|
|
|
+ return ret;
|
|
|
|
+ } else if (ret > 0) {
|
|
|
|
+ *size_ret = i_size_read(inode);
|
|
|
|
+ } else {
|
|
|
|
+ struct btrfs_inode_item *item;
|
|
|
|
+
|
|
|
|
+ item = btrfs_item_ptr(path->nodes[0], path->slots[0],
|
|
|
|
+ struct btrfs_inode_item);
|
|
|
|
+ *size_ret = btrfs_inode_size(path->nodes[0], item);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ btrfs_release_path(path);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
/* log a single inode in the tree log.
|
|
/* log a single inode in the tree log.
|
|
* At least one parent directory for this inode must exist in the tree
|
|
* At least one parent directory for this inode must exist in the tree
|
|
* or be logged already.
|
|
* or be logged already.
|
|
@@ -3970,6 +4012,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
|
|
bool fast_search = false;
|
|
bool fast_search = false;
|
|
u64 ino = btrfs_ino(inode);
|
|
u64 ino = btrfs_ino(inode);
|
|
struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
|
|
struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
|
|
|
|
+ u64 logged_isize = 0;
|
|
|
|
|
|
path = btrfs_alloc_path();
|
|
path = btrfs_alloc_path();
|
|
if (!path)
|
|
if (!path)
|
|
@@ -4030,6 +4073,25 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
|
|
max_key_type = BTRFS_XATTR_ITEM_KEY;
|
|
max_key_type = BTRFS_XATTR_ITEM_KEY;
|
|
ret = drop_objectid_items(trans, log, path, ino, max_key_type);
|
|
ret = drop_objectid_items(trans, log, path, ino, max_key_type);
|
|
} else {
|
|
} else {
|
|
|
|
+ if (inode_only == LOG_INODE_EXISTS) {
|
|
|
|
+ /*
|
|
|
|
+ * Make sure the new inode item we write to the log has
|
|
|
|
+ * the same isize as the current one (if it exists).
|
|
|
|
+ * This is necessary to prevent data loss after log
|
|
|
|
+ * replay, and also to prevent doing a wrong expanding
|
|
|
|
+ * truncate - for e.g. create file, write 4K into offset
|
|
|
|
+ * 0, fsync, write 4K into offset 4096, add hard link,
|
|
|
|
+ * fsync some other file (to sync log), power fail - if
|
|
|
|
+ * we use the inode's current i_size, after log replay
|
|
|
|
+ * we get a 8Kb file, with the last 4Kb extent as a hole
|
|
|
|
+ * (zeroes), as if an expanding truncate happened,
|
|
|
|
+ * instead of getting a file of 4Kb only.
|
|
|
|
+ */
|
|
|
|
+ err = logged_inode_size(log, inode, path,
|
|
|
|
+ &logged_isize);
|
|
|
|
+ if (err)
|
|
|
|
+ goto out_unlock;
|
|
|
|
+ }
|
|
if (test_and_clear_bit(BTRFS_INODE_NEEDS_FULL_SYNC,
|
|
if (test_and_clear_bit(BTRFS_INODE_NEEDS_FULL_SYNC,
|
|
&BTRFS_I(inode)->runtime_flags)) {
|
|
&BTRFS_I(inode)->runtime_flags)) {
|
|
clear_bit(BTRFS_INODE_COPY_EVERYTHING,
|
|
clear_bit(BTRFS_INODE_COPY_EVERYTHING,
|
|
@@ -4085,7 +4147,8 @@ again:
|
|
}
|
|
}
|
|
|
|
|
|
ret = copy_items(trans, inode, dst_path, path, &last_extent,
|
|
ret = copy_items(trans, inode, dst_path, path, &last_extent,
|
|
- ins_start_slot, ins_nr, inode_only);
|
|
|
|
|
|
+ ins_start_slot, ins_nr, inode_only,
|
|
|
|
+ logged_isize);
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
err = ret;
|
|
err = ret;
|
|
goto out_unlock;
|
|
goto out_unlock;
|
|
@@ -4109,7 +4172,7 @@ next_slot:
|
|
if (ins_nr) {
|
|
if (ins_nr) {
|
|
ret = copy_items(trans, inode, dst_path, path,
|
|
ret = copy_items(trans, inode, dst_path, path,
|
|
&last_extent, ins_start_slot,
|
|
&last_extent, ins_start_slot,
|
|
- ins_nr, inode_only);
|
|
|
|
|
|
+ ins_nr, inode_only, logged_isize);
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
err = ret;
|
|
err = ret;
|
|
goto out_unlock;
|
|
goto out_unlock;
|
|
@@ -4130,7 +4193,8 @@ next_slot:
|
|
}
|
|
}
|
|
if (ins_nr) {
|
|
if (ins_nr) {
|
|
ret = copy_items(trans, inode, dst_path, path, &last_extent,
|
|
ret = copy_items(trans, inode, dst_path, path, &last_extent,
|
|
- ins_start_slot, ins_nr, inode_only);
|
|
|
|
|
|
+ ins_start_slot, ins_nr, inode_only,
|
|
|
|
+ logged_isize);
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
err = ret;
|
|
err = ret;
|
|
goto out_unlock;
|
|
goto out_unlock;
|