Create a hard link from a file handle on Unix?

前端 未结 3 1799
再見小時候
再見小時候 2021-02-07 03:39

If I\'ve got a handle to an open file, is it possible to create a hard link to that file after all references to it have been removed from the filesystem?

For example, s

相关标签:
3条回答
  • 2021-02-07 03:49

    The newly released linux 3.11 offers a solution to this problem with the new O_TMPFILE open(2) flag. With this flag you can create an "invisible" file (i.e. an inode with no hardlinks) in some file system (specified by a directory in that file system). Then, after the file is fully set up, you can create a hardlink using linkat. It works like this:

    fd = open("/tmp", O_TMPFILE | O_RDWR, 0600);
    // write something to the file here
    // fchown()/fchmod() it
    linkat(fd, "", AT_FDCWD, "/tmp/test", AT_EMPTY_PATH);
    

    Note that aside from the >=3.11 kernel requirement, this also requires support from the underlying file system (I tried the above snippet on ext3 and it worked, but it did not seem to work on btrfs).

    0 讨论(0)
  • 2021-02-07 03:52

    Not generally, no. [Edit: since Linux 3.11 there is now linkat; see safsaf32's answer. This does not work on POSIX systems in general since POSIX linkat is restricted to directories only.] There are security considerations here: someone can pass to you an open file descriptor that you could not normally open on your own, e.g.:

    mkdir lock; chmod 700 lock
    echo secret contents > lock/in
    sudoish cmd < lock/in
    

    Here cmd runs as a user who has no permission to open the input file (lock/in) by name, but can still read from it. If cmd could create a new name on the same file system, it could pass the file contents on to a later process. (Obviously it can copy those contents, so this issue is more of a "pass the contents on by mistake" thing than "pass the contents on, on purpose".)

    That said, people have come up with ways of "relinking" files by inode/vnode internally (it's pretty easy to do inside most file systems), so you could make your own private system call for it. The descriptor must refer to a real file on the appropriate mount point, of course—there's no way of "relinking" a pipe or socket or device into becoming a regular file.

    Otherwise you're stuck with "catch signals and clean up and hope for the best", or a similar trick, "fork off a subprocess, run it, and if it succeeds/fails, take appropriate move/clean-up action".


    Edit to add historical note: the above lock example is not particularly good, but back in the days of V6 Unix, MDQS used a fancier version of this trick. Bits and pieces of MDQS survive in various forms today.

    0 讨论(0)
  • 2021-02-07 04:02

    On Linux, you might try the unportable trick of using /proc/self/fd by trying to call

     char pbuf[64];
     snprintf (pbuf, sizeof(pbuf), "/proc/self/fd/%d", fd);
     link(pbuf, "/tmp/hello");
    

    I would be surprised if that trick worked after an unlink("/tmp/foo") ... I did not try that.

    A more portable (but less robust) way would be to generate a "unique temporary path" perhaps like

     int p = (int) getpid();
     int t = (int) time(0);
     int r = (int) random();
     sprintf(pbuf, sizeof(pbuf), "/tmp/out-p%d-r%d-t%d.tmp", p, r, t);
     int fd = open  (pbuf, O_CREAT|O_WRONLY);
    

    Once the file has been written and closed, you rename(2) it to some more sensible path. You could use atexit in your program to do the renaming (or the removing).

    And have some cron job to clean the [old] /tmp/out*.tmp every hour...

    0 讨论(0)
提交回复
热议问题