7

I know that cp has a --reflink option to control full copies vs. copy-on-write "copies".

On btrfs, can I use ls (or some other command) to find out whether a file shares (in a copy-on-write senses) some storage with another file?

EDIT: @StéphaneChazelas points me to filefrag, but that fails for me:

root@void:/tmp/mount# mount | tail -1
/tmp/back on /tmp/mount type btrfs (rw,relatime,space_cache)
root@void:/tmp/mount# df -h | tail -1
/dev/loop0       32M   13M   20M  38% /tmp/mount
root@void:/tmp/mount# ls -lh
total 8.0M
-rw-r--r-- 1 root root 8.0M Jan 19 08:43 one
root@void:/tmp/mount# cp --reflink=always one two
root@void:/tmp/mount# sync
root@void:/tmp/mount# ls -lh
total 16M
-rw-r--r-- 1 root root 8.0M Jan 19 08:43 one
-rw-r--r-- 1 root root 8.0M Jan 19 08:45 two
root@void:/tmp/mount# df -h | tail -1
/dev/loop0       32M   13M   20M  38% /tmp/mount
root@void:/tmp/mount# filefrag -kvx one 
Filesystem type is: 9123683e
File size of one is 8388608 (8192 blocks of 1024 bytes)
FIEMAP failed with unknown flags 2
one: FIBMAP unsupported
root@void:/tmp/mount# uname -a
Linux void 4.1.7+ #817 PREEMPT Sat Sep 19 15:25:36 BST 2015 armv6l GNU/Linux
  • 2
    With filefrag -v, you can check whether two files have data in common. – Stéphane Chazelas Jan 18 '16 at 15:26
  • 1
    @StéphaneChazelas: Fails for me, see my edit. – not-a-user Jan 19 '16 at 7:50
5

I don't know how to find it via the ls command. But if you really want it, you can use the btrfs-progs/btrfs-debug-tree.

With reflink=always, the files will share a common data block. This common data block (aka extents) has refs more than 1.

  1. First you need to find the objectid for the files one and two

    #./btrfs-debug-tree  /dev/xvdc
    (Check under FS_TREE)
      <snip>
        item 8 key (256 DIR_INDEX 4) itemoff 15842 itemsize 33
            location key (259 INODE_ITEM 0) type FILE
            namelen 3 datalen 0 name: one
        item 9 key (256 DIR_INDEX 5) itemoff 15809 itemsize 33
            location key (260 INODE_ITEM 0) type FILE
            namelen 3 datalen 0 name: two
      </snip>
    

From above we can see its 259(one) and 260(two).

  1. Now find its refs. from extent tree. Below command will find the data block shared between two files.

    # ./btrfs-debug-tree  /dev/xvdc | grep -A2 "refs 2"
            extent refs 2 gen 9 flags DATA
            extent data backref root 5 objectid 260 offset 0 count 1
            extent data backref root 5 objectid 259 offset 0 count 1
    

Bonus: Create another reference:

# cp --reflink=always one three

verify the refcount is incremented by 1.

# ./btrfs-debug-tree   /dev/xvdc | grep -A3 "refs 3"
        extent refs 3 gen 9 flags DATA
        extent data backref root 5 objectid 260 offset 0 count 1
        extent data backref root 5 objectid 261 offset 0 count 1
        extent data backref root 5 objectid 259 offset 0 count 1

Here the data block is shared between three files which are pointed to by objectid 259,260,261.

  • Thanks, good to know about btrfs-debug-tree. – not-a-user Jan 27 '16 at 9:21
1

I have just released a program called fienode (← link) which computes a SHA1 hash of the physical extents of a file. Identical CoW copies have the same hash.

There is also a more detailed answer here, explaining why this is necessary.

Note however, that BTRFS is at liberty to change the physical extents. I've observed a large reflinked file changes its physical extents without provocation, making the fienode output different, even though the majority of the physical extents were still shared.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.