• Filipe Manana's avatar
    Btrfs: incremental send, fix invalid path for unlink commands · fdb13889
    Filipe Manana authored
    An incremental send can contain unlink operations with an invalid target
    path when we rename some directory inode A, then rename some file inode B
    to the old name of inode A and directory inode A is an ancestor of inode B
    in the parent snapshot (but not anymore in the send snapshot).
    
    Consider the following example scenario where this issue happens.
    
    Parent snapshot:
    
     .                                                      (ino 256)
     |
     |--- dir1/                                             (ino 257)
           |--- dir2/                                       (ino 258)
           |     |--- file1                                 (ino 259)
           |     |--- file3                                 (ino 261)
           |
           |--- dir3/                                       (ino 262)
                 |--- file22                                (ino 260)
                 |--- dir4/                                 (ino 263)
    
    Send snapshot:
    
     .                                                      (ino 256)
     |
     |--- dir1/                                             (ino 257)
           |--- dir2/                                       (ino 258)
           |--- dir3                                        (ino 260)
           |--- file3/                                      (ino 262)
                 |--- dir4/                                 (ino 263)
                       |--- file11                          (ino 269)
                       |--- file33                          (ino 261)
    
    When attempting to apply the corresponding incremental send stream, an
    unlink operation contains an invalid path which makes the receiver fail.
    The following is verbose output of the btrfs receive command:
    
     receiving snapshot snap2 uuid=7d5450da-a573-e043-a451-ec85f4879f0f (...)
     utimes
     utimes dir1
     utimes dir1/dir2
     link dir1/dir3/dir4/file11 -> dir1/dir2/file1
     unlink dir1/dir2/file1
     utimes dir1/dir2
     truncate dir1/dir3/dir4/file11 size=0
     utimes dir1/dir3/dir4/file11
     rename dir1/dir3 -> o262-7-0
     link dir1/dir3 -> o262-7-0/file22
     unlink dir1/dir3/file22
     ERROR: unlink dir1/dir3/file22 failed. Not a directory
    
    The following steps happen during the computation of the incremental send
    stream the lead to this issue:
    
    1) Before we start processing the new and deleted references for inode
       260, we compute the full path of the deleted reference
       ("dir1/dir3/file22") and cache it in the list of deleted references
       for our inode.
    
    2) We then start processing the new references for inode 260, for which
       there is only one new, located at "dir1/dir3". When processing this
       new reference, we check that inode 262, which was not yet processed,
       collides with the new reference and because of that we orphanize
       inode 262 so its new full path becomes "o262-7-0".
    
    3) After the orphanization of inode 262, we create the new reference for
       inode 260 by issuing a link command with a target path of "dir1/dir3"
       and a source path of "o262-7-0/file22".
    
    4) We then start processing the deleted references for inode 260, for
       which there is only one with the base name of "file22", and issue
       an unlink operation containing the target path computed at step 1,
       which is wrong because that path no longer exists and should be
       replaced with "o262-7-0/file22".
    
    So fix this issue by recomputing the full path of deleted references if
    when we processed the new references for an inode we ended up orphanizing
    any other inode that is an ancestor of our inode in the parent snapshot.
    
    A test case for fstests follows soon.
    Signed-off-by: default avatarFilipe Manana <fdmanana@suse.com>
    [ adjusted after prev patch removed fs_path::dir_path and dir_path_len ]
    Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
    fdb13889
send.c 157 KB