From nobody Wed Jun 24 01:03:37 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9ADE8C433FE for ; Thu, 24 Feb 2022 03:27:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229575AbiBXD2J (ORCPT ); Wed, 23 Feb 2022 22:28:09 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37408 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229473AbiBXD2G (ORCPT ); Wed, 23 Feb 2022 22:28:06 -0500 Received: from mail-pj1-x1032.google.com (mail-pj1-x1032.google.com [IPv6:2607:f8b0:4864:20::1032]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 930151451F3; Wed, 23 Feb 2022 19:27:37 -0800 (PST) Received: by mail-pj1-x1032.google.com with SMTP id h17-20020a17090acf1100b001bc68ecce4aso4532014pju.4; Wed, 23 Feb 2022 19:27:37 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :organization; bh=wpWe5O7EFXSzjubgSJYvFawDAlnWMvr3GYY6emfmRhE=; b=VyoxDqxegFT4OYZ0T4LGwVRlZ3/5NM0raMJPxOTvOwyBitX+43x6YULdP9LGpeWoag GLRtr1czCGTu2pJdJ+C/KZPTMdybX1TLx9p12kp/Aayf4uF4gkWQu8JzuWi49gAY7DlA jCtOjpNTblJxYB058JDa6q5s84yo5qvLUTsh3VpSdFlwmrrWXUcga523npQv4dnCSErm h/Xc2hCrsuib/lxwFBtsEdGsQqtPt/YoSiywTE3pQBlHoNDzCufgHJwgib8zzT/lSHSC V61qXuk8gwNkBgT3wte8+psVHp6VqBh9E4TQLaeUVZnEsHFX5jvAWsKnscxbgKrtJG84 8UPg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:organization; bh=wpWe5O7EFXSzjubgSJYvFawDAlnWMvr3GYY6emfmRhE=; b=MahQHcFGdx8ASN9ON+/z3dILm0a9EFTUzgu7Irb+GsV4PsXtmlRA/3CTw4Jg5Xv6oX qO41qm2/dJ+cJI1npX2333/9nRrNYaE7yHx7+ZIx8/rCRFwkcRMdNDM4znkNfPyaxFrg IStiABAkJOKm6hd7rIikRiK+ao9ckPQqMzZbi0gSjebFFFItcuTQiE1tQhRUom+UTrqy TKZAd53B13xTX7hnWQuGAYNnJW6d1oNge2XWPiEWWG71g4wgyXmrfufWl+Eg+0T6wngB 97GAmx5CuGlIl65XkJ2fE27lrgdBVhGTK8P/laD6o+9nGTct7NfdKby2fBP+lHCn04SW 6TVA== X-Gm-Message-State: AOAM5320NNe7rzlpQjKupxH+rL7fo41E8+WImZ0QngJiRgsyhXruBhsE o1TPNYfRIAcL2y78YwHI1b0= X-Google-Smtp-Source: ABdhPJxncnTk+OcyBNz5+oTh/qIVH8uAv/3BZCykaSCZ6aoUgUkBuukHFu7cyDng5NTar2vvFeJuFA== X-Received: by 2002:a17:90a:2e04:b0:1bc:a5db:bcdb with SMTP id q4-20020a17090a2e0400b001bca5dbbcdbmr4807311pjd.116.1645673256980; Wed, 23 Feb 2022 19:27:36 -0800 (PST) Received: from localhost.localdomain ([123.201.194.48]) by smtp.googlemail.com with ESMTPSA id 23-20020a17090a0d5700b001bc3c650e01sm7236178pju.1.2022.02.23.19.27.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 23 Feb 2022 19:27:36 -0800 (PST) From: Dharmendra Singh To: miklos@szeredi.hu Cc: Dharmendra Singh , linux-fsdevel@vger.kernel.org, fuse-devel@lists.sourceforge.net, linux-kernel@vger.kernel.org, Dharmendra Singh Subject: [PATCH 1/2] FUSE: Implement atomic lookup + open Date: Thu, 24 Feb 2022 08:53:36 +0530 Message-Id: <20220224032337.19284-2-dharamhans87@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20220224032337.19284-1-dharamhans87@gmail.com> References: <20220224032337.19284-1-dharamhans87@gmail.com> Organization: DDN STORAGE Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: Dharmendra Singh There are couple of places in FUSE where we do agressive lookup. 1) When we go for creating a file (O_CREAT), we do lookup for non-existent file. It is very much likely that file does not exists yet as O_CREAT is passed to open(). This lookup can be avoided and can be performed as part of open call into libfuse. 2) When there is normal open for file/dir (dentry is new/negative). In this case since we are anyway going to open the file/dir with USER space, avoid this separate lookup call into libfuse and combine it with open. This lookup + open in single call to libfuse and finally to USER space has been named as atomic open. It is expected that USER space open the file and fills in the attributes which are then used to make inode stand/revalidate in the kernel cache. Signed-off-by: Dharmendra Singh --- fs/fuse/dir.c | 105 ++++++++++++++++++++++++++++++-------- fs/fuse/fuse_i.h | 3 ++ fs/fuse/inode.c | 4 +- include/uapi/linux/fuse.h | 2 + 4 files changed, 93 insertions(+), 21 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 656e921f3506..48fb126d44ad 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -516,16 +516,14 @@ static int get_security_context(struct dentry *entry,= umode_t mode, } =20 /* - * Atomic create+open operation - * - * If the filesystem doesn't support this, then fall back to separate - * 'mknod' + 'open' requests. + * Perform create + open or lookup + open in single call to libfuse */ -static int fuse_create_open(struct inode *dir, struct dentry *entry, - struct file *file, unsigned int flags, - umode_t mode) +static int fuse_atomic_open_common(struct inode *dir, struct dentry *entry, + struct dentry **alias, struct file *file, + unsigned int flags, umode_t mode, + uint32_t opcode) { - int err; + bool create =3D (opcode =3D=3D FUSE_CREATE ? true : false); struct inode *inode; struct fuse_mount *fm =3D get_fuse_mount(dir); FUSE_ARGS(args); @@ -535,11 +533,16 @@ static int fuse_create_open(struct inode *dir, struct= dentry *entry, struct fuse_entry_out outentry; struct fuse_inode *fi; struct fuse_file *ff; + struct dentry *res =3D NULL; void *security_ctx =3D NULL; u32 security_ctxlen; + int err; + + if (alias) + *alias =3D NULL; =20 /* Userspace expects S_IFREG in create mode */ - BUG_ON((mode & S_IFMT) !=3D S_IFREG); + BUG_ON(create && (mode & S_IFMT) !=3D S_IFREG); =20 forget =3D fuse_alloc_forget(); err =3D -ENOMEM; @@ -554,7 +557,13 @@ static int fuse_create_open(struct inode *dir, struct = dentry *entry, if (!fm->fc->dont_mask) mode &=3D ~current_umask(); =20 - flags &=3D ~O_NOCTTY; + if (!create) { + flags =3D flags & ~(O_CREAT | O_EXCL | O_NOCTTY); + if (!fm->fc->atomic_o_trunc) + flags &=3D ~O_TRUNC; + } else { + flags &=3D ~O_NOCTTY; + } memset(&inarg, 0, sizeof(inarg)); memset(&outentry, 0, sizeof(outentry)); inarg.flags =3D flags; @@ -566,7 +575,7 @@ static int fuse_create_open(struct inode *dir, struct d= entry *entry, inarg.open_flags |=3D FUSE_OPEN_KILL_SUIDGID; } =20 - args.opcode =3D FUSE_CREATE; + args.opcode =3D opcode; args.nodeid =3D get_node_id(dir); args.in_numargs =3D 2; args.in_args[0].size =3D sizeof(inarg); @@ -595,8 +604,12 @@ static int fuse_create_open(struct inode *dir, struct = dentry *entry, if (err) goto out_free_ff; =20 + err =3D -ENOENT; + if (!S_ISDIR(outentry.attr.mode) && !outentry.nodeid) + goto out_free_ff; + err =3D -EIO; - if (!S_ISREG(outentry.attr.mode) || invalid_nodeid(outentry.nodeid) || + if (invalid_nodeid(outentry.nodeid) || fuse_invalid_attr(&outentry.attr)) goto out_free_ff; =20 @@ -612,10 +625,29 @@ static int fuse_create_open(struct inode *dir, struct= dentry *entry, err =3D -ENOMEM; goto out_err; } + /* + * Close the file in user space, but do not unlink it, if it was + * created - with network file systems other clients might have + * already accessed it. + */ + res =3D d_splice_alias(inode, entry); + if (res) { + if (IS_ERR(res)) { + fi =3D get_fuse_inode(inode); + fuse_sync_release(fi, ff, flags); + fuse_queue_forget(fm->fc, forget, outentry.nodeid, 1); + err =3D PTR_ERR(res); + goto out_err; + } else { + entry =3D res; + if (alias) + *alias =3D res; + } + } kfree(forget); - d_instantiate(entry, inode); fuse_change_entry_timeout(entry, &outentry); - fuse_dir_changed(dir); + if (create) + fuse_dir_changed(dir); err =3D finish_open(file, entry, generic_file_open); if (err) { fi =3D get_fuse_inode(inode); @@ -634,20 +666,49 @@ static int fuse_create_open(struct inode *dir, struct= dentry *entry, return err; } =20 +/* + * Atomic lookup + open + */ + +static int fuse_do_atomic_open(struct inode *dir, struct dentry *entry, + struct dentry **alias, struct file *file, + unsigned int flags, umode_t mode) +{ + int err; + struct fuse_conn *fc =3D get_fuse_conn(dir); + + if (!fc->do_atomic_open) + return -ENOSYS; + err =3D fuse_atomic_open_common(dir, entry, alias, file, + flags, mode, FUSE_ATOMIC_OPEN); + return err; +} + static int fuse_mknod(struct user_namespace *, struct inode *, struct dent= ry *, umode_t, dev_t); static int fuse_atomic_open(struct inode *dir, struct dentry *entry, struct file *file, unsigned flags, umode_t mode) { - int err; + bool create =3D (flags & O_CREAT) ? true : false; struct fuse_conn *fc =3D get_fuse_conn(dir); - struct dentry *res =3D NULL; + struct dentry *res =3D NULL, *alias =3D NULL; + int err; =20 if (fuse_is_bad(dir)) return -EIO; =20 - if (d_in_lookup(entry)) { + /* Atomic lookup + open - dentry might be File or Directory */ + if (!create) { + err =3D fuse_do_atomic_open(dir, entry, &alias, file, flags, mode); + res =3D alias; + if (!err) + goto out_dput; + else if (err !=3D -ENOSYS) + goto no_open; + } + /* ENOSYS fall back - user space does not have full atomic open */ + if (!create && d_in_lookup(entry)) { res =3D fuse_lookup(dir, entry, 0); if (IS_ERR(res)) return PTR_ERR(res); @@ -656,7 +717,7 @@ static int fuse_atomic_open(struct inode *dir, struct d= entry *entry, entry =3D res; } =20 - if (!(flags & O_CREAT) || d_really_is_positive(entry)) + if (!create || d_really_is_positive(entry)) goto no_open; =20 /* Only creates */ @@ -664,8 +725,12 @@ static int fuse_atomic_open(struct inode *dir, struct = dentry *entry, =20 if (fc->no_create) goto mknod; - - err =3D fuse_create_open(dir, entry, file, flags, mode); + /* + * If the filesystem doesn't support atomic create + open, then fall + * back to separate 'mknod' + 'open' requests. + */ + err =3D fuse_atomic_open_common(dir, entry, NULL, file, flags, mode, + FUSE_CREATE); if (err =3D=3D -ENOSYS) { fc->no_create =3D 1; goto mknod; diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index e8e59fbdefeb..e4dc68a90b28 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -669,6 +669,9 @@ struct fuse_conn { /** Is open/release not implemented by fs? */ unsigned no_open:1; =20 + /** Does the filesystem support atomic open? */ + unsigned do_atomic_open:1; + /** Is opendir/releasedir not implemented by fs? */ unsigned no_opendir:1; =20 diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index ee846ce371d8..2a14131cb9fe 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -1190,6 +1190,8 @@ static void process_init_reply(struct fuse_mount *fm,= struct fuse_args *args, fc->setxattr_ext =3D 1; if (flags & FUSE_SECURITY_CTX) fc->init_security =3D 1; + if (arg->flags & FUSE_DO_ATOMIC_OPEN) + fc->do_atomic_open =3D 1; } else { ra_pages =3D fc->max_read / PAGE_SIZE; fc->no_lock =3D 1; @@ -1235,7 +1237,7 @@ void fuse_send_init(struct fuse_mount *fm) FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS | FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA | FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT | FUSE_INIT_EXT | - FUSE_SECURITY_CTX; + FUSE_SECURITY_CTX | FUSE_DO_ATOMIC_OPEN; #ifdef CONFIG_FUSE_DAX if (fm->fc->dax) flags |=3D FUSE_MAP_ALIGNMENT; diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index d6ccee961891..a28dd60078ff 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -389,6 +389,7 @@ struct fuse_file_lock { /* bits 32..63 get shifted down 32 bits into the flags2 field */ #define FUSE_SECURITY_CTX (1ULL << 32) #define FUSE_HAS_INODE_DAX (1ULL << 33) +#define FUSE_DO_ATOMIC_OPEN (1ULL << 34) =20 /** * CUSE INIT request/reply flags @@ -537,6 +538,7 @@ enum fuse_opcode { FUSE_SETUPMAPPING =3D 48, FUSE_REMOVEMAPPING =3D 49, FUSE_SYNCFS =3D 50, + FUSE_ATOMIC_OPEN =3D 51, =20 /* CUSE specific operations */ CUSE_INIT =3D 4096, --=20 2.17.1 From nobody Wed Jun 24 01:03:37 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DD41EC433F5 for ; Thu, 24 Feb 2022 03:27:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229621AbiBXD2Q (ORCPT ); Wed, 23 Feb 2022 22:28:16 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38134 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229614AbiBXD2N (ORCPT ); Wed, 23 Feb 2022 22:28:13 -0500 Received: from mail-pg1-x52a.google.com (mail-pg1-x52a.google.com [IPv6:2607:f8b0:4864:20::52a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 867A814CD8C; Wed, 23 Feb 2022 19:27:43 -0800 (PST) Received: by mail-pg1-x52a.google.com with SMTP id 75so637511pgb.4; Wed, 23 Feb 2022 19:27:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :organization; bh=EqTqbG22nPy0+F73wZdoZ2+JENECZ7gltb4t3SUedlU=; b=fCeqiNLIn6cD3ibV5it1hkRppJHEs7urCEdW+JndTbN9BUWY7Fa2wO9byY62Y38JCE xVoVvqMLPlD3xgZh0uPdHeORM913opyRuOqGXZr3wJpggAD0NDjLxkDUtLlAEeHonnm0 oNMO4v6yZid5DY5WmYxgvsWs2VOTc+q9+qF7CLOY//jWGjm6nj8ZkuWUVSQkR3rrhiyN BpvMo2ujFMVVH48lKQYwkIvAgtmToj+kRMfMVkdfoa5IYAkEekWJbOqV8MVMRJNtwHrJ c8TmdEXr+je6KqvDcB7NcA5Fo2PyYRRNFzAJop4dtVJDvyV+oYVN6RZkE8P7i8RKifG5 kT2A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:organization; bh=EqTqbG22nPy0+F73wZdoZ2+JENECZ7gltb4t3SUedlU=; b=gZTAYvmwFNDzSe0LhpuzVp9YvCvfeXwIJowAdC2wFeOEYSiJvZmMjPL5vyHTqDT3uG pLsWLJLZbSFlrC6E3LJ30CoofEuggGVGO+xYec7gi86dO+qvu/zPfFR+6AwCkAXMizhV En2j7RuOf1x24PyS6ya/xTrQLs3jWAbiiEmUjw5pv0CPIpw2YZgDbC4eSm/k8vBgvBHo QQF5tkimZfP13Cf8NrH0o0kGc37Fm2haFVsetTHtTtgLWENHoCVZQwUc8/hm0jFI8uis B08cmbk3Uzvjail9c++oe5CFBOCBG/kePADpFDVrQyTF1wN5cLHi2AKkiTGdyS35RSFR nkVA== X-Gm-Message-State: AOAM531bA3esHM7cJ3xvpfKyYYgVhROR9NxOYkrVOW1uPsgDJbfc6ZFl vpkCNZmZZlu+KYBwAgOlrryWtrm/2tAlUODWZvM= X-Google-Smtp-Source: ABdhPJzx9UjexHA8CQiEehW/T/5pTebS+BXzno38AEHglZXF8ROKW9k748udpBMHngfemGWb5E+vEw== X-Received: by 2002:a63:d252:0:b0:363:271c:fe63 with SMTP id t18-20020a63d252000000b00363271cfe63mr771426pgi.524.1645673262909; Wed, 23 Feb 2022 19:27:42 -0800 (PST) Received: from localhost.localdomain ([123.201.194.48]) by smtp.googlemail.com with ESMTPSA id 23-20020a17090a0d5700b001bc3c650e01sm7236178pju.1.2022.02.23.19.27.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 23 Feb 2022 19:27:42 -0800 (PST) From: Dharmendra Singh To: miklos@szeredi.hu Cc: Dharmendra Singh , linux-fsdevel@vger.kernel.org, fuse-devel@lists.sourceforge.net, linux-kernel@vger.kernel.org, Dharmendra Singh Subject: [PATCH 2/2] FUSE: Avoid lookup in d_revalidate() Date: Thu, 24 Feb 2022 08:53:37 +0530 Message-Id: <20220224032337.19284-3-dharamhans87@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20220224032337.19284-1-dharamhans87@gmail.com> References: <20220224032337.19284-1-dharamhans87@gmail.com> Organization: DDN STORAGE Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: Dharmendra Singh With atomic open + lookup implemented, it is possible to avoid lookups in FUSE d_revalidate() for objects other than directories. If FUSE is mounted with default permissions then this optimization is not possible as we need to fetch fresh inode attributes for permission check. This lookup skipped in d_revalidate() can be performed as part of open call into libfuse which is made from fuse_file_open(). And when we return from USER space with file opened and fresh attributes, we can revalidate the inode. Signed-off-by: Dharmendra Singh --- fs/fuse/dir.c | 81 +++++++++++++++++++++++++++++++++++++++++++----- fs/fuse/file.c | 30 ++++++++++++++++-- fs/fuse/fuse_i.h | 10 +++++- fs/fuse/ioctl.c | 2 +- 4 files changed, 110 insertions(+), 13 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 48fb126d44ad..76c60eaee0c0 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -196,6 +196,7 @@ static void fuse_lookup_init(struct fuse_conn *fc, stru= ct fuse_args *args, * the lookup once more. If the lookup results in the same inode, * then refresh the attributes, timeouts and mark the dentry valid. */ + static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) { struct inode *inode; @@ -224,6 +225,17 @@ static int fuse_dentry_revalidate(struct dentry *entry= , unsigned int flags) =20 fm =3D get_fuse_mount(inode); =20 + /* If atomic open is supported by FUSE then use this opportunity + * (only for non-dir) to avoid this lookup and combine + * lookup + open into single call. + */ + if (!fm->fc->default_permissions && fm->fc->do_atomic_open && + !(flags & (LOOKUP_EXCL | LOOKUP_REVAL)) && + (flags & LOOKUP_OPEN) && !S_ISDIR(inode->i_mode)) { + ret =3D 1; + goto out; + } + forget =3D fuse_alloc_forget(); ret =3D -ENOMEM; if (!forget) @@ -515,13 +527,50 @@ static int get_security_context(struct dentry *entry,= umode_t mode, return err; } =20 +/* + * Revalidate the inode after we got fresh attributes from user space. + */ +static int fuse_atomic_open_revalidate_inode(struct inode *reval_inode, + struct dentry *entry, + struct fuse_inode *fi, + struct fuse_forget_link *forget, + struct fuse_entry_out *outentry, + u64 attr_version) +{ + struct fuse_conn *fc =3D get_fuse_conn(reval_inode); + /* Mode should be other than directory */ + BUG_ON(S_ISDIR(reval_inode->i_mode)); + + if (outentry->nodeid !=3D get_node_id(reval_inode)) { + fuse_queue_forget(fc, forget, outentry->nodeid, 1); + return -ESTALE; + } + if (fuse_stale_inode(reval_inode, outentry->generation, + &outentry->attr)) { + fuse_make_bad(reval_inode); + return -ESTALE; + } + fi =3D get_fuse_inode(reval_inode); + spin_lock(&fi->lock); + fi->nlookup++; + spin_unlock(&fi->lock); + + forget_all_cached_acls(reval_inode); + fuse_change_attributes(reval_inode, &outentry->attr, + entry_attr_timeout(outentry), attr_version); + fuse_change_entry_timeout(entry, outentry); + return 0; +} + + + /* * Perform create + open or lookup + open in single call to libfuse */ -static int fuse_atomic_open_common(struct inode *dir, struct dentry *entry, - struct dentry **alias, struct file *file, - unsigned int flags, umode_t mode, - uint32_t opcode) +int fuse_atomic_open_common(struct inode *dir, struct dentry *entry, + struct dentry **alias, struct file *file, + struct inode *reval_inode, unsigned int flags, + umode_t mode, uint32_t opcode) { bool create =3D (opcode =3D=3D FUSE_CREATE ? true : false); struct inode *inode; @@ -536,6 +585,7 @@ static int fuse_atomic_open_common(struct inode *dir, s= truct dentry *entry, struct dentry *res =3D NULL; void *security_ctx =3D NULL; u32 security_ctxlen; + u64 attr_version =3D fuse_get_attr_version(fm->fc); int err; =20 if (alias) @@ -616,6 +666,19 @@ static int fuse_atomic_open_common(struct inode *dir, = struct dentry *entry, ff->fh =3D outopen.fh; ff->nodeid =3D outentry.nodeid; ff->open_flags =3D outopen.open_flags; + + /* Inode revalidation was bypassed previously for type other than + * directories, revalidate now as we got fresh attributes. + */ + if (reval_inode) { + err =3D fuse_atomic_open_revalidate_inode(reval_inode, entry, fi, + forget, &outentry, + attr_version); + if (err) + goto out_free_ff; + inode =3D reval_inode; + goto out_finish_open; + } inode =3D fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation, &outentry.attr, entry_attr_timeout(&outentry), 0); if (!inode) { @@ -649,6 +712,7 @@ static int fuse_atomic_open_common(struct inode *dir, s= truct dentry *entry, if (create) fuse_dir_changed(dir); err =3D finish_open(file, entry, generic_file_open); +out_finish_open: if (err) { fi =3D get_fuse_inode(inode); fuse_sync_release(fi, ff, flags); @@ -679,7 +743,7 @@ static int fuse_do_atomic_open(struct inode *dir, struc= t dentry *entry, =20 if (!fc->do_atomic_open) return -ENOSYS; - err =3D fuse_atomic_open_common(dir, entry, alias, file, + err =3D fuse_atomic_open_common(dir, entry, alias, file, NULL, flags, mode, FUSE_ATOMIC_OPEN); return err; } @@ -700,7 +764,8 @@ static int fuse_atomic_open(struct inode *dir, struct d= entry *entry, =20 /* Atomic lookup + open - dentry might be File or Directory */ if (!create) { - err =3D fuse_do_atomic_open(dir, entry, &alias, file, flags, mode); + err =3D fuse_do_atomic_open(dir, entry, &alias, file, + flags, mode); res =3D alias; if (!err) goto out_dput; @@ -729,8 +794,8 @@ static int fuse_atomic_open(struct inode *dir, struct d= entry *entry, * If the filesystem doesn't support atomic create + open, then fall * back to separate 'mknod' + 'open' requests. */ - err =3D fuse_atomic_open_common(dir, entry, NULL, file, flags, mode, - FUSE_CREATE); + err =3D fuse_atomic_open_common(dir, entry, NULL, file, NULL, flags, + mode, FUSE_CREATE); if (err =3D=3D -ENOSYS) { fc->no_create =3D 1; goto mknod; diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 829094451774..37eebfb90500 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -125,11 +125,15 @@ static void fuse_file_put(struct fuse_file *ff, bool = sync, bool isdir) } =20 struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid, - unsigned int open_flags, bool isdir) + struct file *file, unsigned int open_flags, + bool isdir) { struct fuse_conn *fc =3D fm->fc; struct fuse_file *ff; + struct dentry *dentry =3D NULL; + struct dentry *parent =3D NULL; int opcode =3D isdir ? FUSE_OPENDIR : FUSE_OPEN; + int ret; =20 ff =3D fuse_file_alloc(fm); if (!ff) @@ -138,6 +142,11 @@ struct fuse_file *fuse_file_open(struct fuse_mount *fm= , u64 nodeid, ff->fh =3D 0; /* Default for no-open */ ff->open_flags =3D FOPEN_KEEP_CACHE | (isdir ? FOPEN_CACHE_DIR : 0); + + /* For directories we already had lookup */ + if (!isdir && fc->do_atomic_open && file !=3D NULL) + goto revalidate_atomic_open; + if (isdir ? !fc->no_opendir : !fc->no_open) { struct fuse_open_out outarg; int err; @@ -164,12 +173,27 @@ struct fuse_file *fuse_file_open(struct fuse_mount *f= m, u64 nodeid, ff->nodeid =3D nodeid; =20 return ff; + +revalidate_atomic_open: + dentry =3D file->f_path.dentry; + /* Get ref on parent */ + parent =3D dget_parent(dentry); + ret =3D fuse_atomic_open_common(d_inode_rcu(parent), dentry, NULL, file, + d_inode_rcu(dentry), open_flags, 0, + FUSE_ATOMIC_OPEN); + dput(parent); + if (ret) + goto err_out; + ff =3D file->private_data; + return ff; +err_out: + return ERR_PTR(ret); } =20 int fuse_do_open(struct fuse_mount *fm, u64 nodeid, struct file *file, bool isdir) { - struct fuse_file *ff =3D fuse_file_open(fm, nodeid, file->f_flags, isdir); + struct fuse_file *ff =3D fuse_file_open(fm, nodeid, file, file->f_flags, = isdir); =20 if (!IS_ERR(ff)) file->private_data =3D ff; @@ -252,7 +276,7 @@ int fuse_open_common(struct inode *inode, struct file *= file, bool isdir) } =20 err =3D fuse_do_open(fm, get_node_id(inode), file, isdir); - if (!err) + if (!err && (!fc->do_atomic_open || isdir)) fuse_finish_open(inode, file); =20 out: diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index e4dc68a90b28..bb3cd0631ff2 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1011,6 +1011,13 @@ void fuse_finish_open(struct inode *inode, struct fi= le *file); void fuse_sync_release(struct fuse_inode *fi, struct fuse_file *ff, unsigned int flags); =20 +/** + * Send atomic create + open or lookup + open + */ +int fuse_atomic_open_common(struct inode *dir, struct dentry *entry, + struct dentry **alias, struct file *file, + struct inode *reval_inode, unsigned int flags, + umode_t mode, uint32_t opcode); /** * Send RELEASE or RELEASEDIR request */ @@ -1314,7 +1321,8 @@ int fuse_fileattr_set(struct user_namespace *mnt_user= ns, /* file.c */ =20 struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid, - unsigned int open_flags, bool isdir); + struct file *file, unsigned int open_flags, + bool isdir); void fuse_file_release(struct inode *inode, struct fuse_file *ff, unsigned int open_flags, fl_owner_t id, bool isdir); =20 diff --git a/fs/fuse/ioctl.c b/fs/fuse/ioctl.c index fbc09dab1f85..63106a54ba1a 100644 --- a/fs/fuse/ioctl.c +++ b/fs/fuse/ioctl.c @@ -408,7 +408,7 @@ static struct fuse_file *fuse_priv_ioctl_prepare(struct= inode *inode) if (!S_ISREG(inode->i_mode) && !isdir) return ERR_PTR(-ENOTTY); =20 - return fuse_file_open(fm, get_node_id(inode), O_RDONLY, isdir); + return fuse_file_open(fm, get_node_id(inode), NULL, O_RDONLY, isdir); } =20 static void fuse_priv_ioctl_cleanup(struct inode *inode, struct fuse_file = *ff) --=20 2.17.1