why is is_regular_file reporting true for symbolic link
Asked Answered
A

2

5

Why is it that is_regular_file function that is part of std::filesystem reports true for a symbolic link? I have looked at the documentation for the call and can't find the reason and the examples on cppreference exhibit the same behaviour.

As an example, if we look at the binary pidof; It exists as /usr/sbin/pidof and correctly labelled as a symbolic link as shown here by stat:

[@rhel9 ~]$ stat /usr/sbin/pidof 
  File: /usr/sbin/pidof -> /usr/bin/pidof
  Size: 14          Blocks: 0          IO Block: 4096   symbolic link
Device: fd00h/64768d    Inode: 34952482    Links: 1
Access: (0777/lrwxrwxrwx)  Uid: (    0/    root)   Gid: (    0/    root)
Context: system_u:object_r:bin_t:s0
Access: 2024-02-05 15:46:24.124158991 +0000
Modify: 2023-01-28 09:40:21.000000000 +0000
Change: 2023-05-09 17:34:59.432002380 +0100
 Birth: 2023-05-09 17:34:59.432002380 +0100

If we follow the link and run stat on it:

[@rhel9 ~]$ stat /usr/bin/pidof 
  File: /usr/bin/pidof
  Size: 23760       Blocks: 48         IO Block: 4096   regular file
Device: fd00h/64768d    Inode: 18153014    Links: 1
Access: (0755/-rwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Context: system_u:object_r:bin_t:s0
Access: 2024-02-02 15:12:41.804334284 +0000
Modify: 2023-01-28 09:40:21.000000000 +0000
Change: 2023-05-09 17:34:59.418002380 +0100
 Birth: 2023-05-09 17:34:59.417002380 +0100

If we run the following code:

const std::filesystem::path fsBin("/usr/sbin/pidof");
if (true == std::filesystem::is_regular_file(fsBin))
{
    std::cout << "Regular File" << std::endl;
}
else if(true == std::filesystem::is_symlink(fsBin))
{
    std::cout << "Symbolic Link" << std::endl;
}

The code always prints "Regular File" despite the path being a symbolic link. If I switch the cases around (have is_symlink as the first check), it reports correctly that it is a symlink. What is interesting too is that if I use a std::filesystem::status objects instead of the std::filesystem::path, it never reports a symlink regardless of the order.

Is there an explanation for this behaviour that I'm maybe overlooking?

Archangel answered 5/2 at 22:46 Comment(2)
By "regular file" probably they mean that symlink supports all the API functions that regular file does, so normally your app should not care is it symlink or actually a file. And that is good, from my point of view. You create a symlink and you are sure that your app will work exactly the same as if that was a file.Disintegrate
C++ fs API is analogous to POSIX API, with filesystem::status() corresponding to POSIX stat() and filesystem::symlink_status() corresponding to POSIX lstat(). is_regular_file() uses the result of status(), and is_symlink() uses the result of symlink_status(), so that the results are as presented by OP.Clever
A
8

As documented on cppreference, the function

bool std::filesystem::is_regular_file(const std::filesystem::path& p);

is equivalent to

std::filesystem::is_regular_file(std::filesystem::status(p))

where the std::filesystem::status function

Determines the type and attributes of the filesystem object identified by p as if by POSIX stat (symlinks are followed to their targets).

Whereas the std::filesystem::symlink_status function

is the same as the std::filesystem::status function, except that the behavior is as if the POSIX lstat is used (symlinks are not followed).

Thus, the correct usage in your case would be:

if (std::filesystem::is_regular_file(std::filesystem::symlink_status(fsBin)))
{
    // ...
}
Auspicate answered 6/2 at 8:23 Comment(1)
interesting and an important point I missed when going over the documentation. Changing the code to match your example fixed the issue.Archangel
A
4

is_regular_file(path) answers: is this a regular file after following symlinks? cppreference

is_symlink(path) answers: is this a symlink? cppreference

Note the subtle distinction that is_regular_file uses status which follows symlinks and is_symlink uses symlink_status which does not.

Abuttals answered 6/2 at 3:23 Comment(3)
Where does it say that on the cppreference page?Traps
Ah, I see that now when reading the documentation after reading the comments. is_regular_file is using status whereas is_symlink is using symlink_status and won't follow the link through.Archangel
@Traps fair enough that it's subtle, added a bit more text.Abuttals

© 2022 - 2024 — McMap. All rights reserved.