From nobody Tue Feb 10 04:13:31 2026 Received: from mail-ed1-f48.google.com (mail-ed1-f48.google.com [209.85.208.48]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 01F4D32FA05 for ; Tue, 23 Dec 2025 12:05:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.48 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766491542; cv=none; b=u5SmqOzAuPhmUqUAtGuksfL3wo2QhElGd/4RmQ3uZJ8JsaBJIGyYPCceFtNIuVCMcS9E+LQw5Y86laqc7MSNNqjgbf3nqqPbd1UaEADs+Ftui64SjCxQWnsM9BVdOmRBVM1mHZ4llCBUFZ4fSzNFb9GLDwgUDkOAuKc8JKEJAkM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766491542; c=relaxed/simple; bh=2ieo10arz+brPKJFMI87VJq/NmMOxd7bYLSsc8X+9fM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=B6vVtMyvEnCRe/52fTGmUiZEFkDy9JCVeTcHaB85FE0P2t0613sl6FAVxg/1M9PJKmO9EFcfgWWsleKoXkK0VSKrN+IjpucJ9IU+Qf/Utwh7Rv6YVFVi7r05P978EBZUlSpC3TrltdCM10AcxBZPR3fHADApseZkqBJ8yVKJcYQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=googlemail.com; spf=pass smtp.mailfrom=googlemail.com; dkim=pass (2048-bit key) header.d=googlemail.com header.i=@googlemail.com header.b=mhw3inuG; arc=none smtp.client-ip=209.85.208.48 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=googlemail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=googlemail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=googlemail.com header.i=@googlemail.com header.b="mhw3inuG" Received: by mail-ed1-f48.google.com with SMTP id 4fb4d7f45d1cf-64d80a47491so3401305a12.1 for ; Tue, 23 Dec 2025 04:05:40 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlemail.com; s=20230601; t=1766491539; x=1767096339; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=k/YXFAj7aTNy6h2Rxea3NMcAMPvFs9nTrahKQ+j5ZUg=; b=mhw3inuG5HyZj9d4T+aPwlKs0AiFViTzd6hvBqFkgCwfQPR/BitTQIS9ygHpXg+Msz rEQD4l16EWjR4d/tERUxoJ7l3hFSk0Jv1u6CWn3njsCIqkTuAAqM4VXuFnOFQZKQaK7h Ff3nArFz1nuNQGyS21ESiHLISos80+zLVAlbFjK3+gre/Jc4aiSNoNqBbKQKf0jyAnlE hoxGHqkS8gAMs5R/bPJkj5lBOeFgoz4pjZV7IVEWXlL5CNdXKH3kyRMrB4SNwSuTAQKQ dT0QR3QjxAMbp6zVWOIVRikW3KZIUNQfYDEoGPb/s7oUPF5c26lGtnqxPkWMghz144Rp HlVw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1766491539; x=1767096339; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=k/YXFAj7aTNy6h2Rxea3NMcAMPvFs9nTrahKQ+j5ZUg=; b=sd/qfsfgsc78tmv/uXjz00SuAsQMin5+aHBUFyP0Uth1SW612glgv97CJRhszHApQZ O+AYiDHptuYIPI09dH60zgN5E9YYQIthTkfm/ZKBte2G7xu9Rf6X56lWtZOtq8yjVSwO b/jFKRKuZf1hf5Noo45P1mmUmO+15LZYPqboecgAx/bqB0noj8SXattbJ7E+VKKLVQKH HRqy1O3N9bLsqxW9f9VzjSJ2h5v55MB8/h1Ro2mD8/9ttuIhYTumeLDG0KsPyaEmGO2t b5Sj1l90KIBuJIesaYV4S398TQOHGer0rB+0FaDSzqypkM8fdPKN7Q1vq340szMZJXXx NCHQ== X-Gm-Message-State: AOJu0Ywf3f1bqtKbaortj3wik7Y0gfDA8RnPCrbqIYGSSfmxlwcmX8+K qyi3WKnP7t7APvW5ibBxYyJcIK6htZxRZOpuht1eFMUk8r+EgQZv0uxN X-Gm-Gg: AY/fxX67LdZHlZ3YP1krGDi6TGBjPnoOufX4AvFocmFJuRAn0LKX/I7h6T6fkRPI+Kg p4wX3qpdibFw9LpkEvJHiTGi9WMfUTTgj5QnLWCHuUFjgv1GOi3+Y9S3Si+N4bGX75GuBb6GnEI TMq6f7QBnf3Nc1oZAIWcge/FJ0rq7hOxW6oLxIOd6+mGh7MLzNzwUPsUUNzPD8MTvOP8VyVkBcA pTJIE1MTY7dqzSsG4VXS6DvU2YbO3USx0CJTRwwt2V4Ab/Hpcl4jIJyI8vXUQcKKYMSDU0jwShz n8xxwdxuMr4YhR/uWmUrHiz+N4GFLMv65heshmRm9vASZmyIU8Kgfuw1XnVEEdWuuQtxpqWEQYx CuyeDOMioaVdzdZsHgZWjOL+JkWOxOWgvcKGH8qoE2clqqRLIzf1o29N+b8Y59Cuwsximfw3JHH HgMfkJfxjjFVCvhSiwl+3/3D5zAKLVSxHkwQV18yO5142lCkDS X-Google-Smtp-Source: AGHT+IFeuGq4gDvEfLh3PK2/dArVoU6Q3VLO40+/E4d8G4cQcJ/hxhUfWRFhtBJgxjohGjsrLtzArQ== X-Received: by 2002:aa7:c583:0:b0:62f:9091:ff30 with SMTP id 4fb4d7f45d1cf-64b58494a1bmr13461374a12.3.1766491539103; Tue, 23 Dec 2025 04:05:39 -0800 (PST) Received: from [127.0.1.1] (178-062-210-188.ip-addr.inexio.net. [188.210.62.178]) by smtp.gmail.com with ESMTPSA id 4fb4d7f45d1cf-64b9105655asm13720460a12.9.2025.12.23.04.05.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 23 Dec 2025 04:05:38 -0800 (PST) From: Horst Birthelmer X-Google-Original-From: Horst Birthelmer Date: Tue, 23 Dec 2025 13:05:30 +0100 Subject: [PATCH RFC 2/2] fuse: add an implementation of open+getattr Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251223-fuse-compounds-upstream-v1-2-7bade663947b@ddn.com> References: <20251223-fuse-compounds-upstream-v1-0-7bade663947b@ddn.com> In-Reply-To: <20251223-fuse-compounds-upstream-v1-0-7bade663947b@ddn.com> To: Miklos Szeredi , Bernd Schubert Cc: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, Horst Birthelmer X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1766491536; l=6870; i=hbirthelmer@ddn.com; s=20251006; h=from:subject:message-id; bh=2ieo10arz+brPKJFMI87VJq/NmMOxd7bYLSsc8X+9fM=; b=9Yjy+Yqd+iMunyIjCyA3kDg48B3wDh/Nn3PzWHahvCkzaf55Mc7lMMSvUSP0Zy9XC3IWwjwhY 0fUEAwDq5FJDmy4kEqoc71IbZvBYZ8vdusy63WxhfE8HHstsksoNt7j X-Developer-Key: i=hbirthelmer@ddn.com; a=ed25519; pk=v3BVDFoy16EzgHZ23ObqW+kbpURtjrwxgKu8YNDKjGg= The discussion about compound commands in fuse was started over an argument to add a new operation that will open a file and return its attributes in the same operation. Here is a demonstration of that use case with compound commands. Signed-off-by: Horst Birthelmer --- fs/fuse/file.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++----= ---- fs/fuse/fuse_i.h | 6 ++- fs/fuse/inode.c | 6 +++ fs/fuse/ioctl.c | 2 +- 4 files changed, 111 insertions(+), 18 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 01bc894e9c2b..fd7f90f9f676 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -126,8 +126,74 @@ static void fuse_file_put(struct fuse_file *ff, bool s= ync) } } =20 +static int fuse_compound_open_getattr(struct fuse_mount *fm, u64 nodeid, + int flags, int opcode, + struct fuse_file *ff, + struct fuse_attr_out *outattrp, + struct fuse_open_out *outargp) +{ + struct fuse_compound_req *compound; + struct fuse_args open_args =3D {}, getattr_args =3D {}; + struct fuse_open_in open_in =3D {}; + struct fuse_getattr_in getattr_in =3D {}; + int err; + + /* Build compound request with flag to execute in the given order */ + compound =3D fuse_compound_alloc(fm, 0); + if (IS_ERR(compound)) + return PTR_ERR(compound); + + /* Add OPEN */ + open_in.flags =3D flags & ~(O_CREAT | O_EXCL | O_NOCTTY); + if (!fm->fc->atomic_o_trunc) + open_in.flags &=3D ~O_TRUNC; + + if (fm->fc->handle_killpriv_v2 && + (open_in.flags & O_TRUNC) && !capable(CAP_FSETID)) { + open_in.open_flags |=3D FUSE_OPEN_KILL_SUIDGID; + } + open_args.opcode =3D opcode; + open_args.nodeid =3D nodeid; + open_args.in_numargs =3D 1; + open_args.in_args[0].size =3D sizeof(open_in); + open_args.in_args[0].value =3D &open_in; + open_args.out_numargs =3D 1; + open_args.out_args[0].size =3D sizeof(struct fuse_open_out); + open_args.out_args[0].value =3D outargp; + + err =3D fuse_compound_add(compound, &open_args); + if (err) + goto out; + + /* Add GETATTR */ + getattr_args.opcode =3D FUSE_GETATTR; + getattr_args.nodeid =3D nodeid; + getattr_args.in_numargs =3D 1; + getattr_args.in_args[0].size =3D sizeof(getattr_in); + getattr_args.in_args[0].value =3D &getattr_in; + getattr_args.out_numargs =3D 1; + getattr_args.out_args[0].size =3D sizeof(struct fuse_attr_out); + getattr_args.out_args[0].value =3D outattrp; + + err =3D fuse_compound_add(compound, &getattr_args); + if (err) + goto out; + + err =3D fuse_compound_send(compound); + if (err) + goto out; + + ff->fh =3D outargp->fh; + ff->open_flags =3D outargp->open_flags; + +out: + fuse_compound_free(compound); + return err; +} + struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid, - unsigned int open_flags, bool isdir) + struct inode *inode, + unsigned int open_flags, bool isdir) { struct fuse_conn *fc =3D fm->fc; struct fuse_file *ff; @@ -153,23 +219,41 @@ struct fuse_file *fuse_file_open(struct fuse_mount *f= m, u64 nodeid, if (open) { /* Store outarg for fuse_finish_open() */ struct fuse_open_out *outargp =3D &ff->args->open_outarg; - int err; + int err =3D -ENOSYS; + + if (inode && fc->compound_open_getattr) { + struct fuse_attr_out attr_outarg; + err =3D fuse_compound_open_getattr(fm, nodeid, open_flags, + opcode, ff, &attr_outarg, outargp); + if (!err) + fuse_change_attributes(inode, &attr_outarg.attr, NULL, + ATTR_TIMEOUT(&attr_outarg), + fuse_get_attr_version(fc)); + } + if (err =3D=3D -ENOSYS) { + err =3D fuse_send_open(fm, nodeid, open_flags, opcode, outargp); =20 - err =3D fuse_send_open(fm, nodeid, open_flags, opcode, outargp); - if (!err) { - ff->fh =3D outargp->fh; - ff->open_flags =3D outargp->open_flags; - } else if (err !=3D -ENOSYS) { - fuse_file_free(ff); - return ERR_PTR(err); - } else { - if (isdir) { + if (!err) { + ff->fh =3D outargp->fh; + ff->open_flags =3D outargp->open_flags; + } + } + + if (err) { + if (err !=3D -ENOSYS) { + /* err is not ENOSYS */ + fuse_file_free(ff); + return ERR_PTR(err); + } else { /* No release needed */ kfree(ff->args); ff->args =3D NULL; - fc->no_opendir =3D 1; - } else { - fc->no_open =3D 1; + + /* we don't have open */ + if (isdir) + fc->no_opendir =3D 1; + else + fc->no_open =3D 1; } } } @@ -185,11 +269,10 @@ struct fuse_file *fuse_file_open(struct fuse_mount *f= m, u64 nodeid, 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_inode(file), fil= e->f_flags, isdir); =20 if (!IS_ERR(ff)) file->private_data =3D ff; - return PTR_ERR_OR_ZERO(ff); } EXPORT_SYMBOL_GPL(fuse_do_open); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 86253517f59b..98af019037c3 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -924,6 +924,9 @@ struct fuse_conn { /* Use io_uring for communication */ unsigned int io_uring; =20 + /* Does the filesystem support compound operations? */ + unsigned int compound_open_getattr:1; + /** Maximum stack depth for passthrough backing files */ int max_stack_depth; =20 @@ -1557,7 +1560,8 @@ void fuse_file_io_release(struct fuse_file *ff, struc= t inode *inode); =20 /* file.c */ struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid, - unsigned int open_flags, bool isdir); + struct inode *inode, + 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/inode.c b/fs/fuse/inode.c index 819e50d66622..a5fd721be96d 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -991,6 +991,12 @@ void fuse_conn_init(struct fuse_conn *fc, struct fuse_= mount *fm, fc->blocked =3D 0; fc->initialized =3D 0; fc->connected =3D 1; + + /* pretend fuse server supports compound operations + * until it tells us otherwise. + */ + fc->compound_open_getattr =3D 1; + atomic64_set(&fc->attr_version, 1); atomic64_set(&fc->evict_ctr, 1); get_random_bytes(&fc->scramble_key, sizeof(fc->scramble_key)); diff --git a/fs/fuse/ioctl.c b/fs/fuse/ioctl.c index fdc175e93f74..07a02e47b2c3 100644 --- a/fs/fuse/ioctl.c +++ b/fs/fuse/ioctl.c @@ -494,7 +494,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.51.0