aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/tree-log.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/btrfs/tree-log.c')
-rw-r--r--fs/btrfs/tree-log.c137
1 files changed, 137 insertions, 0 deletions
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 24d03c751149..517d0ccb351e 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -4415,6 +4415,127 @@ static int btrfs_log_trailing_hole(struct btrfs_trans_handle *trans,
4415 return ret; 4415 return ret;
4416} 4416}
4417 4417
4418/*
4419 * When we are logging a new inode X, check if it doesn't have a reference that
4420 * matches the reference from some other inode Y created in a past transaction
4421 * and that was renamed in the current transaction. If we don't do this, then at
4422 * log replay time we can lose inode Y (and all its files if it's a directory):
4423 *
4424 * mkdir /mnt/x
4425 * echo "hello world" > /mnt/x/foobar
4426 * sync
4427 * mv /mnt/x /mnt/y
4428 * mkdir /mnt/x # or touch /mnt/x
4429 * xfs_io -c fsync /mnt/x
4430 * <power fail>
4431 * mount fs, trigger log replay
4432 *
4433 * After the log replay procedure, we would lose the first directory and all its
4434 * files (file foobar).
4435 * For the case where inode Y is not a directory we simply end up losing it:
4436 *
4437 * echo "123" > /mnt/foo
4438 * sync
4439 * mv /mnt/foo /mnt/bar
4440 * echo "abc" > /mnt/foo
4441 * xfs_io -c fsync /mnt/foo
4442 * <power fail>
4443 *
4444 * We also need this for cases where a snapshot entry is replaced by some other
4445 * entry (file or directory) otherwise we end up with an unreplayable log due to
4446 * attempts to delete the snapshot entry (entry of type BTRFS_ROOT_ITEM_KEY) as
4447 * if it were a regular entry:
4448 *
4449 * mkdir /mnt/x
4450 * btrfs subvolume snapshot /mnt /mnt/x/snap
4451 * btrfs subvolume delete /mnt/x/snap
4452 * rmdir /mnt/x
4453 * mkdir /mnt/x
4454 * fsync /mnt/x or fsync some new file inside it
4455 * <power fail>
4456 *
4457 * The snapshot delete, rmdir of x, mkdir of a new x and the fsync all happen in
4458 * the same transaction.
4459 */
4460static int btrfs_check_ref_name_override(struct extent_buffer *eb,
4461 const int slot,
4462 const struct btrfs_key *key,
4463 struct inode *inode)
4464{
4465 int ret;
4466 struct btrfs_path *search_path;
4467 char *name = NULL;
4468 u32 name_len = 0;
4469 u32 item_size = btrfs_item_size_nr(eb, slot);
4470 u32 cur_offset = 0;
4471 unsigned long ptr = btrfs_item_ptr_offset(eb, slot);
4472
4473 search_path = btrfs_alloc_path();
4474 if (!search_path)
4475 return -ENOMEM;
4476 search_path->search_commit_root = 1;
4477 search_path->skip_locking = 1;
4478
4479 while (cur_offset < item_size) {
4480 u64 parent;
4481 u32 this_name_len;
4482 u32 this_len;
4483 unsigned long name_ptr;
4484 struct btrfs_dir_item *di;
4485
4486 if (key->type == BTRFS_INODE_REF_KEY) {
4487 struct btrfs_inode_ref *iref;
4488
4489 iref = (struct btrfs_inode_ref *)(ptr + cur_offset);
4490 parent = key->offset;
4491 this_name_len = btrfs_inode_ref_name_len(eb, iref);
4492 name_ptr = (unsigned long)(iref + 1);
4493 this_len = sizeof(*iref) + this_name_len;
4494 } else {
4495 struct btrfs_inode_extref *extref;
4496
4497 extref = (struct btrfs_inode_extref *)(ptr +
4498 cur_offset);
4499 parent = btrfs_inode_extref_parent(eb, extref);
4500 this_name_len = btrfs_inode_extref_name_len(eb, extref);
4501 name_ptr = (unsigned long)&extref->name;
4502 this_len = sizeof(*extref) + this_name_len;
4503 }
4504
4505 if (this_name_len > name_len) {
4506 char *new_name;
4507
4508 new_name = krealloc(name, this_name_len, GFP_NOFS);
4509 if (!new_name) {
4510 ret = -ENOMEM;
4511 goto out;
4512 }
4513 name_len = this_name_len;
4514 name = new_name;
4515 }
4516
4517 read_extent_buffer(eb, name, name_ptr, this_name_len);
4518 di = btrfs_lookup_dir_item(NULL, BTRFS_I(inode)->root,
4519 search_path, parent,
4520 name, this_name_len, 0);
4521 if (di && !IS_ERR(di)) {
4522 ret = 1;
4523 goto out;
4524 } else if (IS_ERR(di)) {
4525 ret = PTR_ERR(di);
4526 goto out;
4527 }
4528 btrfs_release_path(search_path);
4529
4530 cur_offset += this_len;
4531 }
4532 ret = 0;
4533out:
4534 btrfs_free_path(search_path);
4535 kfree(name);
4536 return ret;
4537}
4538
4418/* log a single inode in the tree log. 4539/* log a single inode in the tree log.
4419 * At least one parent directory for this inode must exist in the tree 4540 * At least one parent directory for this inode must exist in the tree
4420 * or be logged already. 4541 * or be logged already.
@@ -4602,6 +4723,22 @@ again:
4602 if (min_key.type == BTRFS_INODE_ITEM_KEY) 4723 if (min_key.type == BTRFS_INODE_ITEM_KEY)
4603 need_log_inode_item = false; 4724 need_log_inode_item = false;
4604 4725
4726 if ((min_key.type == BTRFS_INODE_REF_KEY ||
4727 min_key.type == BTRFS_INODE_EXTREF_KEY) &&
4728 BTRFS_I(inode)->generation == trans->transid) {
4729 ret = btrfs_check_ref_name_override(path->nodes[0],
4730 path->slots[0],
4731 &min_key, inode);
4732 if (ret < 0) {
4733 err = ret;
4734 goto out_unlock;
4735 } else if (ret > 0) {
4736 err = 1;
4737 btrfs_set_log_full_commit(root->fs_info, trans);
4738 goto out_unlock;
4739 }
4740 }
4741
4605 /* Skip xattrs, we log them later with btrfs_log_all_xattrs() */ 4742 /* Skip xattrs, we log them later with btrfs_log_all_xattrs() */
4606 if (min_key.type == BTRFS_XATTR_ITEM_KEY) { 4743 if (min_key.type == BTRFS_XATTR_ITEM_KEY) {
4607 if (ins_nr == 0) 4744 if (ins_nr == 0)