`cat` cmd on symlinked file in btrfs

Hi.

I’ve discovered that in btrfs, reading a symlink file (cat file.txt) wouldn’t print the contents of actual file but returns an error. It should ideally read the actual file.

The symlink file contains the filename or whatever path that’s passed in the ln command.

So if we do:

  1. ln -s file_1.txt file_2.txt, file_2.txt contains “file_1.txt”
  2. ln -s /home/file_1.txt /home/file_2.txt, file_2.txt contains “/home/file_1.txt”

Assuming the mount point has not changed, the file system driver should read contents of the file_2.txt, create inode object of file_1.txt and read contents of that. If mount point has changed, it’s a broken link and error message is valid.

Tested bfs on Haiku VM and this is working for that file system. But I couldn’t find the above ‘follow the symlink and run read on that’ logic part.

Could someone help in pointing out how it’s working for bfs?

Steps:

touch file_1.txt
echo "content" >> file_1.txt
ln -s file_1.txt file_2.txt
cat file_2.txt
1 Like

From the filesystem point of view there is nothing really special about symlinks. It is indeed just a file whose contents is a path to another file.

All you need to do is make sure that you set the correct type (and size) in the stat function:

https://cgit.haiku-os.org/haiku/tree/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp#n83

Then the VFS (the layer that manage filesystems) will know that this is a link, and handle it accordingly. It will do the resolution and then re-ask the filesystem with the resolved path name.

To find the place where this is done, I will give you details of how I do it, so next time you can find your way by yourself. Let’s start from the open() function in the C library (fcntl.cpp « posix « libroot « system « src - haiku - Haiku's main repository)

We can see that it is calling _kern_open. If you read the Haiku documentation about system calls you know that this is a call into the kernel, and that the matching function on the kernel side will be _user_open. You can find it using “git grep” or similar tools, but since I am not in front of my Haiku machine I used xref.landonf.org which has an index of Haiku sourcecode. Searching for _user_open leads me here: Cross Reference: /haiku/src/system/kernel/fs/vfs.cpp

This does some work related to memory protection and syscall handling, which we don’t need to care about for now, and eventually it calls file_open with the path that theu ser is trying to open: Cross Reference: /haiku/src/system/kernel/fs/vfs.cpp

Here we can see an important thing, the “traverse” value is checking the open options O_NOTRAVERSE and O_NOFOLLOW so it will be useful to identify the special cases for symlinks handling.

This allows us to see that the “traverse” operation (resolving symlinks) is handled by fd_and_path_to_vnode. This function gets an fd for the current directory, a path relative to that fd, and it finds the corresponding vnode (virtual filesystem node) which in turn can eventually be mapped to the filesystem inode.

Inside this function it works as you described: if the path is absolute, it is resolved by path_to_vnode, otherwise directly by vnode_path_to_vnode. In the end, path_to_vnode just finds the root and resolves the path relatively to that, so let’s look at vnode_path_to_vnode directly.

Finally we can see the code that actually uses the “traverse” variable that we set earlier, after passing it down all these functions. It is here: Cross Reference: /haiku/src/system/kernel/fs/vfs.cpp

It relies on:

5 Likes