Why do symbolic links in /proc/$PID/fd/ act as hard links?

6

When the target of a symbolic link is deleted, it points to nothing and there's no way to get to the content of the deleted target.

Files in /proc/$PID/fd/ are displayed as symbolic links, but they allow you to get to the deleted target's content, as explained here: Recover Deleted Linux Files With lsof.

How does that work? Why is it displayed as a symbolic link if it doesn't act as one? Is it the symbolic link implementation of the proc filesystem that keeps a reference to the inode of the file?

user300811

Posted 2016-08-12T11:30:48.533

Reputation: 81

1You misunderstand: deletion will be complete only after all processes using the file at the time of deletion have reached completion: only then the deleted inode will be returned to the pool of available inodes, and the content of the file may begin to be corrupted by over-writing. Until then, the inode is alive and well, and is pointing to the area of the disk containing the file in question. As soon as less completes, the soft link will disappear, and so will the file testing.txt. – MariusMatutiae – 2016-08-12T16:32:59.927

Try to replicate this on an ext4 filesystem, the smbolic link becomes invalid as soon as you delete the file, even if the program having the file open is still running. This is a completely different behaviour than for the symlink in /proc/$PID/fd/ – user300811 – 2016-08-17T07:52:17.560

Answers

3

If its target is deleted, an entity in /proc/$PID/fd/ appears as a broken symbolic link when you use ls(1) or file(1), but it indeed acts differently when being opened with open(2).

On my Debian 9 I used strace(1) to see what happens when I try to read a symlink. The command is sudo strace cat "$symlink". The relevant line from stderr is

  • either OK

    open("$symlink", O_RDONLY)                   = 3
    
  • or ENOENT

    open("$symlink", O_RDONLY)                   = -1 ENOENT (No such file or directory)
    

(note: I'm not saying these are all possible outcomes of open(2) in general).

The results:

               | regular symlink | /proc/$PID/fd/$N |
---------------+-----------------+------------------+
exists, valid  |       OK        |        OK        |
exists, broken |     ENOENT      |        OK        | <- the difference
doesn't exist  |     ENOENT      |      ENOENT      |
---------------+-----------------+------------------+

I also learnt that when I run file "$symlink", it calls lstat(2), readlink(2) and stat(2). These are system calls that base on paths, not file descriptors. If the symlink exists (valid or broken), open(2) is never called to open it or its target. ENOENT from stat(2) indicates the link is broken.

My conclusion is: "broken link" is a property derived from the output of some system call(s); but when you open a link from /proc/$PID/fd/, open(2) just knows what to do with it and doesn't care what other tool(s) would yield.

Note the entire /proc only fakes a "normal" filesystem. Few quirks:

  • Files may have dynamic content, yet they are not being modified with system calls (try inotifywait).
  • Objects may (dis)appear, yet they are not being created nor deleted with system calls (again inotifywait).
  • In some sense objects may not exist until you interact with them. Run bash and wait few minutes. Invoke ls -l /proc/$$/fd to see its file descriptors. Probably ctimes will show "this very moment". Yet if you repeat the command every few seconds, you will notice the ctimes never change. (Trivia: at first I though I could answer this question Determine how long a file has been open with stat and symlinks in /proc/$PID/fd/ but I was wrong; now you know why).

No wonder these symlinks you ask about don't behave like regular symlinks in some circumstances. The entire /proc was designed to behave somewhat differently. I suppose open(2) was deliberately given the ability to take advantage of it.

Kamil Maciorowski

Posted 2016-08-12T11:30:48.533

Reputation: 38 429