• Filipe Manana's avatar
    Btrfs: incremental send, do not delay rename when parent inode is new · fe9c798d
    Filipe Manana authored
    When we are checking if we need to delay the rename operation for an
    inode we not checking if a parent inode that exists in the send and
    parent snapshots is really the same inode or not, that is, we are not
    comparing the generation number of the parent inode in the send and
    parent snapshots. Not only this results in unnecessarily delaying a
    rename operation but also can later on make us generate an incorrect
    name for a new inode in the send snapshot that has the same number
    as another inode in the parent snapshot but a different generation.
    
    Here follows an example where this happens.
    
    Parent snapshot:
    
     .                                                  (ino 256, gen 3)
     |--- dir258/                                       (ino 258, gen 7)
     |       |--- dir257/                               (ino 257, gen 7)
     |
     |--- dir259/                                       (ino 259, gen 7)
    
    Send snapshot:
    
     .                                                  (ino 256, gen 3)
     |--- file258                                       (ino 258, gen 10)
     |
     |--- new_dir259/                                   (ino 259, gen 10)
              |--- dir257/                              (ino 257, gen 7)
    
    The following steps happen when computing the incremental send stream:
    
    1) When processing inode 257, its new parent is created using its orphan
       name (o257-21-0), and the rename operation for inode 257 is delayed
       because its new parent (inode 259) was not yet processed - this
       decision to delay the rename operation does not make much sense
       because the inode 259 in the send snapshot is a new inode, it's not
       the same as inode 259 in the parent snapshot.
    
    2) When processing inode 258 we end up delaying its rmdir operation,
       because inode 257 was not yet renamed (moved away from the directory
       inode 258 represents). We also create the new inode 258 using its
       orphan name "o258-10-0", then rename it to its final name of "file258"
       and then issue a truncate operation for it. However this truncate
       operation contains an incorrect name, which corresponds to the orphan
       name and not to the final name, which makes the receiver fail. This
       happens because when we attempt to compute the inode's current name
       we verify that there's another inode with the same number (258) that
       has its rmdir operation pending and because of that we generate an
       orphan name for the new inode 258 (we do this in the function
       get_cur_path()).
    
    Fix this by not delayed the rename operation of an inode if it has parents
    with the same number but different generations in both snapshots.
    
    The following steps reproduce this example scenario.
    
     $ mkfs.btrfs -f /dev/sdb
     $ mount /dev/sdb /mnt
     $ mkdir /mnt/dir257
     $ mkdir /mnt/dir258
     $ mkdir /mnt/dir259
     $ mv /mnt/dir257 /mnt/dir258/dir257
     $ btrfs subvolume snapshot -r /mnt /mnt/snap1
    
     $ mv /mnt/dir258/dir257 /mnt/dir257
     $ rmdir /mnt/dir258
     $ rmdir /mnt/dir259
    
     # Remount the filesystem so that the next created inodes will have the
     # numbers 258 and 259. This is because when a filesystem is mounted,
     # btrfs sets the subvolume's inode counter to a value corresponding to
     # the highest inode number in the subvolume plus 1. This inode counter
     # is used to assign a unique number to each new inode and it's
     # incremented by 1 after very inode creation.
     # Note: we unmount and then mount instead of doing a mount with
     # "-o remount" because otherwise the inode counter remains at value 260.
     $ umount /mnt
     $ mount /dev/sdb /mnt
     $ touch /mnt/file258
     $ mkdir /mnt/new_dir259
     $ mv /mnt/dir257 /mnt/new_dir259/dir257
     $ btrfs subvolume snapshot -r /mnt /mnt/snap2
    
     $ btrfs send /mnt/snap1 -f /tmp/1.snap
     $ btrfs send -p /mnt/snap1 /mnt/snap2 -f /tmp/2.snap
    
     $ umount /mnt
     $ mkfs.btrfs -f /dev/sdc
     $ mount /dev/sdc /mnt
     $ btrfs receive /mnt -f /tmo/1.snap
     $ btrfs receive /mnt -f /tmo/2.snap -vv
     receiving snapshot mysnap2 uuid=e059b6d1-7f55-f140-8d7c-9a3039d23c97, ctransid=10 parent_uuid=77e98cb6-8762-814f-9e05-e8ba877fc0b0, parent_ctransid=7
     utimes
     mkdir o259-10-0
     rename dir258 -> o258-7-0
     utimes
     mkfile o258-10-0
     rename o258-10-0 -> file258
     utimes
     truncate o258-10-0 size=0
     ERROR: truncate o258-10-0 failed: No such file or directory
    Reported-by: default avatarRobbie Ko <robbieko@synology.com>
    Signed-off-by: default avatarFilipe Manana <fdmanana@suse.com>
    fe9c798d
send.c 153 KB