summaryrefslogtreecommitdiffstats
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.c30
1 files changed, 27 insertions, 3 deletions
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index a5e08a73653e..0dba09334a16 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -5580,9 +5580,33 @@ static int btrfs_log_all_parents(struct btrfs_trans_handle *trans,
5580 5580
5581 dir_inode = btrfs_iget(fs_info->sb, &inode_key, 5581 dir_inode = btrfs_iget(fs_info->sb, &inode_key,
5582 root, NULL); 5582 root, NULL);
5583 /* If parent inode was deleted, skip it. */ 5583 /*
5584 if (IS_ERR(dir_inode)) 5584 * If the parent inode was deleted, return an error to
5585 continue; 5585 * fallback to a transaction commit. This is to prevent
5586 * getting an inode that was moved from one parent A to
5587 * a parent B, got its former parent A deleted and then
5588 * it got fsync'ed, from existing at both parents after
5589 * a log replay (and the old parent still existing).
5590 * Example:
5591 *
5592 * mkdir /mnt/A
5593 * mkdir /mnt/B
5594 * touch /mnt/B/bar
5595 * sync
5596 * mv /mnt/B/bar /mnt/A/bar
5597 * mv -T /mnt/A /mnt/B
5598 * fsync /mnt/B/bar
5599 * <power fail>
5600 *
5601 * If we ignore the old parent B which got deleted,
5602 * after a log replay we would have file bar linked
5603 * at both parents and the old parent B would still
5604 * exist.
5605 */
5606 if (IS_ERR(dir_inode)) {
5607 ret = PTR_ERR(dir_inode);
5608 goto out;
5609 }
5586 5610
5587 if (ctx) 5611 if (ctx)
5588 ctx->log_new_dentries = false; 5612 ctx->log_new_dentries = false;