How to restore a deleted file while it’s still being opened?

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?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

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);
unlink(filename);

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;
}

Method 2

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
foo
$ cat /proc/15743/fd/0 > bar
$ cat bar
foo

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).


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x