diff options
Diffstat (limited to 'fs/btrfs/tree-log.c')
-rw-r--r-- | fs/btrfs/tree-log.c | 137 |
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 | */ | ||
4460 | static 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; | ||
4533 | out: | ||
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) |