I tried

xtricman⚓ArchVirtual⏺️~🤐ls /proc/self/fd/ -l
Total 0
lrwx------ 1 xtricman users 64 1月  16 16:34 0 -> /dev/pts/0
lrwx------ 1 xtricman users 64 1月  16 16:34 1 -> /dev/pts/0
lrwx------ 1 xtricman users 64 1月  16 16:34 2 -> /dev/pts/0
lrwx------ 1 xtricman users 64 1月  16 16:34 3 -> '/home/xtricman/a (deleted)'
lr-x------ 1 xtricman users 64 1月  16 16:34 4 -> /proc/1273/fd
xtricman⚓ArchVirtual⏺️~🤐ln /proc/self/fd/3 b
ln: failed to create hard link 'b' => '/proc/self/fd/3': Invalid cross-device link

Since the inode is still on the disk, how can I re-create a name for it? What if there's no open file description pointing to that inode but that inode is mmaped? How can I restore it in that case?


You're not supposed to be able to do that (but read below for an interesting exception).

If the kernel was to let it happen, then a call like:

fd = open(filename, O_CREAT|O_RDWR, 0666);

linkat(fd, "", 0, "/new/path", AT_EMPTY_PATH);

would succeed even when the inode referenced by fd has a link count of 0, when done by a process with CAP_DAC_READ_SEARCH caps.

But the kernel is actively preventing it from happening, without regard to the capabilities or privileges of the process doing it.

int vfs_link(struct dentry *old_dentry, ...
        /* Make sure we don't allow creating hardlink to an unlinked file */
        if (inode->i_nlink == 0 && !(inode->i_state & I_LINKABLE))
                error =  -ENOENT;

This is also documented in the manpage:

AT_EMPTY_PATH (since Linux 2.6.39)

If oldpath is an empty string, create a link to the file referenced by olddirfd (which may have been obtained using the open(2) O_PATH flag). In this case, olddirfd can refer to any type of file except a directory. This will generally not work if the file has a link count of zero (files created with O_TMPFILE and without O_EXCL are an exception). The caller must have the CAP_DAC_READ_SEARCH capability in order to use this flag. This flag is Linux-specific; define _GNU_SOURCE to obtain its definition.

based on the kernel source, there seems to be no other exception besides O_TMPFILE. O_TMPFILE is documented in the open(2) manpage; below is a small example based on that:

#define _GNU_SOURCE 1
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <err.h>

int main(int ac, char **av){
        char path[64]; int fd;
        if(ac < 3) errx(1, "usage: %s dir newpath", av[0]);

        if((fd = open(av[1], O_TMPFILE|O_RDWR, 0666)) == -1) err(1, "open");

         * ...
         * write stuff to fd and only "realize" the file at the end if
         * everything has succeeded

        /* the following line only works with CAP_DAC_READ_SEARCH */
        /* if(linkat(fd, "", 0, av[2], AT_EMPTY_PATH)) err(1, "linkat"); */

        snprintf(path, sizeof path, "/proc/self/fd/%d", fd);
        if(linkat(AT_FDCWD, path, AT_FDCWD, av[2], AT_SYMLINK_FOLLOW))
                err(1, "linkat");
        return 0;

You could simply cat that file descriptor:

$ echo foo > bar
$ sleep 10m < bar & rm bar 
[1] 15743
$ ls -l /proc/15743/fd 
total 0
lr-x------ 1 olorin olorin 64 Jan 16 17:49 0 -> /tmp/bar (deleted)
lrwx------ 1 olorin olorin 64 Jan 16 17:49 1 -> /dev/pts/6
lrwx------ 1 olorin olorin 64 Jan 16 17:49 2 -> /dev/pts/6
$ cat /proc/15743/fd/0
$ cat /proc/15743/fd/0 > bar
$ cat bar

You can't ln that file to make a hard link because hard links can't span filesystems, and /proc is a virtual filesystem (procfs), and even within /proc, what you can do is restricted (the contents reflect the state of the kernel, so you can't perform arbitrary operations).

  • But I want to restore the inode, not just duplicate the data. – 炸鱼薯条德里克 Jan 16 at 10:33
  • 1
    That's not going to be possible without hackery. Even tools which "undelete" files don't restore to the same inode. – Olorin Jan 17 at 1:41

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.