Jesch
Jesch

Reputation: 45

Can hardlinks be overwritten without using a temporary file?

I have a hardlink the must always exist on the filesystem. What inode the hardlink points is not constant. I want to update the hardlink without adding a temporary entry to the directory.

(Creating a file without a directory entry can be done using open(2) with the temp flag.)

The issue I'm facing is with replacing/updating the hardlink. From the documentation on the relevant system calls, It seems that I have only two options, and neither avoids a temporary file:

  1. Using renameat, it is possible to insure that the hardlink always exists. However, it must consume a hardlink and hence necessitating a temporary files (not to mention its inability to dereference symbolic links).

  2. using linkat, it is possible to produce a hardlink without sacrificing another file. but it cannot overwrite existing files; requiring the deletion of the original hard link.

Is it at all possible to create a link to an inode that replaces an older link with the same name?

Upvotes: 3

Views: 1996

Answers (1)

You need to have another file to which to switch the link. However rename, renameat do not need the inode be linked in the same directory; they just require the inode to exist on the same filesystem, or more specifically on the same mount point; otherwise Linux rename fails with EXDEV:

EXDEV
oldpath and newpath are not on the same mounted filesystem. (Linux permits a filesystem to be mounted at multiple points, but rename() does not work across different mount points, even if the same filesystem is mounted on both.)


Since Linux 3.11 there is a way to make a new file without linking it to the filesystem: open(2) has a new flag O_TMPFILE:

O_TMPFILE (since Linux 3.11)

Create an unnamed temporary file. The pathname argument specifies a directory; an unnamed inode will be created in that directory's filesystem. Anything written to the resulting file will be lost when the last file descriptor is closed, unless the file is given a name.

O_TMPFILE must be specified with one of O_RDWR or O_WRONLY and, optionally, O_EXCL. If O_EXCL is not specified, then linkat(2) can be used to link the temporary file into the filesystem, making it permanent, using code like the following:

      char path[PATH_MAX];
      fd = open("/path/to/dir", O_TMPFILE | O_RDWR,
                              S_IRUSR | S_IWUSR);
      /* File I/O on 'fd'... */
      snprintf(path, PATH_MAX,  "/proc/self/fd/%d", fd);
      linkat(AT_FDCWD, path, AT_FDCWD, "/path/for/file",
                              AT_SYMLINK_FOLLOW);

In this case, the open() mode argument determines the file permission mode, as with O_CREAT.

The manual tells that one of the 2 common use cases for O_TMPFILE is

Creating a file that is initially invisible, which is then populated with data and adjusted to have appropriate filesystem attributes (chown(2), chmod(2), fsetxattr(2), etc.) before being atomically linked into the filesystem in a fully formed state (using linkat(2) as described above).

There are many downsides for this, apart from it being quite new: the file system must also support O_TMPFILE; ext[234] do support it, and so does XFS in 3.15; btrfs in 3.16; furthermore it might still not be a match for your case, as the linkat requires the AT_SYMLINK_FOLLOW which is not available for renameat; if the target name already exists, `linkat does not replace the the target.

Upvotes: 3

Related Questions