Re: Symlink not persisted even after fsync

Theodore Y. Ts'o
2018-04-15T01:30:29Z

On Sat, Apr 14, 2018 at 08:13:28PM -0500, Vijay Chidambaram wrote:
>
> We are *not* saying an fsync on a symlink file has to result in any
> action on the original file. We understand the lack of ordering
> constraints here.

The problem is you're not being precise here. The fsync(2) system
call operates on a file descriptor, not a file. This is an important
distinction.

Suppose you have a symlink called "foo" which points to "/tmp/bar/quux"

fd = open("foo", O_CREAT|O_RDWR);

It will _fail_ if /tmp/bar does not exist:

% ls -l foo
0 lrwxrwxrwx 1 tytso tytso 13 Apr 14 21:21 foo -> /tmp/bar/quux
% echo test > foo
bash: foo: No such file or directory
% mkdir /tmp/bar
% echo test > foo
% ls -l /tmp/bar
total 4
4 -rw-r--r-- 1 tytso tytso 5 Apr 14 21:22 quux

When you open "foo", the restulting file descriptor is not associated
with the symlink. The resulting file descriptor is the exact same
thing you would get if you had instead called:

fd = open("/tmp/bar/quux", O_CREAT|O_RDWR);

Hence, when you call

fsync(fd);

What you are calling fsync on is not the _symlink_, but the inode
which is named by /tmp/bar/quux.

Now, as I said in the e-mail I just sent, you _can_ do this:

fd = open ("foo", O_PATH | O_NOFOLLOW);

And this *will* give you a file descriptor which is associated with
the symlink, and not the inode (if it exists) which the symlink points
at. However, there is a very limited number of system calls can
operate on that file descriptor --- and read(2), write(2), and
fsync(2) are not among them.

> fsync(fd) -> succeeds
>
> So perhaps fsync on "symlink" is unsupported behavior that varies from
> file system to file system? We saw ext4 and xfs had this behavior, so
> we assumed it to be the default.

fsync on "symlink" doesn't exist at all today. You were mistaken as
to what you were doing; what you were fsync'ing was the inode that was
created as the result of:

fd = open("symlink", O_CREAT|O_RDWR);

... if you were able to create file in the first place. If "symlink"
points at /tmp/bar/quux, and /tmp/bar does not exist, the open will
fail. Not because it was a symlink, but because the equivalent
open("/tmp/bar/quux", O_CREAT|O_RDWR) would have failed.

Regards,

- Ted