11

A variant of the question Getting Filename from file descriptor in C. This is about Linux.

If I have a file descriptor that refers to a regular file, can I "save" the file descriptor by giving it a new file name (somewhere on the same device as where it lives, of course)? I'm looking for something similar to rename(2) or link(2) but that would accept a file descriptor as input instead of a file name.

The problem with rename(2) and link(2) is that even though you can attempt to go from the file descritor to the file name, this might fail. I'm thinking more precisely about the case where the opened file descriptor refers to a file that was already unlinked --- in this case, the file has no more name. It seems that there is no way to prevent the file from being deleted when we close() the file descriptor. But am I wrong? Can we, with the Posix or even Linux API, give it a name again?

Update: we can actually see the content of a deleted file on Linux in /proc/<pid>/fd/<fd>, even though it looks like a broken symbolic link. We can't use link(2) or ln(1) to rematerialize such a file, though, because it thinks we're trying to do a cross-device link.

Community
  • 1
  • 1
Armin Rigo
  • 12,048
  • 37
  • 48

2 Answers2

6

Question: a hypothetical system call frename that takes a file descriptor existed, and if a file has multiple names (hard links), which of those names would get moved/renamed when this system call was used on a file descriptor that refers to this file?

There is no good answer to that question, and that's one of the reasons this system call does not exist.

rename deals with directory entries, which are pointers to files (inodes). An open file descriptor is not associated with any particular directory entry, only with the file itself (the inode). From that perspective, the system call you are asking for doesn't really make sense. There is no portable way to follow an inode back to the directory entry that points to it (and, again, there might be more than one of them). Some operating systems may provide various non-portable means for finding this backward link which may or may not be guaranteed to always come up with a result (usually not guaranteed), but these means don't answer the question of which directory entry to return when there is more than one, and to my knowledge none of them have been extended into a system call like what you're looking for.

Celada
  • 21,627
  • 4
  • 64
  • 78
  • Sorry for the confusion. This was clear to me, which is why I also suggested `flink()`. I should not have mentioned `frename()`. – Armin Rigo Dec 14 '12 at 20:28
  • 2
    `flink()` would indeed be nice to have. It's the only thing that would make it possible to reconnect a file that has 0 links but still exists. But yeah... it doesn't exist under any OS I'm aware of. – Celada Dec 14 '12 at 23:28
6

If the question is about linux, and about linux > 2.6.39, you can use the linkat command with the AT_EMPTY_PATH flag to give a name to a file descriptor. See the man page (http://man7.org/linux/man-pages/man2/link.2.html)

linkat(fd,"",destdirfd,"filename",AT_EMPTY_PATH);

Caveats:

  • You need to define _GNU_SOURCE to get the definition for AT_EMPTY_PATH,
  • For files with link count of zero this is not guaranteed to work. I'm not sure I understand what the manual page says about this. My guess is that when a file has zero link count the inode is already deleted on the filesystem, to avoid inconsistencies if the filesystem were to crash.
  • Of course I don't expect it to work if the old file is not on the same filesystem at the target directory.

If this were to fail, you have no other chance than to create a new file and copy the content over it using sendfile (error checking omitted, see man pages of each function for possible error values):

struct stat s;
off_t offset = 0;
int targetfd = open("target/filename", O_WRONLY | O_CREAT | O_EXCL);
fstat(fd,&s);
sendfile(targetfd,fd,&offset, s.st_size);
pqnet
  • 6,070
  • 1
  • 30
  • 51
  • Thanks! It's still not completely general, but it's a step in the "right" direction -- not that I'm implying that what I'm asking for would really be right or even needed. (My original question was a matter of curiosity only.) – Armin Rigo Aug 06 '14 at 17:45
  • I think linux would really help a "give a name to this file descriptor" generic api, which should fail only if the file descriptor does not refers to a normal file or refers to a file in another partition. Also, there should be the way to replace an existing file atomically when doing so, which is now possible by using `rename` but only if the source file has a filesystem name. – pqnet Aug 06 '14 at 18:07