Skip to content
  • Filipe Manana's avatar
    Btrfs: fix for incorrect directory entries after fsync log replay · 657ed1aa
    Filipe Manana authored
    
    
    If we move a directory to a new parent and later log that parent and don't
    explicitly log the old parent, when we replay the log we can end up with
    entries for the moved directory in both the old and new parent directories.
    Besides being ilegal to have directories with multiple hard links in linux,
    it also resulted in the leaving the inode item with a link count of 1.
    A similar issue also happens if we move a regular file - after the log tree
    is replayed the file has a link in both the old and new parent directories,
    when it should be only at the new directory.
    
    Sample reproducer:
    
      $ mkfs.btrfs -f /dev/sdc
      $ mount /dev/sdc /mnt
      $ mkdir /mnt/x
      $ mkdir /mnt/y
      $ touch /mnt/x/foo
      $ mkdir /mnt/y/z
      $ sync
      $ ln /mnt/x/foo /mnt/x/bar
      $ mv /mnt/y/z /mnt/x/z
      < power fail >
      $ mount /dev/sdc /mnt
      $ ls -1Ri /mnt
      /mnt:
      257 x
      258 y
    
      /mnt/x:
      259 bar
      259 foo
      260 z
    
      /mnt/x/z:
    
      /mnt/y:
      260 z
    
      /mnt/y/z:
    
      $ umount /dev/sdc
      $ btrfs check /dev/sdc
      Checking filesystem on /dev/sdc
      UUID: a67e2c4a-a4b4-4fdc-b015-9d9af1e344be
      checking extents
      checking free space cache
      checking fs roots
      root 5 inode 260 errors 2000, link count wrong
            unresolved ref dir 257 index 4 namelen 1 name z filetype 2 errors 0
            unresolved ref dir 258 index 2 namelen 1 name z filetype 2 errors 0
      (...)
    
    Attempting to remove the directory becomes impossible:
    
      $ mount /dev/sdc /mnt
      $ rmdir /mnt/y/z
      $ ls -lh /mnt/y
      ls: cannot access /mnt/y/z: No such file or directory
      total 0
      d????????? ? ? ? ?            ? z
      $ rmdir /mnt/x/z
      rmdir: failed to remove ‘/mnt/x/z’: Stale file handle
      $ ls -lh /mnt/x
      ls: cannot access /mnt/x/z: Stale file handle
      total 0
      -rw-r--r-- 2 root root 0 Apr  6 18:06 bar
      -rw-r--r-- 2 root root 0 Apr  6 18:06 foo
      d????????? ? ?    ?    ?            ? z
    
    So make sure that on rename we set the last_unlink_trans value for our
    inode, even if it's a directory, to the value of the current transaction's
    ID and that if the new parent directory is logged that we fallback to a
    transaction commit.
    
    A test case for fstests is being submitted as well.
    
    Signed-off-by: default avatarFilipe Manana <fdmanana@suse.com>
    657ed1aa