• Filipe Manana's avatar
    Btrfs: incremental send, fix invalid path for link commands · f5962781
    Filipe Manana authored
    In some scenarios an incremental send stream can contain link commands
    with an invalid target path. Such scenarios happen after moving some
    directory inode A, renaming a regular file inode B into the old name of
    inode A and finally creating a new hard link for inode B at directory
    inode A.
    
    Consider the following example scenario where this issue happens.
    
    Parent snapshot:
    
      .                                                      (ino 256)
      |
      |--- dir1/                                             (ino 257)
      |      |--- dir2/                                      (ino 258)
      |             |--- dir3/                               (ino 259)
      |                   |--- file1                         (ino 261)
      |                   |--- dir4/                         (ino 262)
      |
      |--- dir5/                                             (ino 260)
    
    Send snapshot:
    
      .                                                      (ino 256)
      |
      |--- dir1/                                             (ino 257)
             |--- dir2/                                      (ino 258)
             |      |--- dir3/                               (ino 259)
             |            |--- dir4                          (ino 261)
             |
             |--- dir6/                                      (ino 263)
                    |--- dir44/                              (ino 262)
                           |--- file11                       (ino 261)
                           |--- dir55/                       (ino 260)
    
    When attempting to apply the corresponding incremental send stream, a
    link command contains an invalid target path which makes the receiver
    fail. The following is the verbose output of the btrfs receive command:
    
      receiving snapshot mysnap2 uuid=90076fe6-5ba6-e64a-9321-9279670ed16b (...)
      utimes
      utimes dir1
      utimes dir1/dir2/dir3
      utimes
      rename dir1/dir2/dir3/dir4 -> o262-7-0
      link dir1/dir2/dir3/dir4 -> dir1/dir2/dir3/file1
      link dir1/dir2/dir3/dir4/file11 -> dir1/dir2/dir3/file1
      ERROR: link dir1/dir2/dir3/dir4/file11 -> dir1/dir2/dir3/file1 failed: Not a directory
    
    The following steps happen during the computation of the incremental send
    stream the lead to this issue:
    
    1) When processing inode 261, we orphanize inode 262 due to a name/location
       collision with one of the new hard links for inode 261 (created in the
       second step below).
    
    2) We create one of the 2 new hard links for inode 261, the one whose
       location is at "dir1/dir2/dir3/dir4".
    
    3) We then attempt to create the other new hard link for inode 261, which
       has inode 262 as its parent directory. Because the path for this new
       hard link was computed before we started processing the new references
       (hard links), it reflects the old name/location of inode 262, that is,
       it does not account for the orphanization step that happened when
       we started processing the new references for inode 261, whence it is
       no longer valid, causing the receiver to fail.
    
    So fix this issue by recomputing the full path of new references if we
    ended up orphanizing other inodes which are directories.
    
    A test case for fstests follows soon.
    Signed-off-by: default avatarFilipe Manana <fdmanana@suse.com>
    f5962781
send.c 157 KB