Symlinks must not be opened as it would result in ELOOP, but fallback to fuse_create_open is also not ideal, as it would result in atomic-open + lookup for symlinks. Atomic-open already carries all information lookup provides, so just use that and then call finish_no_open instead of finish_open. Codewise, as finish_no_open consumes a reference, compared to finish_open, dput(alias) must not be called for symlinks. Obviously, if we don't have an additional alias reference yet, we need to get one for symlinks. Signed-off-by: Bernd Schubert <bschubert@xxxxxxx> Cc: Miklos Szeredi <miklos@xxxxxxxxxx> Cc: Dharmendra Singh <dsingh@xxxxxxx> Cc: Horst Birthelmer <hbirthelmer@xxxxxxx> Cc: linux-fsdevel@xxxxxxxxxxxxxxx --- (If preferred, this could be merged into the main fuse atomic revalidate patch). Or adding the function could be moved up in the series. --- fs/fuse/dir.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index c4564831af3c..e8cc33a8b3a2 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -978,9 +978,6 @@ static int _fuse_atomic_open(struct inode *dir, struct dentry *entry, goto out_free_ff; } goto free_and_fallback; - } else if (err == -ELOOP) { - /* likely a symlink */ - goto free_and_fallback; } else { if (d_really_is_positive(entry)) { if (err != -EINTR && err != -ENOMEM) @@ -1090,15 +1087,23 @@ static int _fuse_atomic_open(struct inode *dir, struct dentry *entry, } } - if (S_ISDIR(mode)) + if (S_ISDIR(mode) || S_ISDIR(outentry.attr.mode)) ff->open_flags &= ~FOPEN_DIRECT_IO; - err = finish_open(file, entry, generic_file_open); - if (err) { - fi = get_fuse_inode(inode); - fuse_sync_release(fi, ff, flags); - } else { - file->private_data = ff; - fuse_finish_open(inode, file); + + if (S_ISLNK(outentry.attr.mode)) { + err = finish_no_open(file, entry); + if (!alias) + dget(entry); + } else { + err = finish_open(file, entry, generic_file_open); + if (err) { + fi = get_fuse_inode(inode); + fuse_sync_release(fi, ff, flags); + } else { + file->private_data = ff; + fuse_finish_open(inode, file); + } + dput(alias); } kfree(forget); @@ -1108,8 +1113,6 @@ static int _fuse_atomic_open(struct inode *dir, struct dentry *entry, dput(switched_entry); } - dput(alias); - return err; out_free_ff: -- 2.39.2