From patchwork Tue Nov 22 02:15:16 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13051814 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 7FB80C4167B for ; Tue, 22 Nov 2022 02:16:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232446AbiKVCQA (ORCPT ); Mon, 21 Nov 2022 21:16:00 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41574 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229509AbiKVCP5 (ORCPT ); Mon, 21 Nov 2022 21:15:57 -0500 Received: from mail-pj1-x1049.google.com (mail-pj1-x1049.google.com [IPv6:2607:f8b0:4864:20::1049]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 07D05E2218 for ; Mon, 21 Nov 2022 18:15:57 -0800 (PST) Received: by mail-pj1-x1049.google.com with SMTP id p1-20020a17090a2c4100b00212733d7aaaso6784175pjm.4 for ; Mon, 21 Nov 2022 18:15:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=JJiNTZDAcyBcjnZ9p/mphnfF01eV5b88yfC5YuUnZmM=; b=cfFs8F08u2LB6NT9MopIhiguqfQ7WXtFPUO7m1nt1MVmOu8TptOmISB0P+7KLmTetQ Dx6lCs7ljbXbkaXpWqn/sW+iZOX+CNmO7ANeKRWLmvWd0aGdTYHO92RDZ1f/BrwkmBDP esLlCvlF97McdXxQ7QmYwruQ2pTwIQx0q70gHeZE2pWIzYszefNf0R6ekyDZrQ3Bt13l YT3P4alOgbtMQFtKHuealnqJv7qBSCbl1waS4ud1ps5m+emFEYQ+c1p9be6XzvR0zo+O dLPKt+o3ppBSzUto+aIhc/AtAxi6lFKW2dBnHW+Fj58e8ptadwzho1KMyUrjYFPCbA6U jTbQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=JJiNTZDAcyBcjnZ9p/mphnfF01eV5b88yfC5YuUnZmM=; b=ywagqwVsyLE/ZLjbaxvtipsX7f1CrsCk3jLf1Hy6svkdAiNupStAdGIL7TxMWZrv4e owlIgVa5IP4ASdVXtldf3fye9SZyFgSSDX1RSZKdA7VsCSTZVSP0l9Gke/Gw6NOcxRRU a+c+ORVRRCvs8+TFcXQHqB2wqrcg4o1ALDoXwkdh10j1DhBVKcTdwEf8q1beKSCfqsgN ujz0hx3r6EtPfZSJMLD8GuVzDNA12JWeP5503jxRo4kKBb7Bys+fqv4JSsoox9UUvee+ +azV5sHVwqhw3nA5/R1yB+kzIbObMajq2YO8lSeSofYFxgXsfIgvRuxAGcPN7n3P+FeO o62w== X-Gm-Message-State: ANoB5pnvp2yQGxz2QlTXsPQhebp3mgp8jCYxdP7lcNfq4D4TTtXtrg3j Qt9XYopA7lkDTo3BgL/lHAxUWsm1Qr8= X-Google-Smtp-Source: AA0mqf73qIstQw2HWTMqtYCCQgojy0QXuvOTHs6hL/WCgKQAuOC6w/AOOmJG1SLeSt+nZlnhLKI8SHSucd4= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:200:8539:aadd:13be:6e82]) (user=drosen job=sendgmr) by 2002:a65:67c5:0:b0:477:76c0:1d13 with SMTP id b5-20020a6567c5000000b0047776c01d13mr3165786pgs.55.1669083356254; Mon, 21 Nov 2022 18:15:56 -0800 (PST) Date: Mon, 21 Nov 2022 18:15:16 -0800 In-Reply-To: <20221122021536.1629178-1-drosen@google.com> Mime-Version: 1.0 References: <20221122021536.1629178-1-drosen@google.com> X-Mailer: git-send-email 2.38.1.584.g0f3c55d4c2-goog Message-ID: <20221122021536.1629178-2-drosen@google.com> Subject: [RFC PATCH v2 01/21] fs: Generic function to convert iocb to rw flags From: Daniel Rosenberg To: Miklos Szeredi Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, bpf@vger.kernel.org, kernel-team@android.com, Alessio Balsini , Alessio Balsini Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC From: Alessio Balsini OverlayFS implements its own function to translate iocb flags into rw flags, so that they can be passed into another vfs call. With commit ce71bfea207b4 ("fs: align IOCB_* flags with RWF_* flags") Jens created a 1:1 matching between the iocb flags and rw flags, simplifying the conversion. Reduce the OverlayFS code by making the flag conversion function generic and reusable. Signed-off-by: Alessio Balsini --- fs/overlayfs/file.c | 23 +++++------------------ include/linux/fs.h | 5 +++++ 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index a1a22f58ba18..287ae968852a 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -15,6 +15,8 @@ #include #include "overlayfs.h" +#define OVL_IOCB_MASK (IOCB_DSYNC | IOCB_HIPRI | IOCB_NOWAIT | IOCB_SYNC) + struct ovl_aio_req { struct kiocb iocb; refcount_t ref; @@ -240,22 +242,6 @@ static void ovl_file_accessed(struct file *file) touch_atime(&file->f_path); } -static rwf_t ovl_iocb_to_rwf(int ifl) -{ - rwf_t flags = 0; - - if (ifl & IOCB_NOWAIT) - flags |= RWF_NOWAIT; - if (ifl & IOCB_HIPRI) - flags |= RWF_HIPRI; - if (ifl & IOCB_DSYNC) - flags |= RWF_DSYNC; - if (ifl & IOCB_SYNC) - flags |= RWF_SYNC; - - return flags; -} - static inline void ovl_aio_put(struct ovl_aio_req *aio_req) { if (refcount_dec_and_test(&aio_req->ref)) { @@ -315,7 +301,8 @@ static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter) old_cred = ovl_override_creds(file_inode(file)->i_sb); if (is_sync_kiocb(iocb)) { ret = vfs_iter_read(real.file, iter, &iocb->ki_pos, - ovl_iocb_to_rwf(iocb->ki_flags)); + iocb_to_rw_flags(iocb->ki_flags, + OVL_IOCB_MASK)); } else { struct ovl_aio_req *aio_req; @@ -379,7 +366,7 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter) if (is_sync_kiocb(iocb)) { file_start_write(real.file); ret = vfs_iter_write(real.file, iter, &iocb->ki_pos, - ovl_iocb_to_rwf(ifl)); + iocb_to_rw_flags(ifl, OVL_IOCB_MASK)); file_end_write(real.file); /* Update size */ ovl_copyattr(inode); diff --git a/include/linux/fs.h b/include/linux/fs.h index e654435f1651..c913106fdd65 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -3434,6 +3434,11 @@ static inline int kiocb_set_rw_flags(struct kiocb *ki, rwf_t flags) return 0; } +static inline rwf_t iocb_to_rw_flags(int ifl, int iocb_mask) +{ + return ifl & iocb_mask; +} + static inline ino_t parent_ino(struct dentry *dentry) { ino_t res; From patchwork Tue Nov 22 02:15:17 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13051815 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 83558C46467 for ; Tue, 22 Nov 2022 02:16:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232467AbiKVCQB (ORCPT ); Mon, 21 Nov 2022 21:16:01 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41596 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229671AbiKVCP7 (ORCPT ); Mon, 21 Nov 2022 21:15:59 -0500 Received: from mail-yb1-xb4a.google.com (mail-yb1-xb4a.google.com [IPv6:2607:f8b0:4864:20::b4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2E82FE0CB0 for ; Mon, 21 Nov 2022 18:15:59 -0800 (PST) Received: by mail-yb1-xb4a.google.com with SMTP id b17-20020a25b851000000b006e32b877068so12626228ybm.16 for ; Mon, 21 Nov 2022 18:15:59 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=Y0CYFA8id+z3HCWs5kIS2LaBGk6W/Uf1gWskynavTxc=; b=ShxBlDIAwmGqs4fi1P8OTQDitIxkzNdyy6obpBx2DiuBICfglgIm/sEJ56k72mcrDk 7sLoBmC3BCBKNBVTw+a5yisI9f+LbrX/R2U/hPfRfpMyfdvSp6UftfpxF8ptuAxhR1Mc QGRV0dQNmsWkVelI+ut+E9YXVVVey/xFndnaY0+7hsgFk0UClNwfx7uB/GiuIE3WJxKQ 1tWaQL+uuqyCVQsmD7uv1PiRFexy5YwGgOZ9H2D50koMDi22pO5sT5Kyfhs1JNRKiAwl aIbAYznQ2PAOlxao5zbdAc37GRH4JJZVovarG4GlgWKkZq84useB0vbbhW6mKS1ak4Gp rFrg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=Y0CYFA8id+z3HCWs5kIS2LaBGk6W/Uf1gWskynavTxc=; b=M21ZptznvCIAIl8sEHPKcyffZ1WYYfGvDk8q/mGzDMmffgYms0h10Z7t5XTmqFY0PC AlyF5SpHRguguKxCz5v4yaJeVqSPo4s9ZdfuNAUO6omQpJG0BUyx7UUQbpZUsM7bliny GjQgW52jxDLzULbZXaHyMIqs9WztR0YXTWCmgh8KXXtOzqFSAOVADJGzIAc6inQpGbkp 5qy5KCO1Mm1deJvkrQF2bBI3vCnM9CvMT2TC6NwfwEOWQqsk1Ql4sGHi6Z5Kev1zrFRS B30BWsOWrFoaYub7d3vmLt05w73u0hOa78ehLEZhpiWMeyHJCQ8kjWi9hK/W93lqia6x wiSw== X-Gm-Message-State: ANoB5pmnO+0MYTAOnfAJS7bdgcnU6b/haRJZ42hQlD1uQaNBHz3dSZ2O J4RZC3IgIdnbzVdK3+phLbzzle+mb7A= X-Google-Smtp-Source: AA0mqf5Qa1+oao+nkSGR5uF4SNsNwocRj9uOPh0IhMr0Lv2HxEcrI6FPMQ8VuaivB5WjRzhmz9adJH/pD3Q= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:200:8539:aadd:13be:6e82]) (user=drosen job=sendgmr) by 2002:a81:994a:0:b0:36f:d061:dfcd with SMTP id q71-20020a81994a000000b0036fd061dfcdmr1364844ywg.188.1669083358500; Mon, 21 Nov 2022 18:15:58 -0800 (PST) Date: Mon, 21 Nov 2022 18:15:17 -0800 In-Reply-To: <20221122021536.1629178-1-drosen@google.com> Mime-Version: 1.0 References: <20221122021536.1629178-1-drosen@google.com> X-Mailer: git-send-email 2.38.1.584.g0f3c55d4c2-goog Message-ID: <20221122021536.1629178-3-drosen@google.com> Subject: [RFC PATCH v2 02/21] fuse-bpf: Update fuse side uapi From: Daniel Rosenberg To: Miklos Szeredi Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, bpf@vger.kernel.org, kernel-team@android.com, Daniel Rosenberg , Paul Lawrence Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC Adds structures which will be used to inform fuse about what it is being stacked on top of. Once filters are in place, error_in will inform the post filter if the backing call returned an error. Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- include/uapi/linux/fuse.h | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index 76ee8f9e024a..0e19076729d9 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -576,6 +576,21 @@ struct fuse_entry_out { struct fuse_attr attr; }; +#define FUSE_BPF_MAX_ENTRIES 2 + +enum fuse_bpf_type { + FUSE_ENTRY_BACKING = 1, + FUSE_ENTRY_BPF = 2, + FUSE_ENTRY_REMOVE_BACKING = 3, + FUSE_ENTRY_REMOVE_BPF = 4, +}; + +struct fuse_bpf_entry_out { + uint32_t entry_type; + uint32_t unused; + uint64_t fd; +}; + struct fuse_forget_in { uint64_t nlookup; }; @@ -874,7 +889,7 @@ struct fuse_in_header { uint32_t uid; uint32_t gid; uint32_t pid; - uint32_t padding; + uint32_t error_in; }; struct fuse_out_header { From patchwork Tue Nov 22 02:15:18 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13051816 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 C56FFC433FE for ; Tue, 22 Nov 2022 02:16:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232475AbiKVCQ0 (ORCPT ); Mon, 21 Nov 2022 21:16:26 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41654 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232473AbiKVCQD (ORCPT ); Mon, 21 Nov 2022 21:16:03 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C7F63E2203 for ; Mon, 21 Nov 2022 18:16:01 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id f71-20020a25384a000000b006dd7876e98eso12449701yba.15 for ; Mon, 21 Nov 2022 18:16:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=76QMXC6mJ9CAtH5i+yrdnZDHqT+yu4res8J20D/aqg0=; b=l2D1VL2CNvcisUBCfC7TgWMOqx1HOlygw+itHxo7cwEflDooq8QM69jmRhG7gICfG1 hTRGKLnw8K5QuEa9EZLI8ENeRVJuE5SOMmwyPEJjhVZy0H050FJFI8bz4Vc6mrIwQZiN IHCMrogofo5yeziIRnPzb2b9NCBrL1dKOr1Rsx6IvGn9BCrV+CvaI40xmoq/Pkys4N5M T3A4myoeL008QjavUGxH+DQmWe0rU2ZlIK4qlzvsHCPG7wJ1NfooXYDhkk2wqyNTgLh6 h4HPd9c+S/3Z1zd8RBtIkE3BvCURRYwFwWk/vQDvb+L1wb6OV2nLlCOUJuixxEhWkpxi G6lA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=76QMXC6mJ9CAtH5i+yrdnZDHqT+yu4res8J20D/aqg0=; b=vFzjajLjcI4nZHXzmamD0QcqerAU6sMiIGOtE2KfIf83i/0RoKom9ueEVeVNl+d1sD 1vVcc0DamX4MJ0aZvTeAznVzjMXgBlZnwdxHS1c8rFOfVPVH4PSO2+QPCHGEbH7JSWjU fFBvpnWuGYMOOS5RzX2cq8ZkWmN6rOZmGP9lmz6FEmUd2r7sbO4ndJQOOHl4L8vnU+lr x1J6Ny6oH9FctfkxeDaKutu+yY2MiRJMEM3JqYZIxioDCVsyaMVBJn/S/ezktLzTwYvd mbbTcSSgOFGQYZ2hBySJt8Mo0+c2PRbZHdQOpkeXulT9DfNjwIjVWkmFeggNvo7yOXYt bdPw== X-Gm-Message-State: ANoB5pkeWoPoNgmqupzD2m5GovhXow96GQVwqktCbovxVIj4mh4B8fml K6b62tqI5rTolN4wR0tSadvH0+h0eGI= X-Google-Smtp-Source: AA0mqf55G/Y8Pk5Zs24pQf5k4rb24e2BBoc8iasqg8lF/QrPjpTa9SNSGo6ZuBmA0Asti95EjcZJANNWXhQ= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:200:8539:aadd:13be:6e82]) (user=drosen job=sendgmr) by 2002:a25:1c07:0:b0:6dd:fed7:8128 with SMTP id c7-20020a251c07000000b006ddfed78128mr1729703ybc.131.1669083360508; Mon, 21 Nov 2022 18:16:00 -0800 (PST) Date: Mon, 21 Nov 2022 18:15:18 -0800 In-Reply-To: <20221122021536.1629178-1-drosen@google.com> Mime-Version: 1.0 References: <20221122021536.1629178-1-drosen@google.com> X-Mailer: git-send-email 2.38.1.584.g0f3c55d4c2-goog Message-ID: <20221122021536.1629178-4-drosen@google.com> Subject: [RFC PATCH v2 03/21] fuse-bpf: Prepare for fuse-bpf patch From: Daniel Rosenberg To: Miklos Szeredi Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, bpf@vger.kernel.org, kernel-team@android.com, Daniel Rosenberg , Paul Lawrence Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC This moves some functions and structs around to make the following patch easier to read. Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/dir.c | 30 ------------------------------ fs/fuse/fuse_i.h | 35 +++++++++++++++++++++++++++++++++++ fs/fuse/inode.c | 44 ++++++++++++++++++++++---------------------- 3 files changed, 57 insertions(+), 52 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index bb97a384dc5d..168903cadb54 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -46,10 +46,6 @@ static inline u64 fuse_dentry_time(const struct dentry *entry) } #else -union fuse_dentry { - u64 time; - struct rcu_head rcu; -}; static inline void __fuse_dentry_settime(struct dentry *dentry, u64 time) { @@ -83,27 +79,6 @@ static void fuse_dentry_settime(struct dentry *dentry, u64 time) __fuse_dentry_settime(dentry, time); } -/* - * FUSE caches dentries and attributes with separate timeout. The - * time in jiffies until the dentry/attributes are valid is stored in - * dentry->d_fsdata and fuse_inode->i_time respectively. - */ - -/* - * Calculate the time in jiffies until a dentry/attributes are valid - */ -static u64 time_to_jiffies(u64 sec, u32 nsec) -{ - if (sec || nsec) { - struct timespec64 ts = { - sec, - min_t(u32, nsec, NSEC_PER_SEC - 1) - }; - - return get_jiffies_64() + timespec64_to_jiffies(&ts); - } else - return 0; -} /* * Set dentry and possibly attribute timeouts from the lookup/mk* @@ -115,11 +90,6 @@ void fuse_change_entry_timeout(struct dentry *entry, struct fuse_entry_out *o) time_to_jiffies(o->entry_valid, o->entry_valid_nsec)); } -static u64 attr_timeout(struct fuse_attr_out *o) -{ - return time_to_jiffies(o->attr_valid, o->attr_valid_nsec); -} - u64 entry_attr_timeout(struct fuse_entry_out *o) { return time_to_jiffies(o->attr_valid, o->attr_valid_nsec); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 98a9cf531873..57453296e662 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -63,6 +63,14 @@ struct fuse_forget_link { struct fuse_forget_link *next; }; +/** FUSE specific dentry data */ +#if BITS_PER_LONG < 64 +union fuse_dentry { + u64 time; + struct rcu_head rcu; +}; +#endif + /** FUSE inode */ struct fuse_inode { /** Inode data */ @@ -1319,4 +1327,31 @@ struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid, void fuse_file_release(struct inode *inode, struct fuse_file *ff, unsigned int open_flags, fl_owner_t id, bool isdir); +/* + * FUSE caches dentries and attributes with separate timeout. The + * time in jiffies until the dentry/attributes are valid is stored in + * dentry->d_fsdata and fuse_inode->i_time respectively. + */ + +/* + * Calculate the time in jiffies until a dentry/attributes are valid + */ +static inline u64 time_to_jiffies(u64 sec, u32 nsec) +{ + if (sec || nsec) { + struct timespec64 ts = { + sec, + min_t(u32, nsec, NSEC_PER_SEC - 1) + }; + + return get_jiffies_64() + timespec64_to_jiffies(&ts); + } else + return 0; +} + +static inline u64 attr_timeout(struct fuse_attr_out *o) +{ + return time_to_jiffies(o->attr_valid, o->attr_valid_nsec); +} + #endif /* _FS_FUSE_I_H */ diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 6b3beda16c1b..504336d56a7f 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -162,6 +162,28 @@ static ino_t fuse_squash_ino(u64 ino64) return ino; } +static void fuse_fill_attr_from_inode(struct fuse_attr *attr, + const struct fuse_inode *fi) +{ + *attr = (struct fuse_attr){ + .ino = fi->inode.i_ino, + .size = fi->inode.i_size, + .blocks = fi->inode.i_blocks, + .atime = fi->inode.i_atime.tv_sec, + .mtime = fi->inode.i_mtime.tv_sec, + .ctime = fi->inode.i_ctime.tv_sec, + .atimensec = fi->inode.i_atime.tv_nsec, + .mtimensec = fi->inode.i_mtime.tv_nsec, + .ctimensec = fi->inode.i_ctime.tv_nsec, + .mode = fi->inode.i_mode, + .nlink = fi->inode.i_nlink, + .uid = fi->inode.i_uid.val, + .gid = fi->inode.i_gid.val, + .rdev = fi->inode.i_rdev, + .blksize = 1u << fi->inode.i_blkbits, + }; +} + void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, u64 attr_valid, u32 cache_mask) { @@ -1386,28 +1408,6 @@ void fuse_dev_free(struct fuse_dev *fud) } EXPORT_SYMBOL_GPL(fuse_dev_free); -static void fuse_fill_attr_from_inode(struct fuse_attr *attr, - const struct fuse_inode *fi) -{ - *attr = (struct fuse_attr){ - .ino = fi->inode.i_ino, - .size = fi->inode.i_size, - .blocks = fi->inode.i_blocks, - .atime = fi->inode.i_atime.tv_sec, - .mtime = fi->inode.i_mtime.tv_sec, - .ctime = fi->inode.i_ctime.tv_sec, - .atimensec = fi->inode.i_atime.tv_nsec, - .mtimensec = fi->inode.i_mtime.tv_nsec, - .ctimensec = fi->inode.i_ctime.tv_nsec, - .mode = fi->inode.i_mode, - .nlink = fi->inode.i_nlink, - .uid = fi->inode.i_uid.val, - .gid = fi->inode.i_gid.val, - .rdev = fi->inode.i_rdev, - .blksize = 1u << fi->inode.i_blkbits, - }; -} - static void fuse_sb_defaults(struct super_block *sb) { sb->s_magic = FUSE_SUPER_MAGIC; From patchwork Tue Nov 22 02:15:19 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13051817 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 8FB2DC4332F for ; Tue, 22 Nov 2022 02:16:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232545AbiKVCQe (ORCPT ); Mon, 21 Nov 2022 21:16:34 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42354 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232288AbiKVCQ0 (ORCPT ); Mon, 21 Nov 2022 21:16:26 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 14A7DE3D13 for ; Mon, 21 Nov 2022 18:16:04 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id f71-20020a25384a000000b006dd7876e98eso12449770yba.15 for ; Mon, 21 Nov 2022 18:16:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=9UWu0g2nySQwX4/9jlio/nP2cnjQwiiQ4/U1fMJ0ICI=; b=eSiZHiU5wNVl1snjTpE+NZ07db+97rzIZjpcXnkKAGctoRyTIv0dHUQKvs2wQJiHfP Km4ftAFV9YtwF4+WPgEEiTM4PGAC8VZVyFhWzL33VmKpPiu3zTgVa9CvCCdziDA1T0f/ HO1H1uMWxm9MvA+Q0pW7fXVmH3+r0wzGN2XHKRvDX6EVaTthtZD1WE8YpGfaiDU5SuOG yTSBX3P5geOrmw7nshHDuUJpRemavX1Mt5mxUXIMp3ln8sOtQC4wdWsXMxrvuR7ujZfy V+In2Held2pdZ6osqv/RN4Ul/OE/NrocsiePU1HzFOHvo6QQ00wd7Z8aD+tsGQdshvww /n7w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=9UWu0g2nySQwX4/9jlio/nP2cnjQwiiQ4/U1fMJ0ICI=; b=N+3B1wfTnImBhSdnnZOHxBooVJi+mhanPBOcQYQdZfO3DsklN4bNC1Uz+AQbe7aUWN 7SCLLJrM4WQzZN3Qq7JtjkKlnCt3ymYJmA/RwnqPWYrfV0JngnhFjL2n8y7G49yr5kud 8zSslE15wbTpolv1LLFAnUlUvJxkymZ0t/JP7YaIaxb6r31psiCTcpDUnsztQHRDe6FU fy5OigEifUqfjzzkV4+QOZtlmUVQYWfaZXaYAVte5NDMzWiaBm3/zSBuh5K6a36+hRjP Uy3yL+5eyglYOH2YCM1bRSMWbM+Yielud7Xp+DNy+BierKl0OMVWyW51xxFLbPV/qlqY tzQw== X-Gm-Message-State: ANoB5pk+VAtGh7WVaDOBH21OqTqiSKsMkd6WgXEsl+ohYd4UhDpTfeSG 0iM5Rt5Nsu5h+L2aUeswEmVcT1+MvoI= X-Google-Smtp-Source: AA0mqf5HiKz1HVnR4mQz9iBZ+jwuLWfTuW/sF/6X20NwWJ0UA67BH1q41cHRperXXOCDD0SSfWsx4zg82oc= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:200:8539:aadd:13be:6e82]) (user=drosen job=sendgmr) by 2002:a81:920c:0:b0:3a7:c0d4:8c6b with SMTP id j12-20020a81920c000000b003a7c0d48c6bmr0ywg.377.1669083363364; Mon, 21 Nov 2022 18:16:03 -0800 (PST) Date: Mon, 21 Nov 2022 18:15:19 -0800 In-Reply-To: <20221122021536.1629178-1-drosen@google.com> Mime-Version: 1.0 References: <20221122021536.1629178-1-drosen@google.com> X-Mailer: git-send-email 2.38.1.584.g0f3c55d4c2-goog Message-ID: <20221122021536.1629178-5-drosen@google.com> Subject: [RFC PATCH v2 04/21] fuse: Add fuse-bpf, a stacked fs extension for FUSE From: Daniel Rosenberg To: Miklos Szeredi Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, bpf@vger.kernel.org, kernel-team@android.com, Daniel Rosenberg , Paul Lawrence , Alessio Balsini Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC Fuse-bpf provides a short circuit path for Fuse implementations that act as a stacked filesystem. For cases that are directly unchanged, operations are passed directly to the backing filesystem. Small adjustments can be handled by bpf prefilters or postfilters, with the option to fall back to userspace as needed. Fuse implementations may supply backing node information, as well as bpf programs via an optional add on to the lookup structure. This has been split over the next set of patches for readability. Clusters of fuse ops have been split into their own patches, as well as the actual bpf calls and userspace calls for filters. Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence Signed-off-by: Alessio Balsini --- fs/fuse/Kconfig | 8 + fs/fuse/Makefile | 1 + fs/fuse/backing.c | 392 ++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/dev.c | 41 ++++- fs/fuse/dir.c | 187 +++++++++++++++++----- fs/fuse/file.c | 25 ++- fs/fuse/fuse_i.h | 99 +++++++++++- fs/fuse/inode.c | 189 +++++++++++++++++----- fs/fuse/ioctl.c | 2 +- 9 files changed, 861 insertions(+), 83 deletions(-) create mode 100644 fs/fuse/backing.c diff --git a/fs/fuse/Kconfig b/fs/fuse/Kconfig index 038ed0b9aaa5..3a64fa73e591 100644 --- a/fs/fuse/Kconfig +++ b/fs/fuse/Kconfig @@ -52,3 +52,11 @@ config FUSE_DAX If you want to allow mounting a Virtio Filesystem with the "dax" option, answer Y. + +config FUSE_BPF + bool "Adds BPF to fuse" + depends on FUSE_FS + depends on BPF + help + Extends FUSE by adding BPF to prefilter calls and potentially pass to a + backing file system diff --git a/fs/fuse/Makefile b/fs/fuse/Makefile index 0c48b35c058d..a0853c439db2 100644 --- a/fs/fuse/Makefile +++ b/fs/fuse/Makefile @@ -9,5 +9,6 @@ obj-$(CONFIG_VIRTIO_FS) += virtiofs.o fuse-y := dev.o dir.o file.o inode.o control.o xattr.o acl.o readdir.o ioctl.o fuse-$(CONFIG_FUSE_DAX) += dax.o +fuse-$(CONFIG_FUSE_BPF) += backing.o virtiofs-y := virtio_fs.o diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c new file mode 100644 index 000000000000..5a59a8963d52 --- /dev/null +++ b/fs/fuse/backing.c @@ -0,0 +1,392 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * FUSE-BPF: Filesystem in Userspace with BPF + * Copyright (c) 2021 Google LLC + */ + +#include "fuse_i.h" + +#include +#include +#include +#include + +/* + * expression statement to wrap the backing filter logic + * struct inode *inode: inode with bpf and backing inode + * typedef io: (typically complex) type whose components fuse_args can point to. + * An instance of this type is created locally and passed to initialize + * void initialize_in(struct fuse_args *fa, io *in_out, args...): function that sets + * up fa and io based on args + * void initialize_out(struct fuse_args *fa, io *in_out, args...): function that sets + * up fa and io based on args + * int backing(struct fuse_bpf_args_internal *fa, args...): function that actually performs + * the backing io operation + * void *finalize(struct fuse_bpf_args *, args...): function that performs any final + * work needed to commit the backing io + */ +#define fuse_bpf_backing(inode, io, out, initialize_in, initialize_out, \ + backing, finalize, args...) \ +({ \ + struct fuse_inode *fuse_inode = get_fuse_inode(inode); \ + struct fuse_args fa = { 0 }; \ + bool initialized = false; \ + bool handled = false; \ + ssize_t res; \ + io feo = { 0 }; \ + int error = 0; \ + \ + do { \ + if (!fuse_inode || !fuse_inode->backing_inode) \ + break; \ + \ + handled = true; \ + error = initialize_in(&fa, &feo, args); \ + if (error) \ + break; \ + \ + error = initialize_out(&fa, &feo, args); \ + if (error) \ + break; \ + \ + initialized = true; \ + \ + error = backing(&fa, out, args); \ + if (error < 0) \ + fa.error_in = error; \ + \ + } while (false); \ + \ + if (initialized && handled) { \ + res = finalize(&fa, out, args); \ + if (res) \ + error = res; \ + } \ + \ + *out = error ? _Generic((*out), \ + default : \ + error, \ + struct dentry * : \ + ERR_PTR(error), \ + const char * : \ + ERR_PTR(error) \ + ) : (*out); \ + handled; \ +}) + +static void fuse_get_backing_path(struct file *file, struct path *path) +{ + path_get(&file->f_path); + *path = file->f_path; +} + +static bool has_file(int type) +{ + return type == FUSE_ENTRY_BACKING; +} + +/* + * The optional fuse bpf entry lists the backing file for a particular + * lookup. These are inherited by default. + * + * In the future, we may support multiple bpfs, and multiple backing files for + * the bpf to choose between. + * + * Currently, the expected format is possibly a bpf program, then the backing + * file. Changing only the bpf is valid, though meaningless if there isn't an + * inherited backing file. + * + * Support for the bpf program will be added in a later patch + * + */ +int parse_fuse_bpf_entry(struct fuse_bpf_entry *fbe, int num) +{ + struct fuse_bpf_entry_out *fbeo; + struct file *file; + bool has_backing = false; + int num_entries; + int err = -EINVAL; + int i; + + if (num > 0) + num_entries = num; + else + num_entries = FUSE_BPF_MAX_ENTRIES; + + for (i = 0; i < num_entries; i++) { + file = NULL; + fbeo = &fbe->out[i]; + + /* reserved for future use */ + if (fbeo->unused != 0) + goto out_err; + + if (has_file(fbeo->entry_type)) { + file = fget(fbeo->fd); + if (!file) { + err = -EBADF; + goto out_err; + } + } + + switch (fbeo->entry_type) { + case 0: + if (num == -1) + num_entries = i; + else + goto out_err; + break; + case FUSE_ENTRY_REMOVE_BACKING: + if (fbe->backing_action) + goto out_err; + fbe->backing_action = FUSE_BPF_REMOVE; + break; + case FUSE_ENTRY_BACKING: + if (fbe->backing_action) + goto out_err; + fuse_get_backing_path(file, &fbe->backing_path); + fbe->backing_action = FUSE_BPF_SET; + has_backing = true; + break; + default: + err = -EINVAL; + goto out_err; + } + if (has_file(fbeo->entry_type)) { + fput(file); + file = NULL; + } + } + + fbe->is_used = num_entries > 0; + + return 0; +out_err: + if (file) + fput(file); + if (has_backing) + path_put_init(&fbe->backing_path); + return err; +} + +static void fuse_stat_to_attr(struct fuse_conn *fc, struct inode *inode, + struct kstat *stat, struct fuse_attr *attr) +{ + unsigned int blkbits; + + /* see the comment in fuse_change_attributes() */ + if (fc->writeback_cache && S_ISREG(inode->i_mode)) { + stat->size = i_size_read(inode); + stat->mtime.tv_sec = inode->i_mtime.tv_sec; + stat->mtime.tv_nsec = inode->i_mtime.tv_nsec; + stat->ctime.tv_sec = inode->i_ctime.tv_sec; + stat->ctime.tv_nsec = inode->i_ctime.tv_nsec; + } + + attr->ino = stat->ino; + attr->mode = (inode->i_mode & S_IFMT) | (stat->mode & 07777); + attr->nlink = stat->nlink; + attr->uid = from_kuid(fc->user_ns, stat->uid); + attr->gid = from_kgid(fc->user_ns, stat->gid); + attr->atime = stat->atime.tv_sec; + attr->atimensec = stat->atime.tv_nsec; + attr->mtime = stat->mtime.tv_sec; + attr->mtimensec = stat->mtime.tv_nsec; + attr->ctime = stat->ctime.tv_sec; + attr->ctimensec = stat->ctime.tv_nsec; + attr->size = stat->size; + attr->blocks = stat->blocks; + + if (stat->blksize != 0) + blkbits = ilog2(stat->blksize); + else + blkbits = inode->i_sb->s_blocksize_bits; + + attr->blksize = 1 << blkbits; +} + +/******************************************************************************* + * Directory operations after here * + ******************************************************************************/ + +struct fuse_lookup_io { + struct fuse_entry_out feo; + struct fuse_bpf_entry fbe; +}; + +static int fuse_lookup_initialize_in(struct fuse_args *fa, struct fuse_lookup_io *fli, + struct inode *dir, struct dentry *entry, unsigned int flags) +{ + *fa = (struct fuse_args) { + .nodeid = get_fuse_inode(dir)->nodeid, + .opcode = FUSE_LOOKUP, + .in_numargs = 1, + .in_args[0] = (struct fuse_in_arg) { + .size = entry->d_name.len + 1, + .value = (void *) entry->d_name.name, + }, + }; + + return 0; +} + +static int fuse_lookup_initialize_out(struct fuse_args *fa, struct fuse_lookup_io *fli, + struct inode *dir, struct dentry *entry, unsigned int flags) +{ + fa->out_numargs = 2; + fa->out_argvar = true; + fa->is_lookup = true; + fa->out_args[0] = (struct fuse_arg) { + .size = sizeof(fli->feo), + .value = &fli->feo, + }; + fa->out_args[1] = (struct fuse_arg) { + .size = sizeof(fli->fbe.out), + .value = fli->fbe.out, + }; + + return 0; +} + +static int fuse_lookup_backing(struct fuse_args *fa, struct dentry **out, struct inode *dir, + struct dentry *entry, unsigned int flags) +{ + struct fuse_dentry *fuse_entry = get_fuse_dentry(entry); + struct fuse_dentry *dir_fuse_entry = get_fuse_dentry(entry->d_parent); + struct dentry *dir_backing_entry = dir_fuse_entry->backing_path.dentry; + struct inode *dir_backing_inode = dir_backing_entry->d_inode; + struct fuse_entry_out *feo = (void *)fa->out_args[0].value; + struct dentry *backing_entry; + const char *name; + struct kstat stat; + int len; + int err; + + /* TODO this will not handle lookups over mount points */ + inode_lock_nested(dir_backing_inode, I_MUTEX_PARENT); + name = entry->d_name.name; + len = entry->d_name.len; + + backing_entry = lookup_one_len(name, dir_backing_entry, len); + inode_unlock(dir_backing_inode); + + if (IS_ERR(backing_entry)) + return PTR_ERR(backing_entry); + + fuse_entry->backing_path = (struct path) { + .dentry = backing_entry, + .mnt = mntget(dir_fuse_entry->backing_path.mnt), + }; + + if (d_is_negative(backing_entry)) + return 0; + + err = vfs_getattr(&fuse_entry->backing_path, &stat, + STATX_BASIC_STATS, 0); + if (err) { + path_put_init(&fuse_entry->backing_path); + return err; + } + + fuse_stat_to_attr(get_fuse_conn(dir), + backing_entry->d_inode, &stat, &feo->attr); + return 0; +} + +int fuse_handle_backing(struct fuse_bpf_entry *fbe, struct path *backing_path) +{ + switch (fbe->backing_action) { + case FUSE_BPF_UNCHANGED: + /* backing inode/path are added in fuse_lookup_backing */ + break; + + case FUSE_BPF_REMOVE: + path_put_init(backing_path); + break; + + case FUSE_BPF_SET: { + if (!fbe->backing_path.dentry) + return -EINVAL; + + path_put(backing_path); + *backing_path = fbe->backing_path; + fbe->backing_path.dentry = NULL; + fbe->backing_path.mnt = NULL; + + break; + } + + default: + return -EINVAL; + } + + return 0; +} + +static int fuse_lookup_finalize(struct fuse_args *fa, struct dentry **out, + struct inode *dir, struct dentry *entry, unsigned int flags) +{ + struct fuse_dentry *fd; + struct dentry *backing_dentry; + struct inode *inode, *backing_inode; + struct inode *d_inode = entry->d_inode; + struct fuse_entry_out *feo = fa->out_args[0].value; + struct fuse_bpf_entry_out *febo = fa->out_args[1].value; + struct fuse_bpf_entry *fbe = container_of(febo, struct fuse_bpf_entry, out[0]); + int error = -1; + u64 target_nodeid = 0; + + parse_fuse_bpf_entry(fbe, -1); + fd = get_fuse_dentry(entry); + if (!fd) + return -EIO; + error = fuse_handle_backing(fbe, &fd->backing_path); + if (error) + return error; + backing_dentry = fd->backing_path.dentry; + if (!backing_dentry) + return -ENOENT; + backing_inode = backing_dentry->d_inode; + if (!backing_inode) { + *out = 0; + return 0; + } + + if (d_inode) + target_nodeid = get_fuse_inode(d_inode)->nodeid; + + inode = fuse_iget_backing(dir->i_sb, target_nodeid, backing_inode); + + if (IS_ERR(inode)) + return PTR_ERR(inode); + + get_fuse_inode(inode)->nodeid = feo->nodeid; + + *out = d_splice_alias(inode, entry); + return 0; +} + +int fuse_bpf_lookup(struct dentry **out, struct inode *dir, struct dentry *entry, unsigned int flags) +{ + return fuse_bpf_backing(dir, struct fuse_lookup_io, out, + fuse_lookup_initialize_in, fuse_lookup_initialize_out, + fuse_lookup_backing, fuse_lookup_finalize, + dir, entry, flags); +} + +int fuse_revalidate_backing(struct dentry *entry, unsigned int flags) +{ + struct fuse_dentry *fuse_dentry = get_fuse_dentry(entry); + struct dentry *backing_entry = fuse_dentry->backing_path.dentry; + + spin_lock(&backing_entry->d_lock); + if (d_unhashed(backing_entry)) { + spin_unlock(&backing_entry->d_lock); + return 0; + } + spin_unlock(&backing_entry->d_lock); + + if (unlikely(backing_entry->d_flags & DCACHE_OP_REVALIDATE)) + return backing_entry->d_op->d_revalidate(backing_entry, flags); + return 1; +} diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index b4a6e0a1b945..79d2fb6adc83 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -238,6 +238,11 @@ void fuse_queue_forget(struct fuse_conn *fc, struct fuse_forget_link *forget, { struct fuse_iqueue *fiq = &fc->iq; + if (nodeid == 0) { + kfree(forget); + return; + } + forget->forget_one.nodeid = nodeid; forget->forget_one.nlookup = nlookup; @@ -1006,10 +1011,38 @@ static int fuse_copy_one(struct fuse_copy_state *cs, void *val, unsigned size) return 0; } +/* Copy the fuse-bpf lookup args and verify them */ +#ifdef CONFIG_FUSE_BPF +static int fuse_copy_lookup(struct fuse_copy_state *cs, void *val, unsigned size) +{ + struct fuse_bpf_entry_out *fbeo = (struct fuse_bpf_entry_out *)val; + struct fuse_bpf_entry *feb = container_of(fbeo, struct fuse_bpf_entry, out[0]); + int num_entries = size / sizeof(*fbeo); + int err; + + if (size && size % sizeof(*fbeo) != 0) + return -EINVAL; + + if (num_entries > FUSE_BPF_MAX_ENTRIES) + return -EINVAL; + err = fuse_copy_one(cs, val, size); + if (err) + return err; + if (size) + err = parse_fuse_bpf_entry(feb, num_entries); + return err; +} +#else +static int fuse_copy_lookup(struct fuse_copy_state *cs, void *val, unsigned size) +{ + return fuse_copy_one(cs, val, size); +} +#endif + /* Copy request arguments to/from userspace buffer */ static int fuse_copy_args(struct fuse_copy_state *cs, unsigned numargs, unsigned argpages, struct fuse_arg *args, - int zeroing) + int zeroing, unsigned is_lookup) { int err = 0; unsigned i; @@ -1018,6 +1051,8 @@ static int fuse_copy_args(struct fuse_copy_state *cs, unsigned numargs, struct fuse_arg *arg = &args[i]; if (i == numargs - 1 && argpages) err = fuse_copy_pages(cs, arg->size, zeroing); + else if (i == numargs - 1 && is_lookup) + err = fuse_copy_lookup(cs, arg->value, arg->size); else err = fuse_copy_one(cs, arg->value, arg->size); } @@ -1295,7 +1330,7 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file, err = fuse_copy_one(cs, &req->in.h, sizeof(req->in.h)); if (!err) err = fuse_copy_args(cs, args->in_numargs, args->in_pages, - (struct fuse_arg *) args->in_args, 0); + (struct fuse_arg *) args->in_args, 0, 0); fuse_copy_finish(cs); spin_lock(&fpq->lock); clear_bit(FR_LOCKED, &req->flags); @@ -1834,7 +1869,7 @@ static int copy_out_args(struct fuse_copy_state *cs, struct fuse_args *args, lastarg->size -= diffsize; } return fuse_copy_args(cs, args->out_numargs, args->out_pages, - args->out_args, args->page_zeroing); + args->out_args, args->page_zeroing, args->is_lookup); } /* diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 168903cadb54..fb7c6988f0d9 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -34,7 +34,7 @@ static void fuse_advise_use_readdirplus(struct inode *dir) set_bit(FUSE_I_ADVISE_RDPLUS, &fi->state); } -#if BITS_PER_LONG >= 64 +#if BITS_PER_LONG >= 64 && !defined(CONFIG_FUSE_BPF) static inline void __fuse_dentry_settime(struct dentry *entry, u64 time) { entry->d_fsdata = (void *) time; @@ -49,12 +49,12 @@ static inline u64 fuse_dentry_time(const struct dentry *entry) static inline void __fuse_dentry_settime(struct dentry *dentry, u64 time) { - ((union fuse_dentry *) dentry->d_fsdata)->time = time; + ((struct fuse_dentry *) dentry->d_fsdata)->time = time; } static inline u64 fuse_dentry_time(const struct dentry *entry) { - return ((union fuse_dentry *) entry->d_fsdata)->time; + return ((struct fuse_dentry *) entry->d_fsdata)->time; } #endif @@ -79,6 +79,17 @@ static void fuse_dentry_settime(struct dentry *dentry, u64 time) __fuse_dentry_settime(dentry, time); } +void fuse_init_dentry_root(struct dentry *root, struct file *backing_dir) +{ +#ifdef CONFIG_FUSE_BPF + struct fuse_dentry *fuse_dentry = root->d_fsdata; + + if (backing_dir) { + fuse_dentry->backing_path = backing_dir->f_path; + path_get(&fuse_dentry->backing_path); + } +#endif +} /* * Set dentry and possibly attribute timeouts from the lookup/mk* @@ -150,7 +161,8 @@ static void fuse_invalidate_entry(struct dentry *entry) static void fuse_lookup_init(struct fuse_conn *fc, struct fuse_args *args, u64 nodeid, const struct qstr *name, - struct fuse_entry_out *outarg) + struct fuse_entry_out *outarg, + struct fuse_bpf_entry_out *bpf_outarg) { memset(outarg, 0, sizeof(struct fuse_entry_out)); args->opcode = FUSE_LOOKUP; @@ -158,10 +170,43 @@ static void fuse_lookup_init(struct fuse_conn *fc, struct fuse_args *args, args->in_numargs = 1; args->in_args[0].size = name->len + 1; args->in_args[0].value = name->name; - args->out_numargs = 1; + args->out_argvar = true; + args->out_numargs = 2; args->out_args[0].size = sizeof(struct fuse_entry_out); args->out_args[0].value = outarg; + args->out_args[1].size = sizeof(struct fuse_bpf_entry_out) * FUSE_BPF_MAX_ENTRIES; + args->out_args[1].value = bpf_outarg; + args->is_lookup = 1; +} + +#ifdef CONFIG_FUSE_BPF +static bool backing_data_changed(struct fuse_inode *fi, struct dentry *entry, + struct fuse_bpf_entry *bpf_arg) +{ + struct path new_backing_path; + struct inode *new_backing_inode; + int err; + bool ret = true; + + if (!entry) + return false; + + get_fuse_backing_path(entry, &new_backing_path); + + err = fuse_handle_backing(bpf_arg, &new_backing_path); + new_backing_inode = d_inode(new_backing_path.dentry); + + if (err) + goto put_inode; + + ret = (fi->backing_inode != new_backing_inode || + !path_equal(&get_fuse_dentry(entry)->backing_path, &new_backing_path)); + +put_inode: + path_put(&new_backing_path); + return ret; } +#endif /* * Check whether the dentry is still valid @@ -183,9 +228,23 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) inode = d_inode_rcu(entry); if (inode && fuse_is_bad(inode)) goto invalid; - else if (time_before64(fuse_dentry_time(entry), get_jiffies_64()) || + +#ifdef CONFIG_FUSE_BPF + /* TODO: Do we need bpf support for revalidate? + * If the lower filesystem says the entry is invalid, FUSE probably shouldn't + * try to fix that without going through the normal lookup path... + */ + if (get_fuse_dentry(entry)->backing_path.dentry) { + ret = fuse_revalidate_backing(entry, flags); + if (ret <= 0) { + goto out; + } + } +#endif + if (time_before64(fuse_dentry_time(entry), get_jiffies_64()) || (flags & (LOOKUP_EXCL | LOOKUP_REVAL))) { struct fuse_entry_out outarg; + struct fuse_bpf_entry bpf_arg; FUSE_ARGS(args); struct fuse_forget_link *forget; u64 attr_version; @@ -197,27 +256,44 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) ret = -ECHILD; if (flags & LOOKUP_RCU) goto out; - fm = get_fuse_mount(inode); + parent = dget_parent(entry); + +#ifdef CONFIG_FUSE_BPF + /* TODO: Once we're handling timeouts for backing inodes, do a + * bpf based lookup_revalidate here. + */ + if (get_fuse_inode(parent->d_inode)->backing_inode) { + dput(parent); + ret = 1; + goto out; + } +#endif forget = fuse_alloc_forget(); ret = -ENOMEM; - if (!forget) + if (!forget) { + dput(parent); goto out; + } attr_version = fuse_get_attr_version(fm->fc); - parent = dget_parent(entry); fuse_lookup_init(fm->fc, &args, get_node_id(d_inode(parent)), - &entry->d_name, &outarg); + &entry->d_name, &outarg, bpf_arg.out); ret = fuse_simple_request(fm, &args); dput(parent); + /* Zero nodeid is same as -ENOENT */ if (!ret && !outarg.nodeid) ret = -ENOENT; - if (!ret) { + if (!ret || bpf_arg.is_used) { fi = get_fuse_inode(inode); if (outarg.nodeid != get_node_id(inode) || +#ifdef CONFIG_FUSE_BPF + (bpf_arg.is_used && + backing_data_changed(fi, entry, &bpf_arg)) || +#endif (bool) IS_AUTOMOUNT(inode) != (bool) (outarg.attr.flags & FUSE_ATTR_SUBMOUNT)) { fuse_queue_forget(fm->fc, forget, outarg.nodeid, 1); @@ -259,17 +335,20 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) goto out; } -#if BITS_PER_LONG < 64 +#if BITS_PER_LONG < 64 || defined(CONFIG_FUSE_BPF) static int fuse_dentry_init(struct dentry *dentry) { - dentry->d_fsdata = kzalloc(sizeof(union fuse_dentry), + dentry->d_fsdata = kzalloc(sizeof(struct fuse_dentry), GFP_KERNEL_ACCOUNT | __GFP_RECLAIMABLE); return dentry->d_fsdata ? 0 : -ENOMEM; } static void fuse_dentry_release(struct dentry *dentry) { - union fuse_dentry *fd = dentry->d_fsdata; + struct fuse_dentry *fd = dentry->d_fsdata; + + if (fd && fd->backing_path.dentry) + path_put(&fd->backing_path); kfree_rcu(fd, rcu); } @@ -310,7 +389,7 @@ static struct vfsmount *fuse_dentry_automount(struct path *path) const struct dentry_operations fuse_dentry_operations = { .d_revalidate = fuse_dentry_revalidate, .d_delete = fuse_dentry_delete, -#if BITS_PER_LONG < 64 +#if BITS_PER_LONG < 64 || defined(CONFIG_FUSE_BPF) .d_init = fuse_dentry_init, .d_release = fuse_dentry_release, #endif @@ -318,7 +397,7 @@ const struct dentry_operations fuse_dentry_operations = { }; const struct dentry_operations fuse_root_dentry_operations = { -#if BITS_PER_LONG < 64 +#if BITS_PER_LONG < 64 || defined(CONFIG_FUSE_BPF) .d_init = fuse_dentry_init, .d_release = fuse_dentry_release, #endif @@ -336,11 +415,13 @@ bool fuse_invalid_attr(struct fuse_attr *attr) attr->size > LLONG_MAX; } -int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name, - struct fuse_entry_out *outarg, struct inode **inode) +int fuse_lookup_name(struct super_block *sb, u64 nodeid, + const struct qstr *name, struct fuse_entry_out *outarg, + struct dentry *entry, struct inode **inode) { struct fuse_mount *fm = get_fuse_mount_super(sb); FUSE_ARGS(args); + struct fuse_bpf_entry bpf_arg = { 0 }; struct fuse_forget_link *forget; u64 attr_version; int err; @@ -358,23 +439,56 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name attr_version = fuse_get_attr_version(fm->fc); - fuse_lookup_init(fm->fc, &args, nodeid, name, outarg); + fuse_lookup_init(fm->fc, &args, nodeid, name, outarg, bpf_arg.out); err = fuse_simple_request(fm, &args); - /* Zero nodeid is same as -ENOENT, but with valid timeout */ - if (err || !outarg->nodeid) - goto out_put_forget; - err = -EIO; - if (!outarg->nodeid) - goto out_put_forget; - if (fuse_invalid_attr(&outarg->attr)) - goto out_put_forget; - - *inode = fuse_iget(sb, outarg->nodeid, outarg->generation, - &outarg->attr, entry_attr_timeout(outarg), - attr_version); +#ifdef CONFIG_FUSE_BPF + if (bpf_arg.is_used) { + /* TODO Make sure this handles invalid handles */ + struct path *backing_path; + struct inode *backing_inode; + + err = -ENOENT; + if (!entry) + goto out_queue_forget; + + err = -EINVAL; + backing_path = &bpf_arg.backing_path; + if (!backing_path->dentry) + goto out_queue_forget; + + err = fuse_handle_backing(&bpf_arg, + &get_fuse_dentry(entry)->backing_path); + if (err) + goto out_queue_forget; + + backing_inode = d_inode(get_fuse_dentry(entry)->backing_path.dentry); + *inode = fuse_iget_backing(sb, outarg->nodeid, backing_inode); + if (!*inode) + goto out_queue_forget; + } else +#endif + { + /* Zero nodeid is same as -ENOENT, but with valid timeout */ + if (err || !outarg->nodeid) + goto out_put_forget; + + err = -EIO; + if (!outarg->nodeid) + goto out_put_forget; + if (fuse_invalid_attr(&outarg->attr)) + goto out_put_forget; + + *inode = fuse_iget(sb, outarg->nodeid, outarg->generation, + &outarg->attr, entry_attr_timeout(outarg), + attr_version); + } + err = -ENOMEM; - if (!*inode) { +#ifdef CONFIG_FUSE_BPF +out_queue_forget: +#endif + if (!*inode && outarg->nodeid) { fuse_queue_forget(fm->fc, forget, outarg->nodeid, 1); goto out; } @@ -399,9 +513,12 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, if (fuse_is_bad(dir)) return ERR_PTR(-EIO); + if (fuse_bpf_lookup(&newent, dir, entry, flags)) + return newent; + locked = fuse_lock_inode(dir); err = fuse_lookup_name(dir->i_sb, get_node_id(dir), &entry->d_name, - &outarg, &inode); + &outarg, entry, &inode); fuse_unlock_inode(dir, locked); if (err == -ENOENT) { outarg_valid = false; @@ -1295,6 +1412,7 @@ static int fuse_permission(struct user_namespace *mnt_userns, struct fuse_conn *fc = get_fuse_conn(inode); bool refreshed = false; int err = 0; + struct fuse_inode *fi = get_fuse_inode(inode); if (fuse_is_bad(inode)) return -EIO; @@ -1307,7 +1425,6 @@ static int fuse_permission(struct user_namespace *mnt_userns, */ if (fc->default_permissions || ((mask & MAY_EXEC) && S_ISREG(inode->i_mode))) { - struct fuse_inode *fi = get_fuse_inode(inode); u32 perm_mask = STATX_MODE | STATX_UID | STATX_GID; if (perm_mask & READ_ONCE(fi->inval_mask) || @@ -1484,7 +1601,7 @@ static long fuse_dir_compat_ioctl(struct file *file, unsigned int cmd, FUSE_IOCTL_COMPAT | FUSE_IOCTL_DIR); } -static bool update_mtime(unsigned ivalid, bool trust_local_mtime) +static inline bool update_mtime(unsigned int ivalid, bool trust_local_mtime) { /* Always update if mtime is explicitly set */ if (ivalid & ATTR_MTIME_SET) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 71bfb663aac5..503327be3942 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -8,6 +8,7 @@ #include "fuse_i.h" +#include #include #include #include @@ -125,13 +126,18 @@ static void fuse_file_put(struct fuse_file *ff, bool sync, bool isdir) } struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid, - unsigned int open_flags, bool isdir) + unsigned int open_flags, bool isdir, struct file *file) { struct fuse_conn *fc = fm->fc; struct fuse_file *ff; int opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN; - ff = fuse_file_alloc(fm); + if (file && file->private_data) { + ff = file->private_data; + file->private_data = NULL; + } else { + ff = fuse_file_alloc(fm); + } if (!ff) return ERR_PTR(-ENOMEM); @@ -169,7 +175,7 @@ struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid, int fuse_do_open(struct fuse_mount *fm, u64 nodeid, struct file *file, bool isdir) { - struct fuse_file *ff = fuse_file_open(fm, nodeid, file->f_flags, isdir); + struct fuse_file *ff = fuse_file_open(fm, nodeid, file->f_flags, isdir, file); if (!IS_ERR(ff)) file->private_data = ff; @@ -1873,6 +1879,19 @@ int fuse_write_inode(struct inode *inode, struct writeback_control *wbc) */ WARN_ON(wbc->for_reclaim); + /** + * TODO - fully understand why this is necessary + * + * With fuse-bpf, fsstress fails if rename is enabled without this + * + * We are getting writes here on directory inodes, which do not have an + * initialized file list so crash. + * + * The question is why we are getting those writes + */ + if (!S_ISREG(inode->i_mode)) + return 0; + ff = __fuse_write_file_get(fi); err = fuse_flush_times(inode, ff); if (ff) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 57453296e662..d67325af5e72 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include #include @@ -31,6 +33,7 @@ #include #include #include +#include /** Default max number of pages that can be used in a single read request */ #define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32 @@ -64,11 +67,35 @@ struct fuse_forget_link { }; /** FUSE specific dentry data */ -#if BITS_PER_LONG < 64 -union fuse_dentry { - u64 time; - struct rcu_head rcu; +#if BITS_PER_LONG < 64 || defined(CONFIG_FUSE_BPF) +struct fuse_dentry { + union { + u64 time; + struct rcu_head rcu; + }; + struct path backing_path; }; + +static inline struct fuse_dentry *get_fuse_dentry(const struct dentry *entry) +{ + return entry->d_fsdata; +} +#endif + +#ifdef CONFIG_FUSE_BPF +static inline void get_fuse_backing_path(const struct dentry *d, + struct path *path) +{ + struct fuse_dentry *di = get_fuse_dentry(d); + + if (!di) { + *path = (struct path) { .mnt = 0, .dentry = 0 }; + return; + } + + *path = di->backing_path; + path_get(path); +} #endif /** FUSE inode */ @@ -76,6 +103,14 @@ struct fuse_inode { /** Inode data */ struct inode inode; +#ifdef CONFIG_FUSE_BPF + /** + * Backing inode, if this inode is from a backing file system. + * If this is set, nodeid is 0. + */ + struct inode *backing_inode; +#endif + /** Unique ID, which identifies the inode between userspace * and kernel */ u64 nodeid; @@ -226,6 +261,14 @@ struct fuse_file { } readdir; +#ifdef CONFIG_FUSE_BPF + /** + * TODO: Reconcile with passthrough file + * backing file when in bpf mode + */ + struct file *backing_file; +#endif + /** RB node to be linked on fuse_conn->polled_files */ struct rb_node polled_node; @@ -257,6 +300,7 @@ struct fuse_page_desc { struct fuse_args { uint64_t nodeid; uint32_t opcode; + uint32_t error_in; unsigned short in_numargs; unsigned short out_numargs; bool force:1; @@ -269,6 +313,7 @@ struct fuse_args { bool page_zeroing:1; bool page_replace:1; bool may_block:1; + bool is_lookup:1; struct fuse_in_arg in_args[3]; struct fuse_arg out_args[2]; void (*end)(struct fuse_mount *fm, struct fuse_args *args, int error); @@ -522,6 +567,7 @@ struct fuse_fs_context { unsigned int max_read; unsigned int blksize; const char *subtype; + struct file *root_dir; /* DAX device, may be NULL */ struct dax_device *dax_dev; @@ -965,12 +1011,16 @@ extern const struct dentry_operations fuse_root_dentry_operations; /** * Get a filled in inode */ +struct inode *fuse_iget_backing(struct super_block *sb, + u64 nodeid, + struct inode *backing_inode); struct inode *fuse_iget(struct super_block *sb, u64 nodeid, int generation, struct fuse_attr *attr, u64 attr_valid, u64 attr_version); int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name, - struct fuse_entry_out *outarg, struct inode **inode); + struct fuse_entry_out *outarg, + struct dentry *entry, struct inode **inode); /** * Send FORGET command @@ -1115,6 +1165,7 @@ void fuse_invalidate_entry_cache(struct dentry *entry); void fuse_invalidate_atime(struct inode *inode); u64 entry_attr_timeout(struct fuse_entry_out *o); +void fuse_init_dentry_root(struct dentry *root, struct file *backing_dir); void fuse_change_entry_timeout(struct dentry *entry, struct fuse_entry_out *o); /** @@ -1323,10 +1374,46 @@ int fuse_fileattr_set(struct user_namespace *mnt_userns, /* file.c */ struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid, - unsigned int open_flags, bool isdir); + unsigned int open_flags, bool isdir, + struct file *file); void fuse_file_release(struct inode *inode, struct fuse_file *ff, unsigned int open_flags, fl_owner_t id, bool isdir); +/* backing.c */ + +enum fuse_bpf_set { + FUSE_BPF_UNCHANGED = 0, + FUSE_BPF_SET, + FUSE_BPF_REMOVE, +}; + +struct fuse_bpf_entry { + struct fuse_bpf_entry_out out[FUSE_BPF_MAX_ENTRIES]; + + enum fuse_bpf_set backing_action; + struct path backing_path; + bool is_used; +}; + +int parse_fuse_bpf_entry(struct fuse_bpf_entry *fbe, int num_entries); + +#ifdef CONFIG_FUSE_BPF + +int fuse_bpf_lookup(struct dentry **out, struct inode *dir, struct dentry *entry, unsigned int flags); + +#else + +static inline int fuse_bpf_lookup(struct dentry **out, struct inode *dir, struct dentry *entry, unsigned int flags) +{ + return 0; +} + +#endif // CONFIG_FUSE_BPF + +int fuse_handle_backing(struct fuse_bpf_entry *feb, struct path *backing_path); + +int fuse_revalidate_backing(struct dentry *entry, unsigned int flags); + /* * FUSE caches dentries and attributes with separate timeout. The * time in jiffies until the dentry/attributes are valid is stored in diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 504336d56a7f..224d7dfe754d 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -78,6 +78,9 @@ static struct inode *fuse_alloc_inode(struct super_block *sb) fi->i_time = 0; fi->inval_mask = 0; +#ifdef CONFIG_FUSE_BPF + fi->backing_inode = NULL; +#endif fi->nodeid = 0; fi->nlookup = 0; fi->attr_version = 0; @@ -120,6 +123,10 @@ static void fuse_evict_inode(struct inode *inode) /* Will write inode on close/munmap and in all other dirtiers */ WARN_ON(inode->i_state & I_DIRTY_INODE); +#ifdef CONFIG_FUSE_BPF + iput(fi->backing_inode); +#endif + truncate_inode_pages_final(&inode->i_data); clear_inode(inode); if (inode->i_sb->s_flags & SB_ACTIVE) { @@ -163,24 +170,24 @@ static ino_t fuse_squash_ino(u64 ino64) } static void fuse_fill_attr_from_inode(struct fuse_attr *attr, - const struct fuse_inode *fi) + const struct inode *inode) { *attr = (struct fuse_attr){ - .ino = fi->inode.i_ino, - .size = fi->inode.i_size, - .blocks = fi->inode.i_blocks, - .atime = fi->inode.i_atime.tv_sec, - .mtime = fi->inode.i_mtime.tv_sec, - .ctime = fi->inode.i_ctime.tv_sec, - .atimensec = fi->inode.i_atime.tv_nsec, - .mtimensec = fi->inode.i_mtime.tv_nsec, - .ctimensec = fi->inode.i_ctime.tv_nsec, - .mode = fi->inode.i_mode, - .nlink = fi->inode.i_nlink, - .uid = fi->inode.i_uid.val, - .gid = fi->inode.i_gid.val, - .rdev = fi->inode.i_rdev, - .blksize = 1u << fi->inode.i_blkbits, + .ino = inode->i_ino, + .size = inode->i_size, + .blocks = inode->i_blocks, + .atime = inode->i_atime.tv_sec, + .mtime = inode->i_mtime.tv_sec, + .ctime = inode->i_ctime.tv_sec, + .atimensec = inode->i_atime.tv_nsec, + .mtimensec = inode->i_mtime.tv_nsec, + .ctimensec = inode->i_ctime.tv_nsec, + .mode = inode->i_mode, + .nlink = inode->i_nlink, + .uid = inode->i_uid.val, + .gid = inode->i_gid.val, + .rdev = inode->i_rdev, + .blksize = 1u << inode->i_blkbits, }; } @@ -351,28 +358,105 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr) else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { fuse_init_common(inode); - init_special_inode(inode, inode->i_mode, - new_decode_dev(attr->rdev)); + init_special_inode(inode, inode->i_mode, attr->rdev); } else BUG(); } +struct fuse_inode_identifier { + u64 nodeid; + struct inode *backing_inode; +}; + static int fuse_inode_eq(struct inode *inode, void *_nodeidp) { - u64 nodeid = *(u64 *) _nodeidp; - if (get_node_id(inode) == nodeid) - return 1; - else - return 0; + struct fuse_inode_identifier *fii = + (struct fuse_inode_identifier *) _nodeidp; + struct fuse_inode *fi = get_fuse_inode(inode); + + return fii->nodeid == fi->nodeid; +} + +static int fuse_inode_backing_eq(struct inode *inode, void *_nodeidp) +{ + struct fuse_inode_identifier *fii = + (struct fuse_inode_identifier *) _nodeidp; + struct fuse_inode *fi = get_fuse_inode(inode); + + return fii->nodeid == fi->nodeid +#ifdef CONFIG_FUSE_BPF + && fii->backing_inode == fi->backing_inode +#endif + ; } static int fuse_inode_set(struct inode *inode, void *_nodeidp) { - u64 nodeid = *(u64 *) _nodeidp; - get_fuse_inode(inode)->nodeid = nodeid; + struct fuse_inode_identifier *fii = + (struct fuse_inode_identifier *) _nodeidp; + struct fuse_inode *fi = get_fuse_inode(inode); + + fi->nodeid = fii->nodeid; + + return 0; +} + +static int fuse_inode_backing_set(struct inode *inode, void *_nodeidp) +{ + struct fuse_inode_identifier *fii = + (struct fuse_inode_identifier *) _nodeidp; + struct fuse_inode *fi = get_fuse_inode(inode); + + fi->nodeid = fii->nodeid; +#ifdef CONFIG_FUSE_BPF + BUG_ON(fi->backing_inode != NULL); + fi->backing_inode = fii->backing_inode; + if (fi->backing_inode) + ihold(fi->backing_inode); +#endif + return 0; } +struct inode *fuse_iget_backing(struct super_block *sb, u64 nodeid, + struct inode *backing_inode) +{ + struct inode *inode; + struct fuse_inode *fi; + struct fuse_conn *fc = get_fuse_conn_super(sb); + struct fuse_inode_identifier fii = { + .nodeid = nodeid, + .backing_inode = backing_inode, + }; + struct fuse_attr attr; + unsigned long hash = (unsigned long) backing_inode; + + if (nodeid) + hash = nodeid; + + fuse_fill_attr_from_inode(&attr, backing_inode); + inode = iget5_locked(sb, hash, fuse_inode_backing_eq, + fuse_inode_backing_set, &fii); + if (!inode) + return NULL; + + if ((inode->i_state & I_NEW)) { + inode->i_flags |= S_NOATIME; + if (!fc->writeback_cache) + inode->i_flags |= S_NOCMTIME; + fuse_init_common(inode); + unlock_new_inode(inode); + } + + fi = get_fuse_inode(inode); + fuse_init_inode(inode, &attr); + spin_lock(&fi->lock); + fi->nlookup++; + spin_unlock(&fi->lock); + + return inode; +} + struct inode *fuse_iget(struct super_block *sb, u64 nodeid, int generation, struct fuse_attr *attr, u64 attr_valid, u64 attr_version) @@ -380,6 +464,9 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid, struct inode *inode; struct fuse_inode *fi; struct fuse_conn *fc = get_fuse_conn_super(sb); + struct fuse_inode_identifier fii = { + .nodeid = nodeid, + }; /* * Auto mount points get their node id from the submount root, which is @@ -401,7 +488,7 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid, } retry: - inode = iget5_locked(sb, nodeid, fuse_inode_eq, fuse_inode_set, &nodeid); + inode = iget5_locked(sb, nodeid, fuse_inode_eq, fuse_inode_set, &fii); if (!inode) return NULL; @@ -433,13 +520,16 @@ struct inode *fuse_ilookup(struct fuse_conn *fc, u64 nodeid, { struct fuse_mount *fm_iter; struct inode *inode; + struct fuse_inode_identifier fii = { + .nodeid = nodeid, + }; WARN_ON(!rwsem_is_locked(&fc->killsb)); list_for_each_entry(fm_iter, &fc->mounts, fc_entry) { if (!fm_iter->sb) continue; - inode = ilookup5(fm_iter->sb, nodeid, fuse_inode_eq, &nodeid); + inode = ilookup5(fm_iter->sb, nodeid, fuse_inode_eq, &fii); if (inode) { if (fm) *fm = fm_iter; @@ -669,6 +759,7 @@ enum { OPT_ALLOW_OTHER, OPT_MAX_READ, OPT_BLKSIZE, + OPT_ROOT_DIR, OPT_ERR }; @@ -683,6 +774,7 @@ static const struct fs_parameter_spec fuse_fs_parameters[] = { fsparam_u32 ("max_read", OPT_MAX_READ), fsparam_u32 ("blksize", OPT_BLKSIZE), fsparam_string ("subtype", OPT_SUBTYPE), + fsparam_u32 ("root_dir", OPT_ROOT_DIR), {} }; @@ -766,6 +858,12 @@ static int fuse_parse_param(struct fs_context *fsc, struct fs_parameter *param) ctx->blksize = result.uint_32; break; + case OPT_ROOT_DIR: + ctx->root_dir = fget(result.uint_32); + if (!ctx->root_dir) + return invalfc(fsc, "Unable to open root directory"); + break; + default: return -EINVAL; } @@ -778,6 +876,8 @@ static void fuse_free_fsc(struct fs_context *fsc) struct fuse_fs_context *ctx = fsc->fs_private; if (ctx) { + if (ctx->root_dir) + fput(ctx->root_dir); kfree(ctx->subtype); kfree(ctx); } @@ -905,15 +1005,29 @@ struct fuse_conn *fuse_conn_get(struct fuse_conn *fc) } EXPORT_SYMBOL_GPL(fuse_conn_get); -static struct inode *fuse_get_root_inode(struct super_block *sb, unsigned mode) +static struct inode *fuse_get_root_inode(struct super_block *sb, + unsigned int mode, + struct file *backing_fd) { struct fuse_attr attr; - memset(&attr, 0, sizeof(attr)); + struct inode *inode; + memset(&attr, 0, sizeof(attr)); attr.mode = mode; attr.ino = FUSE_ROOT_ID; attr.nlink = 1; - return fuse_iget(sb, 1, 0, &attr, 0, 0); + inode = fuse_iget(sb, 1, 0, &attr, 0, 0); + if (!inode) + return NULL; + +#ifdef CONFIG_FUSE_BPF + if (backing_fd) { + get_fuse_inode(inode)->backing_inode = backing_fd->f_inode; + ihold(backing_fd->f_inode); + } +#endif + + return inode; } struct fuse_inode_handle { @@ -928,11 +1042,14 @@ static struct dentry *fuse_get_dentry(struct super_block *sb, struct inode *inode; struct dentry *entry; int err = -ESTALE; + struct fuse_inode_identifier fii = { + .nodeid = handle->nodeid, + }; if (handle->nodeid == 0) goto out_err; - inode = ilookup5(sb, handle->nodeid, fuse_inode_eq, &handle->nodeid); + inode = ilookup5(sb, handle->nodeid, fuse_inode_eq, &fii); if (!inode) { struct fuse_entry_out outarg; const struct qstr name = QSTR_INIT(".", 1); @@ -941,7 +1058,7 @@ static struct dentry *fuse_get_dentry(struct super_block *sb, goto out_err; err = fuse_lookup_name(sb, handle->nodeid, &name, &outarg, - &inode); + NULL, &inode); if (err && err != -ENOENT) goto out_err; if (err || !inode) { @@ -1035,13 +1152,14 @@ static struct dentry *fuse_get_parent(struct dentry *child) struct inode *inode; struct dentry *parent; struct fuse_entry_out outarg; + const struct qstr name = QSTR_INIT("..", 2); int err; if (!fc->export_support) return ERR_PTR(-ESTALE); err = fuse_lookup_name(child_inode->i_sb, get_node_id(child_inode), - &dotdot_name, &outarg, &inode); + &name, &outarg, NULL, &inode); if (err) { if (err == -ENOENT) return ERR_PTR(-ESTALE); @@ -1451,7 +1569,7 @@ static int fuse_fill_super_submount(struct super_block *sb, if (parent_sb->s_subtype && !sb->s_subtype) return -ENOMEM; - fuse_fill_attr_from_inode(&root_attr, parent_fi); + fuse_fill_attr_from_inode(&root_attr, &parent_fi->inode); root = fuse_iget(sb, parent_fi->nodeid, 0, &root_attr, 0, 0); /* * This inode is just a duplicate, so it is not looked up and @@ -1580,11 +1698,12 @@ int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx) fc->no_force_umount = ctx->no_force_umount; err = -ENOMEM; - root = fuse_get_root_inode(sb, ctx->rootmode); + root = fuse_get_root_inode(sb, ctx->rootmode, ctx->root_dir); sb->s_d_op = &fuse_root_dentry_operations; root_dentry = d_make_root(root); if (!root_dentry) goto err_dev_free; + fuse_init_dentry_root(root_dentry, ctx->root_dir); /* Root dentry doesn't have .d_revalidate */ sb->s_d_op = &fuse_dentry_operations; diff --git a/fs/fuse/ioctl.c b/fs/fuse/ioctl.c index 61d8afcb10a3..8bc8d50917e2 100644 --- a/fs/fuse/ioctl.c +++ b/fs/fuse/ioctl.c @@ -422,7 +422,7 @@ static struct fuse_file *fuse_priv_ioctl_prepare(struct inode *inode) if (!S_ISREG(inode->i_mode) && !isdir) return ERR_PTR(-ENOTTY); - return fuse_file_open(fm, get_node_id(inode), O_RDONLY, isdir); + return fuse_file_open(fm, get_node_id(inode), O_RDONLY, isdir, NULL); } static void fuse_priv_ioctl_cleanup(struct inode *inode, struct fuse_file *ff) From patchwork Tue Nov 22 02:15:20 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13051818 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 76011C4167E for ; Tue, 22 Nov 2022 02:16:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232578AbiKVCQg (ORCPT ); Mon, 21 Nov 2022 21:16:36 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41978 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232517AbiKVCQc (ORCPT ); Mon, 21 Nov 2022 21:16:32 -0500 Received: from mail-yw1-x1149.google.com (mail-yw1-x1149.google.com [IPv6:2607:f8b0:4864:20::1149]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9A9C5E3D19 for ; Mon, 21 Nov 2022 18:16:06 -0800 (PST) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-39afd53dcdbso65479657b3.8 for ; Mon, 21 Nov 2022 18:16:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=w2q3Pe5xMPw4X/9LVfd4DivdDutjiK3KnsX8KQ2N8hg=; b=aTBA/4JqNfwuIeF/z6qbwtHTisSmHirpljRg/jqvop/zWE55jxwONCGDNplDOvHIUW EdpMWtBWdM/Q73DKy22ZiPzuWMZIje8JjbI6oo1SzHSUA74BDWub9IHwW0c1jTUAMbyG qOkCaE9aSL9MCjO6dYMB9PFb0aSX8Hfbqe2UBhcL5iq8ZkRxKNPjQ5ioSBSVeFYk0Qqm Z6DISHRrhTLBKncu7nRqFeA8S3zpz179cTjAmXPcklE8oU9QjNuSHkGrM1Ep/EIROqyT +Xu7VQDk/YnBsHGQZGJERKD3mSGCPFH4bds3NFKtzGnpePnm86694b/7JnzRw2PKb2tW dKoQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=w2q3Pe5xMPw4X/9LVfd4DivdDutjiK3KnsX8KQ2N8hg=; b=CPeOEj1/bh4cCSWG91AwyLo6g/a8tLmdoBYneH0tsxbEhPOnTqVnDnVA0GxtiXTyt2 HPUQFD7H+3hzaiSMPh4IXN2FLcYKxIywrOZeAFRo6zuPxpOLLGtCDp/AblWTeeIH5P30 Cd9/o3VengAvnY4mVUujGZU0BXfKJzuJjUpZzJYXbXYVdL9bRmLuOzRL4wqKafE+/pts 7BoOmMFfUAJYdIid9lnoTFBs7yT8Zc8svW9gHzd0xc5gVnCDdobMtaGjB/6OvIERuzVb VLZCDdbRkxrMkssFPI+KcVz99RcNSVwvCu4w6CyJZ1rFaOq9jr/CVDsQTpeesUSsPRLp sYuw== X-Gm-Message-State: ANoB5pltI6C+Zqa0O6gmjXD/00VMepuz2WL8863lKlpn41vpltU+IsfE aAMMSxC5jSfse622IPTXbKgpzSUL/CU= X-Google-Smtp-Source: AA0mqf56By5tkbMPbE3+8HlVejJa2CcHXpyOQFp8e5Hw5P5xL8vmWpfR7zoKgiNxYfs8ibJqKjx+X/YrEyo= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:200:8539:aadd:13be:6e82]) (user=drosen job=sendgmr) by 2002:a81:47c2:0:b0:388:7d2:587b with SMTP id u185-20020a8147c2000000b0038807d2587bmr3633337ywa.416.1669083365933; Mon, 21 Nov 2022 18:16:05 -0800 (PST) Date: Mon, 21 Nov 2022 18:15:20 -0800 In-Reply-To: <20221122021536.1629178-1-drosen@google.com> Mime-Version: 1.0 References: <20221122021536.1629178-1-drosen@google.com> X-Mailer: git-send-email 2.38.1.584.g0f3c55d4c2-goog Message-ID: <20221122021536.1629178-6-drosen@google.com> Subject: [RFC PATCH v2 05/21] fuse-bpf: Add ioctl interface for /dev/fuse From: Daniel Rosenberg To: Miklos Szeredi Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, bpf@vger.kernel.org, kernel-team@android.com, Daniel Rosenberg Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC This introduces an alternative method of responding to fuse requests. Lookups supplying a backing fd or bpf will need to call through the ioctl to ensure there can be no attempts to fool priveledged processes into inadvertantly performing other actions. Signed-off-by: Daniel Rosenberg --- fs/fuse/dev.c | 56 ++++++++++++++++++++++++++++++++------- fs/fuse/fuse_i.h | 1 + include/uapi/linux/fuse.h | 1 + 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 79d2fb6adc83..fbc519c37e66 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1013,18 +1013,19 @@ static int fuse_copy_one(struct fuse_copy_state *cs, void *val, unsigned size) /* Copy the fuse-bpf lookup args and verify them */ #ifdef CONFIG_FUSE_BPF -static int fuse_copy_lookup(struct fuse_copy_state *cs, void *val, unsigned size) +static int fuse_copy_lookup(struct fuse_copy_state *cs, unsigned via_ioctl, void *val, unsigned size) { struct fuse_bpf_entry_out *fbeo = (struct fuse_bpf_entry_out *)val; struct fuse_bpf_entry *feb = container_of(fbeo, struct fuse_bpf_entry, out[0]); int num_entries = size / sizeof(*fbeo); int err; - if (size && size % sizeof(*fbeo) != 0) + if (size && (size % sizeof(*fbeo) != 0 || !via_ioctl)) return -EINVAL; if (num_entries > FUSE_BPF_MAX_ENTRIES) return -EINVAL; + err = fuse_copy_one(cs, val, size); if (err) return err; @@ -1033,7 +1034,7 @@ static int fuse_copy_lookup(struct fuse_copy_state *cs, void *val, unsigned size return err; } #else -static int fuse_copy_lookup(struct fuse_copy_state *cs, void *val, unsigned size) +static int fuse_copy_lookup(struct fuse_copy_state *cs, unsigned via_ioctl, void *val, unsigned size) { return fuse_copy_one(cs, val, size); } @@ -1042,7 +1043,7 @@ static int fuse_copy_lookup(struct fuse_copy_state *cs, void *val, unsigned size /* Copy request arguments to/from userspace buffer */ static int fuse_copy_args(struct fuse_copy_state *cs, unsigned numargs, unsigned argpages, struct fuse_arg *args, - int zeroing, unsigned is_lookup) + int zeroing, unsigned is_lookup, unsigned via_ioct) { int err = 0; unsigned i; @@ -1052,7 +1053,7 @@ static int fuse_copy_args(struct fuse_copy_state *cs, unsigned numargs, if (i == numargs - 1 && argpages) err = fuse_copy_pages(cs, arg->size, zeroing); else if (i == numargs - 1 && is_lookup) - err = fuse_copy_lookup(cs, arg->value, arg->size); + err = fuse_copy_lookup(cs, via_ioct, arg->value, arg->size); else err = fuse_copy_one(cs, arg->value, arg->size); } @@ -1330,7 +1331,7 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file, err = fuse_copy_one(cs, &req->in.h, sizeof(req->in.h)); if (!err) err = fuse_copy_args(cs, args->in_numargs, args->in_pages, - (struct fuse_arg *) args->in_args, 0, 0); + (struct fuse_arg *) args->in_args, 0, 0, 0); fuse_copy_finish(cs); spin_lock(&fpq->lock); clear_bit(FR_LOCKED, &req->flags); @@ -1869,7 +1870,8 @@ static int copy_out_args(struct fuse_copy_state *cs, struct fuse_args *args, lastarg->size -= diffsize; } return fuse_copy_args(cs, args->out_numargs, args->out_pages, - args->out_args, args->page_zeroing, args->is_lookup); + args->out_args, args->page_zeroing, args->is_lookup, + args->via_ioctl); } /* @@ -1879,7 +1881,7 @@ static int copy_out_args(struct fuse_copy_state *cs, struct fuse_args *args, * it from the list and copy the rest of the buffer to the request. * The request is finished by calling fuse_request_end(). */ -static ssize_t fuse_dev_do_write(struct fuse_dev *fud, +static ssize_t fuse_dev_do_write(struct fuse_dev *fud, bool from_ioctl, struct fuse_copy_state *cs, size_t nbytes) { int err; @@ -1951,6 +1953,7 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud, if (!req->args->page_replace) cs->move_pages = 0; + req->args->via_ioctl = from_ioctl; if (oh.error) err = nbytes != sizeof(oh) ? -EINVAL : 0; else @@ -1989,7 +1992,7 @@ static ssize_t fuse_dev_write(struct kiocb *iocb, struct iov_iter *from) fuse_copy_init(&cs, 0, from); - return fuse_dev_do_write(fud, &cs, iov_iter_count(from)); + return fuse_dev_do_write(fud, false, &cs, iov_iter_count(from)); } static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe, @@ -2070,7 +2073,7 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe, if (flags & SPLICE_F_MOVE) cs.move_pages = 1; - ret = fuse_dev_do_write(fud, &cs, len); + ret = fuse_dev_do_write(fud, false, &cs, len); pipe_lock(pipe); out_free: @@ -2283,6 +2286,33 @@ static int fuse_device_clone(struct fuse_conn *fc, struct file *new) return 0; } +// Provides an alternate means to respond to a fuse request +static int fuse_handle_ioc_response(struct fuse_dev *dev, void *buff, uint32_t size) +{ + struct fuse_copy_state cs; + struct iovec *iov = NULL; + struct iov_iter iter; + int res; + + if (size > PAGE_SIZE) + return -EINVAL; + iov = (struct iovec *) __get_free_page(GFP_KERNEL); + if (!iov) + return -ENOMEM; + + iov->iov_base = buff; + iov->iov_len = size; + + iov_iter_init(&iter, READ, iov, 1, size); + fuse_copy_init(&cs, 0, &iter); + + + res = fuse_dev_do_write(dev, true, &cs, size); + free_page((unsigned long) iov); + + return res; +} + static long fuse_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -2316,6 +2346,12 @@ static long fuse_dev_ioctl(struct file *file, unsigned int cmd, } break; default: + if (_IOC_TYPE(cmd) == FUSE_DEV_IOC_MAGIC + && _IOC_NR(cmd) == _IOC_NR(FUSE_DEV_IOC_BPF_RESPONSE(0)) + && _IOC_DIR(cmd) == _IOC_WRITE) { + res = fuse_handle_ioc_response(fuse_get_dev(file), (void *) arg, _IOC_SIZE(cmd)); + break; + } res = -ENOTTY; break; } diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index d67325af5e72..3452530aba94 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -314,6 +314,7 @@ struct fuse_args { bool page_replace:1; bool may_block:1; bool is_lookup:1; + bool via_ioctl:1; struct fuse_in_arg in_args[3]; struct fuse_arg out_args[2]; void (*end)(struct fuse_mount *fm, struct fuse_args *args, int error); diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index 0e19076729d9..e49e5a8e044c 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -972,6 +972,7 @@ struct fuse_notify_retrieve_in { /* Device ioctls: */ #define FUSE_DEV_IOC_MAGIC 229 #define FUSE_DEV_IOC_CLONE _IOR(FUSE_DEV_IOC_MAGIC, 0, uint32_t) +#define FUSE_DEV_IOC_BPF_RESPONSE(N) _IOW(FUSE_DEV_IOC_MAGIC, 125, char[N]) struct fuse_lseek_in { uint64_t fh; From patchwork Tue Nov 22 02:15:21 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13051827 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 DB343C4332F for ; Tue, 22 Nov 2022 02:17:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232599AbiKVCRB (ORCPT ); Mon, 21 Nov 2022 21:17:01 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42372 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232553AbiKVCQe (ORCPT ); Mon, 21 Nov 2022 21:16:34 -0500 Received: from mail-yw1-x1149.google.com (mail-yw1-x1149.google.com [IPv6:2607:f8b0:4864:20::1149]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D361BE634E for ; Mon, 21 Nov 2022 18:16:08 -0800 (PST) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-393fc59d09fso106735017b3.18 for ; Mon, 21 Nov 2022 18:16:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=J0kBTpGRrZ4fbYeP7Bh1hsOct+NKdkC7fVmp/4th4/s=; b=PTyyzqK8VIpYcc3Jnv1HwKlTZVtN825+P2qskFJugJ/DNoyLCfdNJgvvb4WK//Litx 9PSdHS+fDIZijxFoVEzf+iNQufz0lxZloF1cOdSlQxtrE3mGt+L7NNZ7cAq+QY79+G+5 qDhW62Uexhw5NGJYa+8of89/EQtHfE3nO8G2fOeaWQ4CRwdC6fVqUCyjN41ZDHRvzP0e 1D4semdRyxKDsXvvOZn8BhhTnael1tURdERVZKJGOgEYACViGTrodjUlJBmnKctQGZd1 WHhMCxTizWOZtXg3xr0jXG4k/8eB3tTqtQsiIsWRi9nQDHQUvHOz09sNxIWD/ti3qwf2 kzFw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=J0kBTpGRrZ4fbYeP7Bh1hsOct+NKdkC7fVmp/4th4/s=; b=iq7sfasaK78rBwaD2AnQiT+nNi9rIDq9NYvCXIDF36RNfxmLXE/WwllTrOwhuEeiZ8 qqQF/SGcKnG81vM3Gxoif7m3xPLSvmHG9kBQi9m1LEq3Fcho7foNRbtoj/t1YG9xb/iD l53aL8K4QO/WPvuY57U8Go5Ncn4aCs4nnoTVAdE9dsJtkZibdpD6/sPHPxtYXI+wrMPT 0/MkwekTi+sTWQTdoEGdmQ4RpzRESu0cNJJ7BL2xnyjocC7p5RPrKwrbjEuhgV/Jt2PY GsmW1tcnwfUgUNrSmRh37ibxwsnvPJBrwAxOdVZfmzzAexmexFa4H+uIwkiy+ojMzq6G is9A== X-Gm-Message-State: ANoB5pnG6ldDp/jiZLu25ma1B62OLWWy57/P8pCKSlYMXhaAsBdQ+Msb BZFLgh+BVBQI/OUwnLShdaxOJUtOnmI= X-Google-Smtp-Source: AA0mqf73Jt7+46OfL52Xg14iE61SnVE1xuS16R+nTJ/NK6q+VyFTx2GjobIGL8pHS0TY+qa/ra9LsUFEknk= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:200:8539:aadd:13be:6e82]) (user=drosen job=sendgmr) by 2002:a05:6902:118e:b0:6e7:f54:b3d6 with SMTP id m14-20020a056902118e00b006e70f54b3d6mr4166933ybu.577.1669083368110; Mon, 21 Nov 2022 18:16:08 -0800 (PST) Date: Mon, 21 Nov 2022 18:15:21 -0800 In-Reply-To: <20221122021536.1629178-1-drosen@google.com> Mime-Version: 1.0 References: <20221122021536.1629178-1-drosen@google.com> X-Mailer: git-send-email 2.38.1.584.g0f3c55d4c2-goog Message-ID: <20221122021536.1629178-7-drosen@google.com> Subject: [RFC PATCH v2 06/21] fuse-bpf: Don't support export_operations From: Daniel Rosenberg To: Miklos Szeredi Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, bpf@vger.kernel.org, kernel-team@android.com, Daniel Rosenberg Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC In the future, we may choose to support these, but it poses some challenges. In order to create a disconnected dentry/inode, we'll need to encode the mountpoint and bpf into the file_handle, which means we'd need a stable representation of them. This also won't hold up to cases where the bpf is not stateless. One possibility is registering bpf programs and mounts in a specific order, so they can be assigned consistent ids we can use in the file_handle. We can defer to the lower filesystem for the lower inode's representation in the file_handle. Signed-off-by: Daniel Rosenberg --- fs/fuse/inode.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 224d7dfe754d..bafb2832627d 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -1100,6 +1100,14 @@ static int fuse_encode_fh(struct inode *inode, u32 *fh, int *max_len, nodeid = get_fuse_inode(inode)->nodeid; generation = inode->i_generation; +#ifdef CONFIG_FUSE_BPF + /* TODO: Does it make sense to support this in some cases? */ + if (!nodeid && get_fuse_inode(inode)->backing_inode) { + *max_len = 0; + return FILEID_INVALID; + } +#endif + fh[0] = (u32)(nodeid >> 32); fh[1] = (u32)(nodeid & 0xffffffff); fh[2] = generation; From patchwork Tue Nov 22 02:15:22 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13051828 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 E4CE5C4332F for ; Tue, 22 Nov 2022 02:17:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232608AbiKVCRN (ORCPT ); Mon, 21 Nov 2022 21:17:13 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42600 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232515AbiKVCQf (ORCPT ); Mon, 21 Nov 2022 21:16:35 -0500 Received: from mail-yw1-x114a.google.com (mail-yw1-x114a.google.com [IPv6:2607:f8b0:4864:20::114a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A5912E636E for ; Mon, 21 Nov 2022 18:16:10 -0800 (PST) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-36fc0644f51so130684957b3.17 for ; Mon, 21 Nov 2022 18:16:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=vP4dk37Xutp/zfeNLDJTOudpRYQEN0iw5Uyo2Mo/Ols=; b=DY/+xVmpzHQ3WvEhCX7ieUerHnSB5Lpd6FMaZi6jH6pOfb5qnq5SVm2kzYA7oUL/nw WO9LLKurB78pQ87qElgkNJZ7ImFLGJB6Yh+edXbaQDZNC2NdhxOLAIXfA1Vcf2LkB58P Qn1iFXabi4oOuviuNqWNZGySxV5cfswF7VqpPyA+R3g+3OCV4lvTpIgjkXFytSdsz+LW MHnhROSy/7ZBDK9u9g04Gvn/e5l8NapW2aA0nEHFDnH3IO7QGeHiN/jCcm6kCn72+7Dj JjGtw49yT4bnRddNuuZ4Zgv7DSxrlKLXNb/0Ajf19HiN91NjSd8wXrF0iKLI8LWWc/iQ 1Nzw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=vP4dk37Xutp/zfeNLDJTOudpRYQEN0iw5Uyo2Mo/Ols=; b=g6ftieZ1SN9Nswd9D530Irvw0y/i2PmYc9f7zpmiAH+RFBPry9uDzYHhxpnc3uJtLQ 6EcNbRsi1THykCFtQX21dCcGXixfVB+eP70QlUC3RqsmYfgaZNDOk0bYyay/Ps87X0f7 lI7WWDPjmg4TzXKRpQY5nmGiYjFhxj6BsuSvAFC+wDMNoCCxFnW/nbRoVykir8M6kKe+ MwUTvlkXIvm6+MGSA7z6i06F3XucxNUcI+D0W5tMUsrWXHzM94M+D/febygAjAK8AQyW PVHuXVF+JIERajEFiK5JxKCO9cMeAdNf0Rhc74UFqhqEhg5QLg1Ocj2pkwBWahir+I7m /2UA== X-Gm-Message-State: ANoB5pleWBhKkBDbQuU4P3Vd+A0Q3AaJVdeoeb4VEtWo289SMOxTx4Ov nYm0KGoDsgOZyx8RmjeWcIY/lAp1icE= X-Google-Smtp-Source: AA0mqf476G/R0CUwNiLHlfAwn4G2ioKB+vHzTZxkW/54/ZyJ3M6Rw1N3J+sdZB5SEx/PmuNCUZHD3uiftQc= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:200:8539:aadd:13be:6e82]) (user=drosen job=sendgmr) by 2002:a25:eb04:0:b0:6cf:e761:41ed with SMTP id d4-20020a25eb04000000b006cfe76141edmr3734311ybs.82.1669083370436; Mon, 21 Nov 2022 18:16:10 -0800 (PST) Date: Mon, 21 Nov 2022 18:15:22 -0800 In-Reply-To: <20221122021536.1629178-1-drosen@google.com> Mime-Version: 1.0 References: <20221122021536.1629178-1-drosen@google.com> X-Mailer: git-send-email 2.38.1.584.g0f3c55d4c2-goog Message-ID: <20221122021536.1629178-8-drosen@google.com> Subject: [RFC PATCH v2 07/21] fuse-bpf: Add support for FUSE_ACCESS From: Daniel Rosenberg To: Miklos Szeredi Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, bpf@vger.kernel.org, kernel-team@android.com, Daniel Rosenberg , Paul Lawrence Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/dir.c | 6 ++++++ fs/fuse/fuse_i.h | 6 ++++++ 3 files changed, 57 insertions(+) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index 5a59a8963d52..670e82d68e36 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -390,3 +390,48 @@ int fuse_revalidate_backing(struct dentry *entry, unsigned int flags) return backing_entry->d_op->d_revalidate(backing_entry, flags); return 1; } + +static int fuse_access_initialize_in(struct fuse_args *fa, struct fuse_access_in *fai, + struct inode *inode, int mask) +{ + *fai = (struct fuse_access_in) { + .mask = mask, + }; + + *fa = (struct fuse_args) { + .opcode = FUSE_ACCESS, + .nodeid = get_node_id(inode), + .in_numargs = 1, + .in_args[0].size = sizeof(*fai), + .in_args[0].value = fai, + }; + + return 0; +} + +static int fuse_access_initialize_out(struct fuse_args *fa, struct fuse_access_in *fai, + struct inode *inode, int mask) +{ + return 0; +} + +static int fuse_access_backing(struct fuse_args *fa, int *out, struct inode *inode, int mask) +{ + struct fuse_inode *fi = get_fuse_inode(inode); + const struct fuse_access_in *fai = fa->in_args[0].value; + + *out = inode_permission(&init_user_ns, fi->backing_inode, fai->mask); + return 0; +} + +static int fuse_access_finalize(struct fuse_args *fa, int *out, struct inode *inode, int mask) +{ + return 0; +} + +int fuse_bpf_access(int *out, struct inode *inode, int mask) +{ + return fuse_bpf_backing(inode, struct fuse_access_in, out, + fuse_access_initialize_in, fuse_access_initialize_out, + fuse_access_backing, fuse_access_finalize, inode, mask); +} diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index fb7c6988f0d9..4e19320889ed 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1364,6 +1364,9 @@ static int fuse_access(struct inode *inode, int mask) struct fuse_access_in inarg; int err; + if (fuse_bpf_access(&err, inode, mask)) + return err; + BUG_ON(mask & MAY_NOT_BLOCK); if (fm->fc->no_access) @@ -1420,6 +1423,9 @@ static int fuse_permission(struct user_namespace *mnt_userns, if (!fuse_allow_current_process(fc)) return -EACCES; + if (fuse_bpf_access(&err, inode, mask)) + return err; + /* * If attributes are needed, refresh them before proceeding */ diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 3452530aba94..db3f703c700f 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1401,6 +1401,7 @@ int parse_fuse_bpf_entry(struct fuse_bpf_entry *fbe, int num_entries); #ifdef CONFIG_FUSE_BPF int fuse_bpf_lookup(struct dentry **out, struct inode *dir, struct dentry *entry, unsigned int flags); +int fuse_bpf_access(int *out, struct inode *inode, int mask); #else @@ -1409,6 +1410,11 @@ static inline int fuse_bpf_lookup(struct dentry **out, struct inode *dir, struct return 0; } +static inline int fuse_bpf_access(int *out, struct inode *inode, int mask) +{ + return 0; +} + #endif // CONFIG_FUSE_BPF int fuse_handle_backing(struct fuse_bpf_entry *feb, struct path *backing_path); From patchwork Tue Nov 22 02:15:23 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13051829 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 BEA8FC4321E for ; Tue, 22 Nov 2022 02:17:22 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232134AbiKVCRT (ORCPT ); Mon, 21 Nov 2022 21:17:19 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42816 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232287AbiKVCQi (ORCPT ); Mon, 21 Nov 2022 21:16:38 -0500 Received: from mail-yw1-x114a.google.com (mail-yw1-x114a.google.com [IPv6:2607:f8b0:4864:20::114a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 45FC1E637C for ; Mon, 21 Nov 2022 18:16:13 -0800 (PST) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-3a15bec5d25so43101657b3.19 for ; Mon, 21 Nov 2022 18:16:13 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=YI7vW6c4K96rrFjk3kAmKImDsQ1aqnDC905YGpSrwzg=; b=QjoJIIfxEO0QcSPemPtaHnOA3LOIMlbI0LWuX5Qcvau93ztFNlhi6LpWUYlXE5V6lR s+/YMob9Vz1BugWRNv0XR1XHOFD5K48n+WY8PC4yBmcG1k5Bro/WAglb1fsEe87u/U38 wRUxe8rfXhhdOD6+W97hB8aZVBiiIRQ23y3AgJ9G+p7yUdXJePBNkRahA0wfaL+st6bw fwci5sDqdYCh1ja1BPHV9lFNw7YpJKkvFpu0JRvoQSKxbX1U4lj3XRM2dULPxY8TQrg2 t7lXb/vX6HgrpmFyOYkgGmvUnSIf6zizvQ59C9VGjUaEIUt5rLIctZ29RCV6wVb22Cz9 EbKQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=YI7vW6c4K96rrFjk3kAmKImDsQ1aqnDC905YGpSrwzg=; b=6tQUIGbD9vW+q0wzACIQ8SSsfwZGYysOZUNpMoFxVp7WbcCD4b9voTzJ7GLXdByJNT jzNlF2h0aGR71opIoGe7B+bl6CFvX/hJ51KtG4hdHmJ8nePfiyKacIL6QgUVinLY8aDH UAyE32LjCiiRwt4C3v6ORvazwpWlls9/uewoqLs9m24L+sz/6rGTBE+Tx3trxiR32lN+ 3mW/KlETmTd/yFl2YNEIC0f3MHA6NT+E/qaOAGFhB1b1wDsYRrofhbWs8Gr36GnfhiYN ShuFrFInSdiiGLd6Q61G47nDtI/kzUNp6k3+PCzhGCLL1FaTXZIXbAz9f5Rg1r63fHHu pRcw== X-Gm-Message-State: ANoB5plxtZCedNthEviiaItIJFNN+A8kRaepkUdvda+fCvvdChIu2vE1 c48ueZfqDl0N3kDPEcuIr7RXlFy84PE= X-Google-Smtp-Source: AA0mqf4/pefLkJQGg8e/QbgeP7lFD1agiTyoHkH6/yCiZ2Z9DUXCsl0s2g4JmMF6txc9Z3xM/mdGOD8kgoM= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:200:8539:aadd:13be:6e82]) (user=drosen job=sendgmr) by 2002:a25:d704:0:b0:6b3:369f:7436 with SMTP id o4-20020a25d704000000b006b3369f7436mr2526513ybg.172.1669083372616; Mon, 21 Nov 2022 18:16:12 -0800 (PST) Date: Mon, 21 Nov 2022 18:15:23 -0800 In-Reply-To: <20221122021536.1629178-1-drosen@google.com> Mime-Version: 1.0 References: <20221122021536.1629178-1-drosen@google.com> X-Mailer: git-send-email 2.38.1.584.g0f3c55d4c2-goog Message-ID: <20221122021536.1629178-9-drosen@google.com> Subject: [RFC PATCH v2 08/21] fuse-bpf: Partially add mapping support From: Daniel Rosenberg To: Miklos Szeredi Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, bpf@vger.kernel.org, kernel-team@android.com, Daniel Rosenberg , Paul Lawrence Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC This adds a backing implementation for mapping, but no bpf counterpart yet. Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 37 +++++++++++++++++++++++++++++++++++++ fs/fuse/file.c | 6 ++++++ fs/fuse/fuse_i.h | 4 +++- 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index 670e82d68e36..8d862bc64acd 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -205,6 +205,43 @@ static void fuse_stat_to_attr(struct fuse_conn *fc, struct inode *inode, attr->blksize = 1 << blkbits; } +ssize_t fuse_backing_mmap(struct file *file, struct vm_area_struct *vma) +{ + int ret; + struct fuse_file *ff = file->private_data; + struct inode *fuse_inode = file_inode(file); + struct file *backing_file = ff->backing_file; + struct inode *backing_inode = file_inode(backing_file); + + if (!backing_file->f_op->mmap) + return -ENODEV; + + if (WARN_ON(file != vma->vm_file)) + return -EIO; + + vma->vm_file = get_file(backing_file); + + ret = call_mmap(vma->vm_file, vma); + + if (ret) + fput(backing_file); + else + fput(file); + + if (file->f_flags & O_NOATIME) + return ret; + + if ((!timespec64_equal(&fuse_inode->i_mtime, &backing_inode->i_mtime) || + !timespec64_equal(&fuse_inode->i_ctime, + &backing_inode->i_ctime))) { + fuse_inode->i_mtime = backing_inode->i_mtime; + fuse_inode->i_ctime = backing_inode->i_ctime; + } + touch_atime(&file->f_path); + + return ret; +} + /******************************************************************************* * Directory operations after here * ******************************************************************************/ diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 503327be3942..24fd4f33105c 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -2452,6 +2452,12 @@ static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma) if (FUSE_IS_DAX(file_inode(file))) return fuse_dax_mmap(file, vma); +#ifdef CONFIG_FUSE_BPF + /* TODO - this is simply passthrough, not a proper BPF filter */ + if (ff->backing_file) + return fuse_backing_mmap(file, vma); +#endif + if (ff->open_flags & FOPEN_DIRECT_IO) { /* Can't provide the coherency needed for MAP_SHARED */ if (vma->vm_flags & VM_MAYSHARE) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index db3f703c700f..95d67afcff05 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1417,7 +1417,9 @@ static inline int fuse_bpf_access(int *out, struct inode *inode, int mask) #endif // CONFIG_FUSE_BPF -int fuse_handle_backing(struct fuse_bpf_entry *feb, struct path *backing_path); +ssize_t fuse_backing_mmap(struct file *file, struct vm_area_struct *vma); + +int fuse_handle_backing(struct fuse_bpf_entry *fbe, struct path *backing_path); int fuse_revalidate_backing(struct dentry *entry, unsigned int flags); From patchwork Tue Nov 22 02:15:24 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13051830 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 1EAB3C4332F for ; Tue, 22 Nov 2022 02:17:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232252AbiKVCRv (ORCPT ); Mon, 21 Nov 2022 21:17:51 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42686 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232600AbiKVCRC (ORCPT ); Mon, 21 Nov 2022 21:17:02 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 75CB6E674E for ; Mon, 21 Nov 2022 18:16:15 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id p188-20020a2542c5000000b006ea37a57e20so6626860yba.13 for ; Mon, 21 Nov 2022 18:16:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=mBD6bGviTnQaNR8TlIrtDcNsv7AoSToN0hwSmPU39NM=; b=oKcvpaX8yBxVrqjzpCwp8hRJbcWcrVDPsDHSTtU/JumfrQNEB9LNqF2bZQDgm66L23 ezgqXB2c8vNVR5Ss+f0VlAAkX7XbGHrjbnnMn7F2oqVZtP8ZWNt7oydnS1QJUerPmQrw wZlW7iK35Eki/V2aGDIyRlsqTBNDWVYquyeLToqnZuW5Dk/b7LAie/wJsfzS3DkKBD2Z UTRBDxexsXEWuJSNee5jmYV9WWIOxyhAQPI0DgGAvgfFII8jVxgE22HiU1GNrHiYDYUL 1O33KGWuGc2xU5ucwwHWvW+lMdsOuhQ+wShSsdOzU36NkBFSAmPL0D30z2fGKF6c0j0K MJ1Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=mBD6bGviTnQaNR8TlIrtDcNsv7AoSToN0hwSmPU39NM=; b=xrop3iZeNv/XsOh6/u6HSVN524UcfexyGArxhKJs/40Po0KVzcBaBmTCcRVdvslPK9 zSedd7ptAl5AHcmooFUHjT3Js7j8RXfPJLd1R32l2GhcScS1ZIS7PUdpShnymjd1Dffj O3+XsB6LnzHG/FeFA50yo0JF9CP3vR/ljqE4l5PE2Zp4+WaqXtnoK5DlLXtyl9U7ZdLd jwbdv3yWwgr7Pqf+rZKiZUuDQm/ei64vcKOyqfDL6BwvSc0Jr/UTLnbFqJeZfJ2bT3mo ztIoGIBDcSpYFPlweypGg8NLcNCXlNw6LXdXquQwBjwqu/w5Kos/fjnebMjYkSMGihs4 C0gg== X-Gm-Message-State: ACrzQf3AdevCPD3yrfWj3tZVhpJhX9ct7luDfimdAXi0XDDA6x59Jrcm EMlprnmhrvDz3y9rFpf9GSXWFES+DIM= X-Google-Smtp-Source: AMsMyM4Kn48IaLW3aQ/ISb02XRlP+7K9GeYY53FXTg2JD7CCTW1NuBvn0mi2FPfdwt99RzojkrLJTecFwGg= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:200:8539:aadd:13be:6e82]) (user=drosen job=sendgmr) by 2002:a81:86c2:0:b0:332:a104:f7e4 with SMTP id w185-20020a8186c2000000b00332a104f7e4mr66525980ywf.505.1669083374599; Mon, 21 Nov 2022 18:16:14 -0800 (PST) Date: Mon, 21 Nov 2022 18:15:24 -0800 In-Reply-To: <20221122021536.1629178-1-drosen@google.com> Mime-Version: 1.0 References: <20221122021536.1629178-1-drosen@google.com> X-Mailer: git-send-email 2.38.1.584.g0f3c55d4c2-goog Message-ID: <20221122021536.1629178-10-drosen@google.com> Subject: [RFC PATCH v2 09/21] fuse-bpf: Add lseek support From: Daniel Rosenberg To: Miklos Szeredi Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, bpf@vger.kernel.org, kernel-team@android.com, Daniel Rosenberg , Paul Lawrence Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/file.c | 3 ++ fs/fuse/fuse_i.h | 6 ++++ 3 files changed, 97 insertions(+) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index 8d862bc64acd..76f48872ed35 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -205,6 +205,94 @@ static void fuse_stat_to_attr(struct fuse_conn *fc, struct inode *inode, attr->blksize = 1 << blkbits; } +struct fuse_lseek_io { + struct fuse_lseek_in fli; + struct fuse_lseek_out flo; +}; + +static int fuse_lseek_initialize_in(struct fuse_args *fa, struct fuse_lseek_io *flio, + struct file *file, loff_t offset, int whence) +{ + struct fuse_file *fuse_file = file->private_data; + + flio->fli = (struct fuse_lseek_in) { + .fh = fuse_file->fh, + .offset = offset, + .whence = whence, + }; + + *fa = (struct fuse_args) { + .nodeid = get_node_id(file->f_inode), + .opcode = FUSE_LSEEK, + .in_numargs = 1, + .in_args[0].size = sizeof(flio->fli), + .in_args[0].value = &flio->fli, + }; + + return 0; +} + +static int fuse_lseek_initialize_out(struct fuse_args *fa, struct fuse_lseek_io *flio, + struct file *file, loff_t offset, int whence) +{ + fa->out_numargs = 1; + fa->out_args[0].size = sizeof(flio->flo); + fa->out_args[0].value = &flio->flo; + + return 0; +} + +static int fuse_lseek_backing(struct fuse_args *fa, loff_t *out, + struct file *file, loff_t offset, int whence) +{ + const struct fuse_lseek_in *fli = fa->in_args[0].value; + struct fuse_lseek_out *flo = fa->out_args[0].value; + struct fuse_file *fuse_file = file->private_data; + struct file *backing_file = fuse_file->backing_file; + + /* TODO: Handle changing of the file handle */ + if (offset == 0) { + if (whence == SEEK_CUR) { + flo->offset = file->f_pos; + *out = flo->offset; + return 0; + } + + if (whence == SEEK_SET) { + flo->offset = vfs_setpos(file, 0, 0); + *out = flo->offset; + return 0; + } + } + + inode_lock(file->f_inode); + backing_file->f_pos = file->f_pos; + *out = vfs_llseek(backing_file, fli->offset, fli->whence); + flo->offset = *out; + inode_unlock(file->f_inode); + return 0; +} + +static int fuse_lseek_finalize(struct fuse_args *fa, loff_t *out, + struct file *file, loff_t offset, int whence) +{ + struct fuse_lseek_out *flo = fa->out_args[0].value; + + if (!fa->error_in) + file->f_pos = flo->offset; + *out = flo->offset; + return 0; +} + +int fuse_bpf_lseek(loff_t *out, struct inode *inode, struct file *file, loff_t offset, int whence) +{ + return fuse_bpf_backing(inode, struct fuse_lseek_io, out, + fuse_lseek_initialize_in, fuse_lseek_initialize_out, + fuse_lseek_backing, + fuse_lseek_finalize, + file, offset, whence); +} + ssize_t fuse_backing_mmap(struct file *file, struct vm_area_struct *vma) { int ret; diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 24fd4f33105c..e90b3e2d5452 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -2704,6 +2704,9 @@ static loff_t fuse_file_llseek(struct file *file, loff_t offset, int whence) loff_t retval; struct inode *inode = file_inode(file); + if (fuse_bpf_lseek(&retval, inode, file, offset, whence)) + return retval; + switch (whence) { case SEEK_SET: case SEEK_CUR: diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 95d67afcff05..108c2ea15a49 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1400,11 +1400,17 @@ int parse_fuse_bpf_entry(struct fuse_bpf_entry *fbe, int num_entries); #ifdef CONFIG_FUSE_BPF +int fuse_bpf_lseek(loff_t *out, struct inode *inode, struct file *file, loff_t offset, int whence); int fuse_bpf_lookup(struct dentry **out, struct inode *dir, struct dentry *entry, unsigned int flags); int fuse_bpf_access(int *out, struct inode *inode, int mask); #else +static inline int fuse_bpf_lseek(loff_t *out, struct inode *inode, struct file *file, loff_t offset, int whence) +{ + return 0; +} + static inline int fuse_bpf_lookup(struct dentry **out, struct inode *dir, struct dentry *entry, unsigned int flags) { return 0; From patchwork Tue Nov 22 02:15:25 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13051831 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 2CD94C433FE for ; Tue, 22 Nov 2022 02:17:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232455AbiKVCRw (ORCPT ); Mon, 21 Nov 2022 21:17:52 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42704 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232602AbiKVCRC (ORCPT ); Mon, 21 Nov 2022 21:17:02 -0500 Received: from mail-yw1-x114a.google.com (mail-yw1-x114a.google.com [IPv6:2607:f8b0:4864:20::114a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 38EABE6744 for ; Mon, 21 Nov 2022 18:16:17 -0800 (PST) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-3a15bec5d25so43102687b3.19 for ; Mon, 21 Nov 2022 18:16:17 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=/H+L2cRtspRUVOzDKfhmH9r0h1z7XV5y5jiKoA2w7Jo=; b=GkbYhSRuG3pnC12YeGswQtbl8Qkm/FgEIiaZy5lmsxd6nnIJGVSGgoCrt6HlEko6Ki C6yr+mtFUhPtVnoaJg/ByALo10sNnWpcBCubg/D8R0ssNLL3XjyN46mJHVC5Um8b16CU orSmyP3WSpCgYDTcMIiZcxb2R80zdMKhHpr45RrSwbcJgu+a3jL9Xhb8MiJaYe2teBth L8HZN6e2FJlFs1cfNF808/GPl/xs2NrIzv6kCLjM5O/xSQgt9ou3kcdxWqlecQ+QDqfO utLWnK4t1OVh8O7dFmQ1g64uSIWNxLOGjQxuWWbknOLE404qWDNW8Qs3bG4NsLYvtmbe jw9Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=/H+L2cRtspRUVOzDKfhmH9r0h1z7XV5y5jiKoA2w7Jo=; b=Kte62Y6ITXGrBzZB4vAZdqdlN3wuiczevl3vttqkwtcCkJadXrUc29rB3yH6feCA3z R3wv9Jt5YpifNHFMnuey2TK2ceqzSqE6/pmcK8HBWUPwCLJAyyyrpCLG5Eo7NG2DIkb9 bMzbZ2SsgfpYvDkaIOltx2Xl9E2YPxYK69belxolerR4lC6D49ed6H0ZpaOfn+Z0CTmI HzdT7WvnNIk0NV1MYM+5PScvD0EqxXxBSsc8RL53MLkqWKuZwvXCEmud7gkIX5ld5+Qq F3ubeu9Re/yZRMgojxbxtG6qM+rOjib3OjpqXTMDcbm3fscImqls9xZivTerIMPsfwW7 x0IA== X-Gm-Message-State: ANoB5pmVJl5nI7JdWu70kQXzdJpTWFXnJ0O9WYZ+f4R5pjT46uLVMRm4 pQlEuR6Z9ghmfUdDpq/BZQiaiIWlikc= X-Google-Smtp-Source: AA0mqf6T81Q7kb/8MBkNfWtjc8pLPbKvhJyO5HsbQTEwR7WiW1wy5USpYOyxaJPL2wysP5NucahLhCtfZFU= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:200:8539:aadd:13be:6e82]) (user=drosen job=sendgmr) by 2002:a05:690c:845:b0:3a5:36e5:94eb with SMTP id bz5-20020a05690c084500b003a536e594ebmr5266716ywb.39.1669083377322; Mon, 21 Nov 2022 18:16:17 -0800 (PST) Date: Mon, 21 Nov 2022 18:15:25 -0800 In-Reply-To: <20221122021536.1629178-1-drosen@google.com> Mime-Version: 1.0 References: <20221122021536.1629178-1-drosen@google.com> X-Mailer: git-send-email 2.38.1.584.g0f3c55d4c2-goog Message-ID: <20221122021536.1629178-11-drosen@google.com> Subject: [RFC PATCH v2 10/21] fuse-bpf: Add support for fallocate From: Daniel Rosenberg To: Miklos Szeredi Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, bpf@vger.kernel.org, kernel-team@android.com, Daniel Rosenberg , Paul Lawrence Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/file.c | 3 +++ fs/fuse/fuse_i.h | 6 +++++ 3 files changed, 67 insertions(+) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index 76f48872ed35..51aadeb1b7dc 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -330,6 +330,64 @@ ssize_t fuse_backing_mmap(struct file *file, struct vm_area_struct *vma) return ret; } +static int fuse_file_fallocate_initialize_in(struct fuse_args *fa, + struct fuse_fallocate_in *ffi, + struct file *file, int mode, loff_t offset, loff_t length) +{ + struct fuse_file *ff = file->private_data; + + *ffi = (struct fuse_fallocate_in) { + .fh = ff->fh, + .offset = offset, + .length = length, + .mode = mode, + }; + + *fa = (struct fuse_args) { + .opcode = FUSE_FALLOCATE, + .nodeid = ff->nodeid, + .in_numargs = 1, + .in_args[0].size = sizeof(*ffi), + .in_args[0].value = ffi, + }; + + return 0; +} + +static int fuse_file_fallocate_initialize_out(struct fuse_args *fa, + struct fuse_fallocate_in *ffi, + struct file *file, int mode, loff_t offset, loff_t length) +{ + return 0; +} + +static int fuse_file_fallocate_backing(struct fuse_args *fa, int *out, + struct file *file, int mode, loff_t offset, loff_t length) +{ + const struct fuse_fallocate_in *ffi = fa->in_args[0].value; + struct fuse_file *ff = file->private_data; + + *out = vfs_fallocate(ff->backing_file, ffi->mode, ffi->offset, + ffi->length); + return 0; +} + +static int fuse_file_fallocate_finalize(struct fuse_args *fa, int *out, + struct file *file, int mode, loff_t offset, loff_t length) +{ + return 0; +} + +int fuse_bpf_file_fallocate(int *out, struct inode *inode, struct file *file, int mode, loff_t offset, loff_t length) +{ + return fuse_bpf_backing(inode, struct fuse_fallocate_in, out, + fuse_file_fallocate_initialize_in, + fuse_file_fallocate_initialize_out, + fuse_file_fallocate_backing, + fuse_file_fallocate_finalize, + file, mode, offset, length); +} + /******************************************************************************* * Directory operations after here * ******************************************************************************/ diff --git a/fs/fuse/file.c b/fs/fuse/file.c index e90b3e2d5452..ab3cd43556e0 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -2997,6 +2997,9 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, bool block_faults = FUSE_IS_DAX(inode) && lock_inode; + if (fuse_bpf_file_fallocate(&err, inode, file, mode, offset, length)) + return err; + if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE)) return -EOPNOTSUPP; diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 108c2ea15a49..4351dbc7f10d 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1401,6 +1401,7 @@ int parse_fuse_bpf_entry(struct fuse_bpf_entry *fbe, int num_entries); #ifdef CONFIG_FUSE_BPF int fuse_bpf_lseek(loff_t *out, struct inode *inode, struct file *file, loff_t offset, int whence); +int fuse_bpf_file_fallocate(int *out, struct inode *inode, struct file *file, int mode, loff_t offset, loff_t length); int fuse_bpf_lookup(struct dentry **out, struct inode *dir, struct dentry *entry, unsigned int flags); int fuse_bpf_access(int *out, struct inode *inode, int mask); @@ -1411,6 +1412,11 @@ static inline int fuse_bpf_lseek(loff_t *out, struct inode *inode, struct file * return 0; } +static inline int fuse_bpf_file_fallocate(int *out, struct inode *inode, struct file *file, int mode, loff_t offset, loff_t length) +{ + return 0; +} + static inline int fuse_bpf_lookup(struct dentry **out, struct inode *dir, struct dentry *entry, unsigned int flags) { return 0; From patchwork Tue Nov 22 02:15:26 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13051832 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 8B12DC43217 for ; Tue, 22 Nov 2022 02:18:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232556AbiKVCR6 (ORCPT ); Mon, 21 Nov 2022 21:17:58 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42600 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230123AbiKVCRN (ORCPT ); Mon, 21 Nov 2022 21:17:13 -0500 Received: from mail-yw1-x1149.google.com (mail-yw1-x1149.google.com [IPv6:2607:f8b0:4864:20::1149]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7A191E6766 for ; Mon, 21 Nov 2022 18:16:19 -0800 (PST) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-39967085ae9so75180947b3.11 for ; Mon, 21 Nov 2022 18:16:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=78iV0IOgPJQWyL/mQIMxjEmfCaR2pA5J6aYjL9H4JYo=; b=gHf8zmM74lrP+ge6ba1Qf4CO+Nh8VrJVNTDNaB6851IfJmOnkJ9ucSQHZ88q1KOTd+ scELBI5K48bn/AvOvHx+ifRIumMlSCEwFxJpxi2umJX8mnAjEqaEO6/stVSz4IJufrDX dcFF5dQpT7vbFhruef2o5VdJRcpO8uUmBSSz6S7QfwY0RLpSM6MCa+I2pB3eqEeb3Acp M1J1HugnhLOmrs4z2U67ABsYmbFXtQB+TgX2i34vlhHkAiCWRqVvh+kjjkSlgEC0PiRZ KZadGpwzL6nMOVpKqP1JCEfC84/rhDeQlsu6WgPM9C+S+FP03gX6W3GRxsTIvDham//l h4Lw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=78iV0IOgPJQWyL/mQIMxjEmfCaR2pA5J6aYjL9H4JYo=; b=hHGAF1reaucQoG4j8Sn5Y8UvKP/DAMLV+9sCLhmdQXp3E9AxRFGTFPqT1SFHXFE91O V121oBb6npRgV4i+uv7yOPmgRvOHc9rTq5Sw2n2nnp81FOhb7/a15fy+SdS74jjfFauG J0nBzFKM2oKqy7saLaMd3nbfKJ+soWKxMXEzGd8GDH49bvmnKWz5X/zYGEsTL5fg7wlc Hk0RE2qYYaPy9VsUeeFf8ZNys/XuWMbccdUQ9toy1ndtZ94blzjoFRglgsgd1CPbd7Yo HTKCUsU/4/igmOr++krCIgVscMC/CdiQevlAkTxxuH6ZzT3QmspBXheiuz1PQS4I41MK SUhA== X-Gm-Message-State: ANoB5pmRojXRvameU8IsNtNXBdNmd8K1PEjckaYCX/0wbuj0XDsO/4QO bTxbyDEJrmo44LhVa9XmdD7tTaF2pDc= X-Google-Smtp-Source: AA0mqf7zD7izU7ao8UfvD9Uu+T9/S4ydpSW8lFWddu0/Ffn632J82EExw5VThXRP1csBheW0dVALIvnRBDc= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:200:8539:aadd:13be:6e82]) (user=drosen job=sendgmr) by 2002:a5b:bcd:0:b0:6cf:f46b:c28f with SMTP id c13-20020a5b0bcd000000b006cff46bc28fmr4030875ybr.520.1669083379336; Mon, 21 Nov 2022 18:16:19 -0800 (PST) Date: Mon, 21 Nov 2022 18:15:26 -0800 In-Reply-To: <20221122021536.1629178-1-drosen@google.com> Mime-Version: 1.0 References: <20221122021536.1629178-1-drosen@google.com> X-Mailer: git-send-email 2.38.1.584.g0f3c55d4c2-goog Message-ID: <20221122021536.1629178-12-drosen@google.com> Subject: [RFC PATCH v2 11/21] fuse-bpf: Support file/dir open/close From: Daniel Rosenberg To: Miklos Szeredi Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, bpf@vger.kernel.org, kernel-team@android.com, Daniel Rosenberg , Paul Lawrence Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 356 ++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/dir.c | 8 ++ fs/fuse/file.c | 7 + fs/fuse/fuse_i.h | 26 ++++ 4 files changed, 397 insertions(+) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index 51aadeb1b7dc..c8e95abc04aa 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -205,6 +205,362 @@ static void fuse_stat_to_attr(struct fuse_conn *fc, struct inode *inode, attr->blksize = 1 << blkbits; } +struct fuse_open_io { + struct fuse_open_in foi; + struct fuse_open_out foo; +}; + +static int fuse_open_initialize_in(struct fuse_args *fa, struct fuse_open_io *foio, + struct inode *inode, struct file *file, bool isdir) +{ + foio->foi = (struct fuse_open_in) { + .flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY), + }; + *fa = (struct fuse_args) { + .nodeid = get_fuse_inode(inode)->nodeid, + .opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN, + .in_numargs = 1, + .in_args[0] = (struct fuse_in_arg) { + .size = sizeof(foio->foi), + .value = &foio->foi, + }, + }; + + return 0; +} + +static int fuse_open_initialize_out(struct fuse_args *fa, struct fuse_open_io *foio, + struct inode *inode, struct file *file, bool isdir) +{ + foio->foo = (struct fuse_open_out) { 0 }; + + fa->out_numargs = 1; + fa->out_args[0] = (struct fuse_arg) { + .size = sizeof(foio->foo), + .value = &foio->foo, + }; + + return 0; +} + +static int fuse_open_backing(struct fuse_args *fa, int *out, + struct inode *inode, struct file *file, bool isdir) +{ + struct fuse_mount *fm = get_fuse_mount(inode); + const struct fuse_open_in *foi = fa->in_args[0].value; + struct fuse_file *ff; + int mask; + struct fuse_dentry *fd = get_fuse_dentry(file->f_path.dentry); + struct file *backing_file; + + ff = fuse_file_alloc(fm); + if (!ff) + return -ENOMEM; + file->private_data = ff; + + switch (foi->flags & O_ACCMODE) { + case O_RDONLY: + mask = MAY_READ; + break; + + case O_WRONLY: + mask = MAY_WRITE; + break; + + case O_RDWR: + mask = MAY_READ | MAY_WRITE; + break; + + default: + return -EINVAL; + } + + *out = inode_permission(&init_user_ns, + get_fuse_inode(inode)->backing_inode, mask); + if (*out) + return *out; + + backing_file = + dentry_open(&fd->backing_path, foi->flags, current_cred()); + + if (IS_ERR(backing_file)) { + fuse_file_free(ff); + file->private_data = NULL; + return PTR_ERR(backing_file); + } + ff->backing_file = backing_file; + + *out = 0; + return 0; +} + +static int fuse_open_finalize(struct fuse_args *fa, int *out, + struct inode *inode, struct file *file, bool isdir) +{ + struct fuse_file *ff = file->private_data; + struct fuse_open_out *foo = fa->out_args[0].value; + + if (ff) { + ff->fh = foo->fh; + ff->nodeid = get_fuse_inode(inode)->nodeid; + } + return 0; +} + +int fuse_bpf_open(int *out, struct inode *inode, struct file *file, bool isdir) +{ + return fuse_bpf_backing(inode, struct fuse_open_io, out, + fuse_open_initialize_in, fuse_open_initialize_out, + fuse_open_backing, + fuse_open_finalize, + inode, file, isdir); +} + +struct fuse_create_open_io { + struct fuse_create_in fci; + struct fuse_entry_out feo; + struct fuse_open_out foo; +}; + +static int fuse_create_open_initialize_in(struct fuse_args *fa, struct fuse_create_open_io *fcoio, + struct inode *dir, struct dentry *entry, + struct file *file, unsigned int flags, umode_t mode) +{ + fcoio->fci = (struct fuse_create_in) { + .flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY), + .mode = mode, + }; + + *fa = (struct fuse_args) { + .nodeid = get_node_id(dir), + .opcode = FUSE_CREATE, + .in_numargs = 2, + .in_args[0] = (struct fuse_in_arg) { + .size = sizeof(fcoio->fci), + .value = &fcoio->fci, + }, + .in_args[1] = (struct fuse_in_arg) { + .size = entry->d_name.len + 1, + .value = (void *) entry->d_name.name, + }, + }; + + return 0; +} + +static int fuse_create_open_initialize_out(struct fuse_args *fa, struct fuse_create_open_io *fcoio, + struct inode *dir, struct dentry *entry, + struct file *file, unsigned int flags, umode_t mode) +{ + fcoio->feo = (struct fuse_entry_out) { 0 }; + fcoio->foo = (struct fuse_open_out) { 0 }; + + fa->out_numargs = 2; + fa->out_args[0] = (struct fuse_arg) { + .size = sizeof(fcoio->feo), + .value = &fcoio->feo, + }; + fa->out_args[1] = (struct fuse_arg) { + .size = sizeof(fcoio->foo), + .value = &fcoio->foo, + }; + + return 0; +} + +static int fuse_open_file_backing(struct inode *inode, struct file *file) +{ + struct fuse_mount *fm = get_fuse_mount(inode); + struct dentry *entry = file->f_path.dentry; + struct fuse_dentry *fuse_dentry = get_fuse_dentry(entry); + struct fuse_file *fuse_file; + struct file *backing_file; + + fuse_file = fuse_file_alloc(fm); + if (!fuse_file) + return -ENOMEM; + file->private_data = fuse_file; + + backing_file = dentry_open(&fuse_dentry->backing_path, file->f_flags, + current_cred()); + if (IS_ERR(backing_file)) { + fuse_file_free(fuse_file); + file->private_data = NULL; + return PTR_ERR(backing_file); + } + fuse_file->backing_file = backing_file; + + return 0; +} + +static int fuse_create_open_backing(struct fuse_args *fa, int *out, + struct inode *dir, struct dentry *entry, + struct file *file, unsigned int flags, umode_t mode) +{ + struct fuse_inode *dir_fuse_inode = get_fuse_inode(dir); + struct path backing_path; + struct inode *inode = NULL; + struct dentry *backing_parent; + struct dentry *newent; + const struct fuse_create_in *fci = fa->in_args[0].value; + + get_fuse_backing_path(entry, &backing_path); + if (!backing_path.dentry) + return -EBADF; + + if (IS_ERR(backing_path.dentry)) + return PTR_ERR(backing_path.dentry); + + if (d_really_is_positive(backing_path.dentry)) { + *out = -EIO; + goto out; + } + + backing_parent = dget_parent(backing_path.dentry); + inode_lock_nested(dir_fuse_inode->backing_inode, I_MUTEX_PARENT); + *out = vfs_create(&init_user_ns, d_inode(backing_parent), + backing_path.dentry, fci->mode, true); + inode_unlock(d_inode(backing_parent)); + dput(backing_parent); + if (*out) + goto out; + + inode = fuse_iget_backing(dir->i_sb, 0, backing_path.dentry->d_inode); + if (IS_ERR(inode)) { + *out = PTR_ERR(inode); + goto out; + } + + newent = d_splice_alias(inode, entry); + if (IS_ERR(newent)) { + *out = PTR_ERR(newent); + goto out; + } + + entry = newent ? newent : entry; + *out = finish_open(file, entry, fuse_open_file_backing); + +out: + path_put(&backing_path); + return *out; +} + +static int fuse_create_open_finalize(struct fuse_args *fa, int *out, + struct inode *dir, struct dentry *entry, + struct file *file, unsigned int flags, umode_t mode) +{ + struct fuse_file *ff = file->private_data; + struct fuse_inode *fi = get_fuse_inode(file->f_inode); + struct fuse_entry_out *feo = fa->out_args[0].value; + struct fuse_open_out *foo = fa->out_args[1].value; + + if (fi) + fi->nodeid = feo->nodeid; + if (ff) + ff->fh = foo->fh; + return 0; +} + +int fuse_bpf_create_open(int *out, struct inode *dir, struct dentry *entry, + struct file *file, unsigned int flags, umode_t mode) +{ + return fuse_bpf_backing(dir, struct fuse_create_open_io, out, + fuse_create_open_initialize_in, + fuse_create_open_initialize_out, + fuse_create_open_backing, + fuse_create_open_finalize, + dir, entry, file, flags, mode); +} + +static int fuse_release_initialize_in(struct fuse_args *fa, struct fuse_release_in *fri, + struct inode *inode, struct file *file) +{ + struct fuse_file *fuse_file = file->private_data; + + /* Always put backing file whatever bpf/userspace says */ + fput(fuse_file->backing_file); + + *fri = (struct fuse_release_in) { + .fh = ((struct fuse_file *)(file->private_data))->fh, + }; + + *fa = (struct fuse_args) { + .nodeid = get_fuse_inode(inode)->nodeid, + .opcode = FUSE_RELEASE, + .in_numargs = 1, + .in_args[0].size = sizeof(*fri), + .in_args[0].value = fri, + }; + + return 0; +} + +static int fuse_release_initialize_out(struct fuse_args *fa, struct fuse_release_in *fri, + struct inode *inode, struct file *file) +{ + return 0; +} + +static int fuse_releasedir_initialize_in(struct fuse_args *fa, + struct fuse_release_in *fri, + struct inode *inode, struct file *file) +{ + struct fuse_file *fuse_file = file->private_data; + + /* Always put backing file whatever bpf/userspace says */ + fput(fuse_file->backing_file); + + *fri = (struct fuse_release_in) { + .fh = ((struct fuse_file *)(file->private_data))->fh, + }; + + *fa = (struct fuse_args) { + .nodeid = get_fuse_inode(inode)->nodeid, + .opcode = FUSE_RELEASEDIR, + .in_numargs = 1, + .in_args[0].size = sizeof(*fri), + .in_args[0].value = fri, + }; + + return 0; +} + +static int fuse_releasedir_initialize_out(struct fuse_args *fa, + struct fuse_release_in *fri, + struct inode *inode, struct file *file) +{ + return 0; +} + +static int fuse_release_backing(struct fuse_args *fa, int *out, + struct inode *inode, struct file *file) +{ + return 0; +} + +static int fuse_release_finalize(struct fuse_args *fa, int *out, + struct inode *inode, struct file *file) +{ + fuse_file_free(file->private_data); + *out = 0; + return 0; +} + +int fuse_bpf_release(int *out, struct inode *inode, struct file *file) +{ + return fuse_bpf_backing(inode, struct fuse_release_in, out, + fuse_release_initialize_in, fuse_release_initialize_out, + fuse_release_backing, fuse_release_finalize, + inode, file); +} + +int fuse_bpf_releasedir(int *out, struct inode *inode, struct file *file) +{ + return fuse_bpf_backing(inode, struct fuse_release_in, out, + fuse_releasedir_initialize_in, fuse_releasedir_initialize_out, + fuse_release_backing, fuse_release_finalize, inode, file); +} + struct fuse_lseek_io { struct fuse_lseek_in fli; struct fuse_lseek_out flo; diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 4e19320889ed..e330a6af9ee7 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -635,6 +635,9 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, /* Userspace expects S_IFREG in create mode */ BUG_ON((mode & S_IFMT) != S_IFREG); + if (fuse_bpf_create_open(&err, dir, entry, file, flags, mode)) + return err; + forget = fuse_alloc_forget(); err = -ENOMEM; if (!forget) @@ -1554,6 +1557,11 @@ static int fuse_dir_open(struct inode *inode, struct file *file) static int fuse_dir_release(struct inode *inode, struct file *file) { + int err = 0; + + if (fuse_bpf_releasedir(&err, inode, file)) + return err; + fuse_release_common(file, true); return 0; diff --git a/fs/fuse/file.c b/fs/fuse/file.c index ab3cd43556e0..70a5bd5403ca 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -241,6 +241,9 @@ int fuse_open_common(struct inode *inode, struct file *file, bool isdir) if (err) return err; + if (fuse_bpf_open(&err, inode, file, isdir)) + return err; + if (is_wb_truncate || dax_truncate) inode_lock(inode); @@ -349,6 +352,10 @@ static int fuse_open(struct inode *inode, struct file *file) static int fuse_release(struct inode *inode, struct file *file) { struct fuse_conn *fc = get_fuse_conn(inode); + int err; + + if (fuse_bpf_release(&err, inode, file)) + return err; /* * Dirty pages might remain despite write_inode_now() call from diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 4351dbc7f10d..794b1a06079c 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1400,6 +1400,11 @@ int parse_fuse_bpf_entry(struct fuse_bpf_entry *fbe, int num_entries); #ifdef CONFIG_FUSE_BPF +int fuse_bpf_open(int *err, struct inode *inode, struct file *file, bool isdir); +int fuse_bpf_create_open(int *out, struct inode *dir, struct dentry *entry, + struct file *file, unsigned int flags, umode_t mode); +int fuse_bpf_release(int *out, struct inode *inode, struct file *file); +int fuse_bpf_releasedir(int *out, struct inode *inode, struct file *file); int fuse_bpf_lseek(loff_t *out, struct inode *inode, struct file *file, loff_t offset, int whence); int fuse_bpf_file_fallocate(int *out, struct inode *inode, struct file *file, int mode, loff_t offset, loff_t length); int fuse_bpf_lookup(struct dentry **out, struct inode *dir, struct dentry *entry, unsigned int flags); @@ -1407,6 +1412,27 @@ int fuse_bpf_access(int *out, struct inode *inode, int mask); #else +static inline int fuse_bpf_open(int *err, struct inode *inode, struct file *file, bool isdir) +{ + return 0; +} + +static inline int fuse_bpf_create_open(int *out, struct inode *dir, struct dentry *entry, + struct file *file, unsigned int flags, umode_t mode) +{ + return 0; +} + +static inline int fuse_bpf_release(int *out, struct inode *inode, struct file *file) +{ + return 0; +} + +static inline int fuse_bpf_releasedir(int *out, struct inode *inode, struct file *file) +{ + return 0; +} + static inline int fuse_bpf_lseek(loff_t *out, struct inode *inode, struct file *file, loff_t offset, int whence) { return 0; From patchwork Tue Nov 22 02:15:27 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13051833 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 E42C8C4332F for ; Tue, 22 Nov 2022 02:18:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232572AbiKVCSN (ORCPT ); Mon, 21 Nov 2022 21:18:13 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42630 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232617AbiKVCRO (ORCPT ); Mon, 21 Nov 2022 21:17:14 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 65E51E6778 for ; Mon, 21 Nov 2022 18:16:22 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id t9-20020a5b03c9000000b006cff5077dc9so12439428ybp.3 for ; Mon, 21 Nov 2022 18:16:22 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=ydyHUSUVRLNupkLXr6P8WzdsU341BmaDDMAW+LRksTY=; b=k+KyxhZMTRllx3lQFyjWbf+28UVtsTsFqm5n+/aW2I9WhizNoXiOdaaQkDKJd5Rj0u hO64pYowvIgPhLWK5B50w4OIjBXWqMsmXMsK3BpMofw9xupyoMj8iGDFjKiS5ZLGtIQM vf2CqSanuhwMpqzaA6j2GmjhW+C6WXaqn+aCrvwrJyf+Bg+VKdVJX3zH/5z95QjOmAYR 6oG/oLULfWKPkw2tcp6VjKDSHdn66CHYe0CqwzBwuBYF2/GVTIHJUuPWJD7jLAuH3wlr DX4zRU8SXoKcbpHwtFdX+EOOV3uwqJ+eie1fI7Ta+5aREznvRvC2rn3Gi6vz7/pJw9gI dVYA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=ydyHUSUVRLNupkLXr6P8WzdsU341BmaDDMAW+LRksTY=; b=to3bh4re0oHBKzbn+73/cYgMkYQcl1QUNiQegv3vhVoIFCrLaEkyARP/mJIS9yfZR+ w7DXYAGoleZ2YTnyQ/Z78ZtXk1lzwwJ491A1ue0pHfgexY4rBtD7aO69DX5alSzMcrl3 Pjbk9FJOXgCXhFFOQKKK/N6hTCA205de7K8FrF8G7xLLK+qUQsGH+vj6npZrylAVGD3/ j4WgtMGjwVjhGeOPdJ/0uEW+MKceC6CDGdsfzIe0Pj/3Q7gdXDkkuyJRJKxlWgfr8x3r Hifb3JM8FZkyoEFawFEZpg2K6qQ+nmFoPflA78KlJiUDs1KUghTRUTsxtWHYvHRak029 ENZg== X-Gm-Message-State: ANoB5pm6gALL7D0Wj6fAYJR9L9eQS0quZwn91HlCxHlu3DGXOQIJI99S 8AKXJYJH0llBERGSvefyBner9ruWy8w= X-Google-Smtp-Source: AA0mqf6RY+vOUOe+eEVuThyyVpCLoTK7JOhwb/Ik9aUtjrjQNd+3UCndd6B+au4Ss8OmcBoTTY5ZlCo+/Nc= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:200:8539:aadd:13be:6e82]) (user=drosen job=sendgmr) by 2002:a0d:ea96:0:b0:3a6:4f96:2639 with SMTP id t144-20020a0dea96000000b003a64f962639mr1916559ywe.440.1669083381448; Mon, 21 Nov 2022 18:16:21 -0800 (PST) Date: Mon, 21 Nov 2022 18:15:27 -0800 In-Reply-To: <20221122021536.1629178-1-drosen@google.com> Mime-Version: 1.0 References: <20221122021536.1629178-1-drosen@google.com> X-Mailer: git-send-email 2.38.1.584.g0f3c55d4c2-goog Message-ID: <20221122021536.1629178-13-drosen@google.com> Subject: [RFC PATCH v2 12/21] fuse-bpf: Support mknod/unlink/mkdir/rmdir From: Daniel Rosenberg To: Miklos Szeredi Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, bpf@vger.kernel.org, kernel-team@android.com, Daniel Rosenberg , Paul Lawrence Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 306 ++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/dir.c | 14 +++ fs/fuse/fuse_i.h | 24 ++++ 3 files changed, 344 insertions(+) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index c8e95abc04aa..a7505d6887e0 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -205,6 +205,13 @@ static void fuse_stat_to_attr(struct fuse_conn *fc, struct inode *inode, attr->blksize = 1 << blkbits; } +/* + * Unused io passed to fuse_bpf_backing when io operation needs no scratch space + */ +struct fuse_unused_io { + int unused; +}; + struct fuse_open_io { struct fuse_open_in foi; struct fuse_open_out foo; @@ -930,6 +937,305 @@ int fuse_revalidate_backing(struct dentry *entry, unsigned int flags) return 1; } +static int fuse_mknod_initialize_in(struct fuse_args *fa, struct fuse_mknod_in *fmi, + struct inode *dir, struct dentry *entry, umode_t mode, dev_t rdev) +{ + *fmi = (struct fuse_mknod_in) { + .mode = mode, + .rdev = new_encode_dev(rdev), + .umask = current_umask(), + }; + *fa = (struct fuse_args) { + .nodeid = get_node_id(dir), + .opcode = FUSE_MKNOD, + .in_numargs = 2, + .in_args[0] = (struct fuse_in_arg) { + .size = sizeof(*fmi), + .value = fmi, + }, + .in_args[1] = (struct fuse_in_arg) { + .size = entry->d_name.len + 1, + .value = (void *) entry->d_name.name, + }, + }; + + return 0; +} + +static int fuse_mknod_initialize_out(struct fuse_args *fa, struct fuse_mknod_in *fmi, + struct inode *dir, struct dentry *entry, umode_t mode, dev_t rdev) +{ + return 0; +} + +static int fuse_mknod_backing(struct fuse_args *fa, int *out, + struct inode *dir, struct dentry *entry, umode_t mode, dev_t rdev) +{ + const struct fuse_mknod_in *fmi = fa->in_args[0].value; + struct fuse_inode *fuse_inode = get_fuse_inode(dir); + struct inode *backing_inode = fuse_inode->backing_inode; + struct path backing_path; + struct inode *inode = NULL; + + get_fuse_backing_path(entry, &backing_path); + if (!backing_path.dentry) + return -EBADF; + + inode_lock_nested(backing_inode, I_MUTEX_PARENT); + mode = fmi->mode; + if (!IS_POSIXACL(backing_inode)) + mode &= ~fmi->umask; + *out = vfs_mknod(&init_user_ns, backing_inode, backing_path.dentry, mode, + new_decode_dev(fmi->rdev)); + inode_unlock(backing_inode); + if (*out) + goto out; + if (d_really_is_negative(backing_path.dentry) || + unlikely(d_unhashed(backing_path.dentry))) { + *out = -EINVAL; + /** + * TODO: overlayfs responds to this situation with a + * lookupOneLen. Should we do that too? + */ + goto out; + } + inode = fuse_iget_backing(dir->i_sb, fuse_inode->nodeid, backing_inode); + if (IS_ERR(inode)) { + *out = PTR_ERR(inode); + goto out; + } + d_instantiate(entry, inode); +out: + path_put(&backing_path); + return *out; +} + +static int fuse_mknod_finalize(struct fuse_args *fa, int *out, + struct inode *dir, struct dentry *entry, umode_t mode, dev_t rdev) +{ + return 0; +} + +int fuse_bpf_mknod(int *out, struct inode *dir, struct dentry *entry, umode_t mode, dev_t rdev) +{ + return fuse_bpf_backing(dir, struct fuse_mknod_in, out, + fuse_mknod_initialize_in, fuse_mknod_initialize_out, + fuse_mknod_backing, fuse_mknod_finalize, + dir, entry, mode, rdev); +} + +static int fuse_mkdir_initialize_in(struct fuse_args *fa, struct fuse_mkdir_in *fmi, + struct inode *dir, struct dentry *entry, umode_t mode) +{ + *fmi = (struct fuse_mkdir_in) { + .mode = mode, + .umask = current_umask(), + }; + *fa = (struct fuse_args) { + .nodeid = get_node_id(dir), + .opcode = FUSE_MKDIR, + .in_numargs = 2, + .in_args[0] = (struct fuse_in_arg) { + .size = sizeof(*fmi), + .value = fmi, + }, + .in_args[1] = (struct fuse_in_arg) { + .size = entry->d_name.len + 1, + .value = (void *) entry->d_name.name, + }, + }; + + return 0; +} + +static int fuse_mkdir_initialize_out(struct fuse_args *fa, struct fuse_mkdir_in *fmi, + struct inode *dir, struct dentry *entry, umode_t mode) +{ + return 0; +} + +static int fuse_mkdir_backing(struct fuse_args *fa, int *out, + struct inode *dir, struct dentry *entry, umode_t mode) +{ + const struct fuse_mkdir_in *fmi = fa->in_args[0].value; + struct fuse_inode *fuse_inode = get_fuse_inode(dir); + struct inode *backing_inode = fuse_inode->backing_inode; + struct path backing_path; + struct inode *inode = NULL; + struct dentry *d; + + get_fuse_backing_path(entry, &backing_path); + if (!backing_path.dentry) + return -EBADF; + + inode_lock_nested(backing_inode, I_MUTEX_PARENT); + mode = fmi->mode; + if (!IS_POSIXACL(backing_inode)) + mode &= ~fmi->umask; + *out = vfs_mkdir(&init_user_ns, backing_inode, backing_path.dentry, + mode); + if (*out) + goto out; + if (d_really_is_negative(backing_path.dentry) || + unlikely(d_unhashed(backing_path.dentry))) { + d = lookup_one_len(entry->d_name.name, + backing_path.dentry->d_parent, + entry->d_name.len); + if (IS_ERR(d)) { + *out = PTR_ERR(d); + goto out; + } + dput(backing_path.dentry); + backing_path.dentry = d; + } + inode = fuse_iget_backing(dir->i_sb, fuse_inode->nodeid, backing_inode); + if (IS_ERR(inode)) { + *out = PTR_ERR(inode); + goto out; + } + d_instantiate(entry, inode); +out: + inode_unlock(backing_inode); + path_put(&backing_path); + return *out; +} + +static int fuse_mkdir_finalize(struct fuse_args *fa, int *out, + struct inode *dir, struct dentry *entry, umode_t mode) +{ + return 0; +} + +int fuse_bpf_mkdir(int *out, struct inode *dir, struct dentry *entry, umode_t mode) +{ + return fuse_bpf_backing(dir, struct fuse_mkdir_in, out, + fuse_mkdir_initialize_in, fuse_mkdir_initialize_out, + fuse_mkdir_backing, fuse_mkdir_finalize, + dir, entry, mode); +} + +static int fuse_rmdir_initialize_in(struct fuse_args *fa, struct fuse_unused_io *unused, + struct inode *dir, struct dentry *entry) +{ + *fa = (struct fuse_args) { + .nodeid = get_node_id(dir), + .opcode = FUSE_RMDIR, + .in_numargs = 1, + .in_args[0] = (struct fuse_in_arg) { + .size = entry->d_name.len + 1, + .value = (void *) entry->d_name.name, + }, + }; + + return 0; +} + +static int fuse_rmdir_initialize_out(struct fuse_args *fa, struct fuse_unused_io *unused, + struct inode *dir, struct dentry *entry) +{ + return 0; +} + +static int fuse_rmdir_backing(struct fuse_args *fa, int *out, + struct inode *dir, struct dentry *entry) +{ + struct path backing_path; + struct dentry *backing_parent_dentry; + struct inode *backing_inode; + + get_fuse_backing_path(entry, &backing_path); + if (!backing_path.dentry) + return -EBADF; + + backing_parent_dentry = dget_parent(backing_path.dentry); + backing_inode = d_inode(backing_parent_dentry); + + inode_lock_nested(backing_inode, I_MUTEX_PARENT); + *out = vfs_rmdir(&init_user_ns, backing_inode, backing_path.dentry); + inode_unlock(backing_inode); + + dput(backing_parent_dentry); + if (!*out) + d_drop(entry); + path_put(&backing_path); + return *out; +} + +static int fuse_rmdir_finalize(struct fuse_args *fa, int *out, struct inode *dir, struct dentry *entry) +{ + return 0; +} + +int fuse_bpf_rmdir(int *out, struct inode *dir, struct dentry *entry) +{ + return fuse_bpf_backing(dir, struct fuse_unused_io, out, + fuse_rmdir_initialize_in, fuse_rmdir_initialize_out, + fuse_rmdir_backing, fuse_rmdir_finalize, + dir, entry); +} + +static int fuse_unlink_initialize_in(struct fuse_args *fa, struct fuse_unused_io *unused, + struct inode *dir, struct dentry *entry) +{ + *fa = (struct fuse_args) { + .nodeid = get_node_id(dir), + .opcode = FUSE_UNLINK, + .in_numargs = 1, + .in_args[0] = (struct fuse_in_arg) { + .size = entry->d_name.len + 1, + .value = (void *) entry->d_name.name, + }, + }; + + return 0; +} + +static int fuse_unlink_initialize_out(struct fuse_args *fa, struct fuse_unused_io *unused, + struct inode *dir, struct dentry *entry) +{ + return 0; +} + +static int fuse_unlink_backing(struct fuse_args *fa, int *out, struct inode *dir, struct dentry *entry) +{ + struct path backing_path; + struct dentry *backing_parent_dentry; + struct inode *backing_inode; + + get_fuse_backing_path(entry, &backing_path); + if (!backing_path.dentry) + return -EBADF; + + /* TODO Not sure if we should reverify like overlayfs, or get inode from d_parent */ + backing_parent_dentry = dget_parent(backing_path.dentry); + backing_inode = d_inode(backing_parent_dentry); + + inode_lock_nested(backing_inode, I_MUTEX_PARENT); + *out = vfs_unlink(&init_user_ns, backing_inode, backing_path.dentry, + NULL); + inode_unlock(backing_inode); + + dput(backing_parent_dentry); + if (!*out) + d_drop(entry); + path_put(&backing_path); + return *out; +} + +static int fuse_unlink_finalize(struct fuse_args *fa, int *out, + struct inode *dir, struct dentry *entry) +{ + return 0; +} + +int fuse_bpf_unlink(int *out, struct inode *dir, struct dentry *entry) +{ + return fuse_bpf_backing(dir, struct fuse_unused_io, out, + fuse_unlink_initialize_in, fuse_unlink_initialize_out, + fuse_unlink_backing, fuse_unlink_finalize, + dir, entry); +} + static int fuse_access_initialize_in(struct fuse_args *fa, struct fuse_access_in *fai, struct inode *inode, int mask) { diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index e330a6af9ee7..729a0348fa01 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -869,6 +869,10 @@ static int fuse_mknod(struct user_namespace *mnt_userns, struct inode *dir, struct fuse_mknod_in inarg; struct fuse_mount *fm = get_fuse_mount(dir); FUSE_ARGS(args); + int err; + + if (fuse_bpf_mknod(&err, dir, entry, mode, rdev)) + return err; if (!fm->fc->dont_mask) mode &= ~current_umask(); @@ -915,6 +919,10 @@ static int fuse_mkdir(struct user_namespace *mnt_userns, struct inode *dir, struct fuse_mkdir_in inarg; struct fuse_mount *fm = get_fuse_mount(dir); FUSE_ARGS(args); + int err; + + if (fuse_bpf_mkdir(&err, dir, entry, mode)) + return err; if (!fm->fc->dont_mask) mode &= ~current_umask(); @@ -1001,6 +1009,9 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry) if (fuse_is_bad(dir)) return -EIO; + if (fuse_bpf_unlink(&err, dir, entry)) + return err; + args.opcode = FUSE_UNLINK; args.nodeid = get_node_id(dir); args.in_numargs = 1; @@ -1024,6 +1035,9 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry) if (fuse_is_bad(dir)) return -EIO; + if (fuse_bpf_rmdir(&err, dir, entry)) + return err; + args.opcode = FUSE_RMDIR; args.nodeid = get_node_id(dir); args.in_numargs = 1; diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 794b1a06079c..dc5bba2a75ab 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1403,6 +1403,10 @@ int parse_fuse_bpf_entry(struct fuse_bpf_entry *fbe, int num_entries); int fuse_bpf_open(int *err, struct inode *inode, struct file *file, bool isdir); int fuse_bpf_create_open(int *out, struct inode *dir, struct dentry *entry, struct file *file, unsigned int flags, umode_t mode); +int fuse_bpf_mknod(int *out, struct inode *dir, struct dentry *entry, umode_t mode, dev_t rdev); +int fuse_bpf_mkdir(int *out, struct inode *dir, struct dentry *entry, umode_t mode); +int fuse_bpf_rmdir(int *out, struct inode *dir, struct dentry *entry); +int fuse_bpf_unlink(int *out, struct inode *dir, struct dentry *entry); int fuse_bpf_release(int *out, struct inode *inode, struct file *file); int fuse_bpf_releasedir(int *out, struct inode *inode, struct file *file); int fuse_bpf_lseek(loff_t *out, struct inode *inode, struct file *file, loff_t offset, int whence); @@ -1423,6 +1427,26 @@ static inline int fuse_bpf_create_open(int *out, struct inode *dir, struct dentr return 0; } +static inline int fuse_bpf_mknod(int *out, struct inode *dir, struct dentry *entry, umode_t mode, dev_t rdev) +{ + return 0; +} + +static inline int fuse_bpf_mkdir(int *out, struct inode *dir, struct dentry *entry, umode_t mode) +{ + return 0; +} + +static inline int fuse_bpf_rmdir(int *out, struct inode *dir, struct dentry *entry) +{ + return 0; +} + +static inline int fuse_bpf_unlink(int *out, struct inode *dir, struct dentry *entry) +{ + return 0; +} + static inline int fuse_bpf_release(int *out, struct inode *inode, struct file *file) { return 0; From patchwork Tue Nov 22 02:15:28 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13051834 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 93B13C43217 for ; Tue, 22 Nov 2022 02:18:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232648AbiKVCSZ (ORCPT ); Mon, 21 Nov 2022 21:18:25 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42848 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231325AbiKVCRg (ORCPT ); Mon, 21 Nov 2022 21:17:36 -0500 Received: from mail-yb1-xb4a.google.com (mail-yb1-xb4a.google.com [IPv6:2607:f8b0:4864:20::b4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5E126E6EEB for ; Mon, 21 Nov 2022 18:16:24 -0800 (PST) Received: by mail-yb1-xb4a.google.com with SMTP id n141-20020a254093000000b006eacc13c38eso5736849yba.0 for ; Mon, 21 Nov 2022 18:16:24 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=mZmovE66ZdqhOmBbykw9B8nSGupb/ebR6gSpmGb5LGI=; b=o7bbni3nAcZylijRv69Vs5PD7bchjW2PbDqhDwTIf/E1XkYKIhh8XNGNi/YPCo0HZF pNkTB/LJmEZ1oIf8pbwqXf1yAqIu1gby/ZQSrozaSQFqMLUNSQ+G4wPg2+s1okCgHcpI rCdLKMiB+OugLqqK1dHnnlANqrm/izNKItafjIuJM84hklmpSFFZ0tGtA1F95XtRip9p 0xy1xmnk9oFyZrX0QZB5Z/1zJsrsFzwy1XXnJge9o59l+jdFndAI/TDt2xseMgc30VLg gl1+UHW8205lO3C4cSBQFSH8IuXm8TsTbL4nFlsnurGz1y19fyCqMnlIbqZ4Hlvv83V+ J/MA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=mZmovE66ZdqhOmBbykw9B8nSGupb/ebR6gSpmGb5LGI=; b=iPbYoc7bbryfsdqdgO75ZHIZYGOtHrNHlLcyQIPpx2cQRN13CSgJMONE5ccL4All1D YJugynHV7xyZPVRAWyHX7e+RZW/VUtGlnuR60w+zABENcxiIHadSTP1G8hhMBJpXUOyD RKy8Gk4GIGZwyvv9TN8S5vUJL4AL5mnN5UmRnh+MXAroBLDl4ofQlgJHLU0gJji/auyJ spf5RbHc8x8w7HLlo+NhKImhlI3nqyLKiPwtxlV0TCDv+2dNcRiTQwoZY6twVzQWjTLG yxdQdpJ86hMWnrUWOy9X0/jG5cnKt4DrIMSwwGteniNpTT/shoX6/SfUIewSSrf/tcSb sdhA== X-Gm-Message-State: ANoB5plrSJOrT8qCuJbZzCj2hKovu4mEEd+qahaCzKha4TFrNxSgb/qh ZF5+F3b8ccVHdCpS6sMUSltHjY4j1n8= X-Google-Smtp-Source: AA0mqf6qA2TG/uVflDd0veqW/Cs6jYwHv7pDIhvA1ecR1WdwREWm6rcSIV+IaIpdMk2s/nh3ZLJU6vjVKSk= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:200:8539:aadd:13be:6e82]) (user=drosen job=sendgmr) by 2002:a05:6902:50c:b0:6dd:df31:a7a2 with SMTP id x12-20020a056902050c00b006dddf31a7a2mr19543665ybs.635.1669083383456; Mon, 21 Nov 2022 18:16:23 -0800 (PST) Date: Mon, 21 Nov 2022 18:15:28 -0800 In-Reply-To: <20221122021536.1629178-1-drosen@google.com> Mime-Version: 1.0 References: <20221122021536.1629178-1-drosen@google.com> X-Mailer: git-send-email 2.38.1.584.g0f3c55d4c2-goog Message-ID: <20221122021536.1629178-14-drosen@google.com> Subject: [RFC PATCH v2 13/21] fuse-bpf: Add support for read/write iter From: Daniel Rosenberg To: Miklos Szeredi Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, bpf@vger.kernel.org, kernel-team@android.com, Daniel Rosenberg , Paul Lawrence Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC This includes adjustments from Amir Goldstein's patch to FUSE Passthrough Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 377 ++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/control.c | 2 +- fs/fuse/file.c | 8 + fs/fuse/fuse_i.h | 19 ++- fs/fuse/inode.c | 13 ++ 5 files changed, 417 insertions(+), 2 deletions(-) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index a7505d6887e0..425815d7f5dc 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -10,6 +10,7 @@ #include #include #include +#include /* * expression statement to wrap the backing filter logic @@ -74,6 +75,89 @@ handled; \ }) +#define FUSE_BPF_IOCB_MASK (IOCB_APPEND | IOCB_DSYNC | IOCB_HIPRI | IOCB_NOWAIT | IOCB_SYNC) + +struct fuse_bpf_aio_req { + struct kiocb iocb; + refcount_t ref; + struct kiocb *iocb_orig; + struct timespec64 pre_atime; +}; + +static struct kmem_cache *fuse_bpf_aio_request_cachep; + +static void fuse_file_accessed(struct file *dst_file, struct file *src_file) +{ + struct inode *dst_inode; + struct inode *src_inode; + + if (dst_file->f_flags & O_NOATIME) + return; + + dst_inode = file_inode(dst_file); + src_inode = file_inode(src_file); + + if ((!timespec64_equal(&dst_inode->i_mtime, &src_inode->i_mtime) || + !timespec64_equal(&dst_inode->i_ctime, &src_inode->i_ctime))) { + dst_inode->i_mtime = src_inode->i_mtime; + dst_inode->i_ctime = src_inode->i_ctime; + } + + touch_atime(&dst_file->f_path); +} + +static void fuse_copyattr(struct file *dst_file, struct file *src_file) +{ + struct inode *dst = file_inode(dst_file); + struct inode *src = file_inode(src_file); + + dst->i_atime = src->i_atime; + dst->i_mtime = src->i_mtime; + dst->i_ctime = src->i_ctime; + i_size_write(dst, i_size_read(src)); + fuse_invalidate_attr(dst); +} + +static void fuse_file_start_write(struct file *fuse_file, struct file *backing_file, + loff_t pos, size_t count) +{ + struct inode *inode = file_inode(fuse_file); + struct fuse_inode *fi = get_fuse_inode(inode); + + if (inode->i_size < pos + count) + set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); + + file_start_write(backing_file); +} + +static void fuse_file_end_write(struct file *fuse_file, struct file *backing_file, + loff_t pos, size_t res) +{ + struct inode *inode = file_inode(fuse_file); + struct fuse_inode *fi = get_fuse_inode(inode); + + file_end_write(backing_file); + + if (res > 0) + fuse_write_update_attr(inode, pos, res); + + clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); + fuse_invalidate_attr(inode); +} + +static void fuse_file_start_read(struct file *backing_file, struct timespec64 *pre_atime) +{ + *pre_atime = file_inode(backing_file)->i_atime; +} + +static void fuse_file_end_read(struct file *fuse_file, struct file *backing_file, + struct timespec64 *pre_atime) +{ + /* Mimic atime update policy of passthrough inode, not the value */ + if (!timespec64_equal(&file_inode(backing_file)->i_atime, pre_atime)) + fuse_invalidate_atime(file_inode(fuse_file)); +} + static void fuse_get_backing_path(struct file *file, struct path *path) { path_get(&file->f_path); @@ -656,6 +740,283 @@ int fuse_bpf_lseek(loff_t *out, struct inode *inode, struct file *file, loff_t o file, offset, whence); } +static inline void fuse_bpf_aio_put(struct fuse_bpf_aio_req *aio_req) +{ + if (refcount_dec_and_test(&aio_req->ref)) + kmem_cache_free(fuse_bpf_aio_request_cachep, aio_req); +} + +static void fuse_bpf_aio_cleanup_handler(struct fuse_bpf_aio_req *aio_req, long res) +{ + struct kiocb *iocb = &aio_req->iocb; + struct kiocb *iocb_orig = aio_req->iocb_orig; + struct file *filp = iocb->ki_filp; + struct file *fuse_filp = iocb_orig->ki_filp; + + if (iocb->ki_flags & IOCB_WRITE) { + __sb_writers_acquired(file_inode(iocb->ki_filp)->i_sb, + SB_FREEZE_WRITE); + fuse_file_end_write(iocb_orig->ki_filp, iocb->ki_filp, iocb->ki_pos, res); + } else { + fuse_file_end_read(fuse_filp, filp, &aio_req->pre_atime); + } + iocb_orig->ki_pos = iocb->ki_pos; + fuse_bpf_aio_put(aio_req); +} + +static void fuse_bpf_aio_rw_complete(struct kiocb *iocb, long res) +{ + struct fuse_bpf_aio_req *aio_req = + container_of(iocb, struct fuse_bpf_aio_req, iocb); + struct kiocb *iocb_orig = aio_req->iocb_orig; + + fuse_bpf_aio_cleanup_handler(aio_req, res); + iocb_orig->ki_complete(iocb_orig, res); +} + +struct fuse_read_iter_out { + uint64_t ret; +}; +struct fuse_file_read_iter_io { + struct fuse_read_in fri; + struct fuse_read_iter_out frio; +}; + +static int fuse_file_read_iter_initialize_in(struct fuse_args *fa, struct fuse_file_read_iter_io *fri, + struct kiocb *iocb, struct iov_iter *to) +{ + struct file *file = iocb->ki_filp; + struct fuse_file *ff = file->private_data; + + fri->fri = (struct fuse_read_in) { + .fh = ff->fh, + .offset = iocb->ki_pos, + .size = to->count, + }; + + /* TODO we can't assume 'to' is a kvec */ + /* TODO we also can't assume the vector has only one component */ + *fa = (struct fuse_args) { + .opcode = FUSE_READ, + .nodeid = ff->nodeid, + .in_numargs = 1, + .in_args[0].size = sizeof(fri->fri), + .in_args[0].value = &fri->fri, + /* + * TODO Design this properly. + * Possible approach: do not pass buf to bpf + * If going to userland, do a deep copy + * For extra credit, do that to/from the vector, rather than + * making an extra copy in the kernel + */ + }; + + return 0; +} + +static int fuse_file_read_iter_initialize_out(struct fuse_args *fa, struct fuse_file_read_iter_io *fri, + struct kiocb *iocb, struct iov_iter *to) +{ + fri->frio = (struct fuse_read_iter_out) { + .ret = fri->fri.size, + }; + + fa->out_numargs = 1; + fa->out_args[0].size = sizeof(fri->frio); + fa->out_args[0].value = &fri->frio; + + return 0; +} + +static int fuse_file_read_iter_backing(struct fuse_args *fa, ssize_t *out, + struct kiocb *iocb, struct iov_iter *to) +{ + struct fuse_read_iter_out *frio = fa->out_args[0].value; + struct file *file = iocb->ki_filp; + struct fuse_file *ff = file->private_data; + + if (!iov_iter_count(to)) + return 0; + + if ((iocb->ki_flags & IOCB_DIRECT) && + (!ff->backing_file->f_mapping->a_ops || + !ff->backing_file->f_mapping->a_ops->direct_IO)) + return -EINVAL; + + /* TODO This just plain ignores any change to fuse_read_in */ + if (is_sync_kiocb(iocb)) { + struct timespec64 pre_atime; + + fuse_file_start_read(ff->backing_file, &pre_atime); + *out = vfs_iter_read(ff->backing_file, to, &iocb->ki_pos, + iocb_to_rw_flags(iocb->ki_flags, FUSE_BPF_IOCB_MASK)); + fuse_file_end_read(file, ff->backing_file, &pre_atime); + } else { + struct fuse_bpf_aio_req *aio_req; + + *out = -ENOMEM; + aio_req = kmem_cache_zalloc(fuse_bpf_aio_request_cachep, GFP_KERNEL); + if (!aio_req) + goto out; + + aio_req->iocb_orig = iocb; + fuse_file_start_read(ff->backing_file, &aio_req->pre_atime); + kiocb_clone(&aio_req->iocb, iocb, ff->backing_file); + aio_req->iocb.ki_complete = fuse_bpf_aio_rw_complete; + refcount_set(&aio_req->ref, 2); + *out = vfs_iocb_iter_read(ff->backing_file, &aio_req->iocb, to); + fuse_bpf_aio_put(aio_req); + if (*out != -EIOCBQUEUED) + fuse_bpf_aio_cleanup_handler(aio_req, *out); + } + + frio->ret = *out; + + /* TODO Need to point value at the buffer for post-modification */ + +out: + fuse_file_accessed(file, ff->backing_file); + + return *out; +} + +static int fuse_file_read_iter_finalize(struct fuse_args *fa, ssize_t *out, + struct kiocb *iocb, struct iov_iter *to) +{ + struct fuse_read_iter_out *frio = fa->out_args[0].value; + + *out = frio->ret; + + return 0; +} + +int fuse_bpf_file_read_iter(ssize_t *out, struct inode *inode, struct kiocb *iocb, struct iov_iter *to) +{ + return fuse_bpf_backing(inode, struct fuse_file_read_iter_io, out, + fuse_file_read_iter_initialize_in, + fuse_file_read_iter_initialize_out, + fuse_file_read_iter_backing, + fuse_file_read_iter_finalize, + iocb, to); +} + +struct fuse_write_iter_out { + uint64_t ret; +}; +struct fuse_file_write_iter_io { + struct fuse_write_in fwi; + struct fuse_write_out fwo; + struct fuse_write_iter_out fwio; +}; + +static int fuse_file_write_iter_initialize_in(struct fuse_args *fa, + struct fuse_file_write_iter_io *fwio, + struct kiocb *iocb, struct iov_iter *from) +{ + struct file *file = iocb->ki_filp; + struct fuse_file *ff = file->private_data; + + *fwio = (struct fuse_file_write_iter_io) { + .fwi.fh = ff->fh, + .fwi.offset = iocb->ki_pos, + .fwi.size = from->count, + }; + + /* TODO we can't assume 'from' is a kvec */ + *fa = (struct fuse_args) { + .opcode = FUSE_WRITE, + .nodeid = ff->nodeid, + .in_numargs = 2, + .in_args[0].size = sizeof(fwio->fwi), + .in_args[0].value = &fwio->fwi, + .in_args[1].size = fwio->fwi.size, + .in_args[1].value = from->kvec->iov_base, + }; + + return 0; +} + +static int fuse_file_write_iter_initialize_out(struct fuse_args *fa, + struct fuse_file_write_iter_io *fwio, + struct kiocb *iocb, struct iov_iter *from) +{ + /* TODO we can't assume 'from' is a kvec */ + fa->out_numargs = 1; + fa->out_args[0].size = sizeof(fwio->fwio); + fa->out_args[0].value = &fwio->fwio; + + return 0; +} + +static int fuse_file_write_iter_backing(struct fuse_args *fa, ssize_t *out, + struct kiocb *iocb, struct iov_iter *from) +{ + struct file *file = iocb->ki_filp; + struct fuse_file *ff = file->private_data; + struct fuse_write_iter_out *fwio = fa->out_args[0].value; + ssize_t count = iov_iter_count(from); + + if (!count) + return 0; + + /* TODO This just plain ignores any change to fuse_write_in */ + /* TODO uint32_t seems smaller than ssize_t.... right? */ + inode_lock(file_inode(file)); + + fuse_copyattr(file, ff->backing_file); + + if (is_sync_kiocb(iocb)) { + fuse_file_start_write(file, ff->backing_file, iocb->ki_pos, count); + *out = vfs_iter_write(ff->backing_file, from, &iocb->ki_pos, + iocb_to_rw_flags(iocb->ki_flags, FUSE_BPF_IOCB_MASK)); + fuse_file_end_write(file, ff->backing_file, iocb->ki_pos, *out); + } else { + struct fuse_bpf_aio_req *aio_req; + + *out = -ENOMEM; + aio_req = kmem_cache_zalloc(fuse_bpf_aio_request_cachep, GFP_KERNEL); + if (!aio_req) + goto out; + + fuse_file_start_write(file, ff->backing_file, iocb->ki_pos, count); + __sb_writers_release(file_inode(ff->backing_file)->i_sb, SB_FREEZE_WRITE); + aio_req->iocb_orig = iocb; + kiocb_clone(&aio_req->iocb, iocb, ff->backing_file); + aio_req->iocb.ki_complete = fuse_bpf_aio_rw_complete; + refcount_set(&aio_req->ref, 2); + *out = vfs_iocb_iter_write(ff->backing_file, &aio_req->iocb, from); + fuse_bpf_aio_put(aio_req); + if (*out != -EIOCBQUEUED) + fuse_bpf_aio_cleanup_handler(aio_req, *out); + } + +out: + inode_unlock(file_inode(file)); + fwio->ret = *out; + if (*out < 0) + return *out; + return 0; +} + +static int fuse_file_write_iter_finalize(struct fuse_args *fa, ssize_t *out, + struct kiocb *iocb, struct iov_iter *from) +{ + struct fuse_write_iter_out *fwio = fa->out_args[0].value; + + *out = fwio->ret; + return 0; +} + +int fuse_bpf_file_write_iter(ssize_t *out, struct inode *inode, struct kiocb *iocb, struct iov_iter *from) +{ + return fuse_bpf_backing(inode, struct fuse_file_write_iter_io, out, + fuse_file_write_iter_initialize_in, + fuse_file_write_iter_initialize_out, + fuse_file_write_iter_backing, + fuse_file_write_iter_finalize, + iocb, from); +} + ssize_t fuse_backing_mmap(struct file *file, struct vm_area_struct *vma) { int ret; @@ -1280,3 +1641,19 @@ int fuse_bpf_access(int *out, struct inode *inode, int mask) fuse_access_initialize_in, fuse_access_initialize_out, fuse_access_backing, fuse_access_finalize, inode, mask); } + +int __init fuse_bpf_init(void) +{ + fuse_bpf_aio_request_cachep = kmem_cache_create("fuse_bpf_aio_req", + sizeof(struct fuse_bpf_aio_req), + 0, SLAB_HWCACHE_ALIGN, NULL); + if (!fuse_bpf_aio_request_cachep) + return -ENOMEM; + + return 0; +} + +void __exit fuse_bpf_cleanup(void) +{ + kmem_cache_destroy(fuse_bpf_aio_request_cachep); +} diff --git a/fs/fuse/control.c b/fs/fuse/control.c index 247ef4f76761..685552453751 100644 --- a/fs/fuse/control.c +++ b/fs/fuse/control.c @@ -378,7 +378,7 @@ int __init fuse_ctl_init(void) return register_filesystem(&fuse_ctl_fs_type); } -void __exit fuse_ctl_cleanup(void) +void fuse_ctl_cleanup(void) { unregister_filesystem(&fuse_ctl_fs_type); } diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 70a5bd5403ca..59f3d85106d3 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1604,6 +1604,7 @@ static ssize_t fuse_file_read_iter(struct kiocb *iocb, struct iov_iter *to) struct file *file = iocb->ki_filp; struct fuse_file *ff = file->private_data; struct inode *inode = file_inode(file); + ssize_t ret; if (fuse_is_bad(inode)) return -EIO; @@ -1611,6 +1612,9 @@ static ssize_t fuse_file_read_iter(struct kiocb *iocb, struct iov_iter *to) if (FUSE_IS_DAX(inode)) return fuse_dax_read_iter(iocb, to); + if (fuse_bpf_file_read_iter(&ret, inode, iocb, to)) + return ret; + if (!(ff->open_flags & FOPEN_DIRECT_IO)) return fuse_cache_read_iter(iocb, to); else @@ -1622,6 +1626,7 @@ static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from) struct file *file = iocb->ki_filp; struct fuse_file *ff = file->private_data; struct inode *inode = file_inode(file); + ssize_t ret = 0; if (fuse_is_bad(inode)) return -EIO; @@ -1629,6 +1634,9 @@ static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from) if (FUSE_IS_DAX(inode)) return fuse_dax_write_iter(iocb, from); + if (fuse_bpf_file_write_iter(&ret, inode, iocb, from)) + return ret; + if (!(ff->open_flags & FOPEN_DIRECT_IO)) return fuse_cache_write_iter(iocb, from); else diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index dc5bba2a75ab..25cedaa9014c 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1130,7 +1130,7 @@ int fuse_dev_init(void); void fuse_dev_cleanup(void); int fuse_ctl_init(void); -void __exit fuse_ctl_cleanup(void); +void fuse_ctl_cleanup(void); /** * Simple request sending that does request allocation and freeing @@ -1410,6 +1410,8 @@ int fuse_bpf_unlink(int *out, struct inode *dir, struct dentry *entry); int fuse_bpf_release(int *out, struct inode *inode, struct file *file); int fuse_bpf_releasedir(int *out, struct inode *inode, struct file *file); int fuse_bpf_lseek(loff_t *out, struct inode *inode, struct file *file, loff_t offset, int whence); +int fuse_bpf_file_read_iter(ssize_t *out, struct inode *inode, struct kiocb *iocb, struct iov_iter *to); +int fuse_bpf_file_write_iter(ssize_t *out, struct inode *inode, struct kiocb *iocb, struct iov_iter *from); int fuse_bpf_file_fallocate(int *out, struct inode *inode, struct file *file, int mode, loff_t offset, loff_t length); int fuse_bpf_lookup(struct dentry **out, struct inode *dir, struct dentry *entry, unsigned int flags); int fuse_bpf_access(int *out, struct inode *inode, int mask); @@ -1462,6 +1464,16 @@ static inline int fuse_bpf_lseek(loff_t *out, struct inode *inode, struct file * return 0; } +static inline int fuse_bpf_file_read_iter(ssize_t *out, struct inode *inode, struct kiocb *iocb, struct iov_iter *to) +{ + return 0; +} + +static inline int fuse_bpf_file_write_iter(ssize_t *out, struct inode *inode, struct kiocb *iocb, struct iov_iter *from) +{ + return 0; +} + static inline int fuse_bpf_file_fallocate(int *out, struct inode *inode, struct file *file, int mode, loff_t offset, loff_t length) { return 0; @@ -1512,4 +1524,9 @@ static inline u64 attr_timeout(struct fuse_attr_out *o) return time_to_jiffies(o->attr_valid, o->attr_valid_nsec); } +#ifdef CONFIG_FUSE_BPF +int __init fuse_bpf_init(void); +void __exit fuse_bpf_cleanup(void); +#endif /* CONFIG_FUSE_BPF */ + #endif /* _FS_FUSE_I_H */ diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index bafb2832627d..9781faff6df6 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -2094,11 +2094,21 @@ static int __init fuse_init(void) if (res) goto err_sysfs_cleanup; +#ifdef CONFIG_FUSE_BPF + res = fuse_bpf_init(); + if (res) + goto err_ctl_cleanup; +#endif + sanitize_global_limit(&max_user_bgreq); sanitize_global_limit(&max_user_congthresh); return 0; +#ifdef CONFIG_FUSE_BPF + err_ctl_cleanup: + fuse_ctl_cleanup(); +#endif err_sysfs_cleanup: fuse_sysfs_cleanup(); err_dev_cleanup: @@ -2116,6 +2126,9 @@ static void __exit fuse_exit(void) fuse_ctl_cleanup(); fuse_sysfs_cleanup(); fuse_fs_cleanup(); +#ifdef CONFIG_FUSE_BPF + fuse_bpf_cleanup(); +#endif fuse_dev_cleanup(); } From patchwork Tue Nov 22 02:15:29 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13051835 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 B2D94C43217 for ; Tue, 22 Nov 2022 02:19:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232498AbiKVCTM (ORCPT ); Mon, 21 Nov 2022 21:19:12 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:43378 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232382AbiKVCRw (ORCPT ); Mon, 21 Nov 2022 21:17:52 -0500 Received: from mail-yw1-x1149.google.com (mail-yw1-x1149.google.com [IPv6:2607:f8b0:4864:20::1149]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C8D82E870E for ; Mon, 21 Nov 2022 18:16:26 -0800 (PST) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-3691846091fso126825787b3.9 for ; Mon, 21 Nov 2022 18:16:26 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=sXC86U5/hdvmtqoI3CrBqri1fBKx6hffM6aKsOurzIQ=; b=nviEqECcUVQGFZCSjYX27H4M5ju7f+JT7lEyRab0UT5tVqqZE8Nr3TDs0EYlPUSSRs IgVAP+mJQk0CFNsd0y2gZllcV9H5NC6rztxOhHtbo2yj20wWri1irhqXA0aEpof27HPJ /2mVbS/N1xkIu4emdYQWAs34awPkiLIz1dX5QmHedJTpqS2auwDSAwRDXZ5kV4uw80bW lcrV3OSql4Gqyn+NM/cCyEw0DYv9BxSFlBZW3oVxq4MqyDQanhisbwCzf2ekYZQaJr9m slJntZYFnJ2tls7oCuJKp6UhuZbW7W7/FRkg2LQaPjFYBH1ZHSpGhD7doqxaX1FJJpKn tjww== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=sXC86U5/hdvmtqoI3CrBqri1fBKx6hffM6aKsOurzIQ=; b=mvUVBraf3egPh+Q3AA4Tg1URsCDF791D1VoOIa+tB5ptYTXSQw0j+naHHq+/Zeax1t vaeGseMgIb41+/A5QDBVFB3cR9CV/Btn4d9lgKgqoIsn8EjRbTIWertzZr+r04EbTYho 2zuj3G41eIa5jxvd6d65+hbzecRdq43vFtsXvOaHuT9OyOYaKzcRhs/0TabAs8RPbEWD QFbz8VQd01iAb0pFLTQxXNmWlSpNp0cgDXf9QpIUvge/ZarNILGBzJct5J6WF+WQV8o2 lSTth0Ty/e+UoZ1rIlKkj5Q3jLOECdsVRpXplmxXxgbopWCldrklr9XoXOp7kFod1rUl T+UA== X-Gm-Message-State: ANoB5pltag671vpRckrbG6U/I3IYzf5gUWkHU6zSmWvWwML18E8VgMST dql3bHTbfm6QchfMcwmHrUNAnN+u61c= X-Google-Smtp-Source: AA0mqf4BhlbPHTes02ABbjotB/JyZWY+vbUvzJOF4pcG5MOu08hPs+emLNEYY0m782nCmCcjPsBqaEVmAak= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:200:8539:aadd:13be:6e82]) (user=drosen job=sendgmr) by 2002:a05:6902:729:b0:6dc:c7e9:17e0 with SMTP id l9-20020a056902072900b006dcc7e917e0mr5539541ybt.411.1669083385614; Mon, 21 Nov 2022 18:16:25 -0800 (PST) Date: Mon, 21 Nov 2022 18:15:29 -0800 In-Reply-To: <20221122021536.1629178-1-drosen@google.com> Mime-Version: 1.0 References: <20221122021536.1629178-1-drosen@google.com> X-Mailer: git-send-email 2.38.1.584.g0f3c55d4c2-goog Message-ID: <20221122021536.1629178-15-drosen@google.com> Subject: [RFC PATCH v2 14/21] fuse-bpf: support FUSE_READDIR From: Daniel Rosenberg To: Miklos Szeredi Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, bpf@vger.kernel.org, kernel-team@android.com, Daniel Rosenberg , Paul Lawrence Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 185 ++++++++++++++++++++++++++++++++++++++ fs/fuse/fuse_i.h | 6 ++ fs/fuse/readdir.c | 5 ++ include/uapi/linux/fuse.h | 6 ++ 4 files changed, 202 insertions(+) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index 425815d7f5dc..a15b5c107cfe 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -1597,6 +1597,191 @@ int fuse_bpf_unlink(int *out, struct inode *dir, struct dentry *entry) dir, entry); } +struct fuse_read_io { + struct fuse_read_in fri; + struct fuse_read_out fro; +}; + +static int fuse_readdir_initialize_in(struct fuse_args *fa, struct fuse_read_io *frio, + struct file *file, struct dir_context *ctx, + bool *force_again, bool *allow_force, bool is_continued) +{ + struct fuse_file *ff = file->private_data; + + *fa = (struct fuse_args) { + .nodeid = ff->nodeid, + .opcode = FUSE_READDIR, + .in_numargs = 1, + .in_args[0] = (struct fuse_in_arg) { + .size = sizeof(frio->fri), + .value = &frio->fri, + }, + }; + + frio->fri = (struct fuse_read_in) { + .fh = ff->fh, + .offset = ctx->pos, + .size = PAGE_SIZE, + }; + + *force_again = false; + *allow_force = true; + return 0; +} + +static int fuse_readdir_initialize_out(struct fuse_args *fa, struct fuse_read_io *frio, + struct file *file, struct dir_context *ctx, + bool *force_again, bool *allow_force, bool is_continued) +{ + u8 *page = (u8 *)__get_free_page(GFP_KERNEL); + + if (!page) + return -ENOMEM; + + fa->out_argvar = true; + fa->out_numargs = 2; + fa->out_args[0] = (struct fuse_arg) { + .size = sizeof(frio->fro), + .value = &frio->fro, + }; + fa->out_args[1] = (struct fuse_arg) { + .size = PAGE_SIZE, + .value = page, + }; + frio->fro = (struct fuse_read_out) { + .again = 0, + .offset = 0, + }; + + return 0; +} + +struct extfuse_ctx { + struct dir_context ctx; + u8 *addr; + size_t offset; +}; + +static bool filldir(struct dir_context *ctx, const char *name, int namelen, + loff_t offset, u64 ino, unsigned int d_type) +{ + struct extfuse_ctx *ec = container_of(ctx, struct extfuse_ctx, ctx); + struct fuse_dirent *fd = (struct fuse_dirent *)(ec->addr + ec->offset); + + if (ec->offset + sizeof(struct fuse_dirent) + namelen > PAGE_SIZE) + return false; + + *fd = (struct fuse_dirent) { + .ino = ino, + .off = offset, + .namelen = namelen, + .type = d_type, + }; + + memcpy(fd->name, name, namelen); + ec->offset += FUSE_DIRENT_SIZE(fd); + + return true; +} + +static int parse_dirfile(char *buf, size_t nbytes, struct dir_context *ctx) +{ + while (nbytes >= FUSE_NAME_OFFSET) { + struct fuse_dirent *dirent = (struct fuse_dirent *) buf; + size_t reclen = FUSE_DIRENT_SIZE(dirent); + + if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX) + return -EIO; + if (reclen > nbytes) + break; + if (memchr(dirent->name, '/', dirent->namelen) != NULL) + return -EIO; + + ctx->pos = dirent->off; + if (!dir_emit(ctx, dirent->name, dirent->namelen, dirent->ino, + dirent->type)) + break; + + buf += reclen; + nbytes -= reclen; + } + + return 0; +} + + +static int fuse_readdir_backing(struct fuse_args *fa, int *out, + struct file *file, struct dir_context *ctx, + bool *force_again, bool *allow_force, bool is_continued) +{ + struct fuse_file *ff = file->private_data; + struct file *backing_dir = ff->backing_file; + struct fuse_read_out *fro = fa->out_args[0].value; + struct extfuse_ctx ec; + + ec = (struct extfuse_ctx) { + .ctx.actor = filldir, + .ctx.pos = ctx->pos, + .addr = fa->out_args[1].value, + }; + + if (!ec.addr) + return -ENOMEM; + + if (!is_continued) + backing_dir->f_pos = file->f_pos; + + *out = iterate_dir(backing_dir, &ec.ctx); + if (ec.offset == 0) + *allow_force = false; + fa->out_args[1].size = ec.offset; + + fro->offset = ec.ctx.pos; + fro->again = false; + + return *out; +} + +static int fuse_readdir_finalize(struct fuse_args *fa, int *out, + struct file *file, struct dir_context *ctx, + bool *force_again, bool *allow_force, bool is_continued) +{ + struct fuse_read_out *fro = fa->out_args[0].value; + struct fuse_file *ff = file->private_data; + struct file *backing_dir = ff->backing_file; + + *out = parse_dirfile(fa->out_args[1].value, fa->out_args[1].size, ctx); + *force_again = !!fro->again; + if (*force_again && !*allow_force) + *out = -EINVAL; + + ctx->pos = fro->offset; + backing_dir->f_pos = fro->offset; + + free_page((unsigned long)fa->out_args[1].value); + return *out; +} + +int fuse_bpf_readdir(int *out, struct inode *inode, struct file *file, struct dir_context *ctx) +{ + int ret; + bool allow_force; + bool force_again = false; + bool is_continued = false; + +again: + ret = fuse_bpf_backing(inode, struct fuse_read_io, out, + fuse_readdir_initialize_in, fuse_readdir_initialize_out, + fuse_readdir_backing, fuse_readdir_finalize, + file, ctx, &force_again, &allow_force, is_continued); + if (force_again && *out >= 0) { + is_continued = true; + goto again; + } + + return ret; +} + static int fuse_access_initialize_in(struct fuse_args *fa, struct fuse_access_in *fai, struct inode *inode, int mask) { diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 25cedaa9014c..0ea3fb74caab 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1414,6 +1414,7 @@ int fuse_bpf_file_read_iter(ssize_t *out, struct inode *inode, struct kiocb *ioc int fuse_bpf_file_write_iter(ssize_t *out, struct inode *inode, struct kiocb *iocb, struct iov_iter *from); int fuse_bpf_file_fallocate(int *out, struct inode *inode, struct file *file, int mode, loff_t offset, loff_t length); int fuse_bpf_lookup(struct dentry **out, struct inode *dir, struct dentry *entry, unsigned int flags); +int fuse_bpf_readdir(int *out, struct inode *inode, struct file *file, struct dir_context *ctx); int fuse_bpf_access(int *out, struct inode *inode, int mask); #else @@ -1484,6 +1485,11 @@ static inline int fuse_bpf_lookup(struct dentry **out, struct inode *dir, struct return 0; } +static inline int fuse_bpf_readdir(int *out, struct inode *inode, struct file *file, struct dir_context *ctx) +{ + return 0; +} + static inline int fuse_bpf_access(int *out, struct inode *inode, int mask) { return 0; diff --git a/fs/fuse/readdir.c b/fs/fuse/readdir.c index e8deaacf1832..f32105679057 100644 --- a/fs/fuse/readdir.c +++ b/fs/fuse/readdir.c @@ -20,6 +20,8 @@ static bool fuse_use_readdirplus(struct inode *dir, struct dir_context *ctx) if (!fc->do_readdirplus) return false; + if (fi->nodeid == 0) + return false; if (!fc->readdirplus_auto) return true; if (test_and_clear_bit(FUSE_I_ADVISE_RDPLUS, &fi->state)) @@ -582,6 +584,9 @@ int fuse_readdir(struct file *file, struct dir_context *ctx) if (fuse_is_bad(inode)) return -EIO; + if (fuse_bpf_readdir(&err, inode, file, ctx)) + return err; + mutex_lock(&ff->readdir.lock); err = UNCACHED; diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index e49e5a8e044c..8c13483f240e 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -709,6 +709,12 @@ struct fuse_read_in { uint32_t padding; }; +struct fuse_read_out { + uint64_t offset; + uint32_t again; + uint32_t padding; +}; + #define FUSE_COMPAT_WRITE_IN_SIZE 24 struct fuse_write_in { From patchwork Tue Nov 22 02:15:30 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13051836 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 F0243C433FE for ; Tue, 22 Nov 2022 02:19:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232540AbiKVCTS (ORCPT ); Mon, 21 Nov 2022 21:19:18 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:43418 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232536AbiKVCR5 (ORCPT ); Mon, 21 Nov 2022 21:17:57 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7DEB1E872D for ; Mon, 21 Nov 2022 18:16:29 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id j132-20020a25238a000000b006da635e2073so12419472ybj.2 for ; Mon, 21 Nov 2022 18:16:29 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=+7lnONjHdi886Ig9Mqdu5pD+AjkqXhnQE6noiyOFdJA=; b=lrtwBrtXPPkYidFgin2J04BdRiJSMvbPz4VqiHsVwzBDAG9kDpeEPdifXAxmHw9zI0 NKIkiPinjAMKlnqhSyv2sygr81lgE3nvzHVOn8k8x8uiH8dfmKseRpx7dw9KRkf7Hg2c lXbCB4QF3JdaNV7G03QBFnoniUskfNIfqqijZfJCWFEjW/44GdV8ysqFYt+GCyXJfN6g r+1L/JiP9nTm8+tZ0g8k577RNDvLHeONZM4F+pfQhpVob28c+olV32piW3TxFwIYXYLH 57nmypvY34lk73vshQ6JxZ9nW5b8Rqizz3+kFsf9bb7liLpnIuarGf6lY2UXRbNq1ZoX ucUQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=+7lnONjHdi886Ig9Mqdu5pD+AjkqXhnQE6noiyOFdJA=; b=CEngdxqD1m0K6ErqNT5ALIuMClOjdFHk3O+Wj33zG1+No4fCjgY/2g7d2l2WFa7a0B yaeQlJvyiTxdTLeqAl63LB2dprUYhdr+qm+9s/1JR8zCk4Y8xIpTy2rMK/Ysb9Hx3sUS eumz03Mab3rB6kcnFuBR4uk7etSMVJk0O4saYvNGijM9yMwoXiJmHtFqHuJ5mUbXOFDN yizi4nPHBBt2CNp2zIva4AeITmYp2WnSuakXY7P73cZbibFjKHs0Sh09AEmPQY9rTDw3 T3D4M25bnzRkw8yiaPodD/EqXxUI4JNjOm0IfRz7SyR9/Loh4nh5SBZ/QeVkArOWE3Qp KjkA== X-Gm-Message-State: ACrzQf2+KuGbRzhRNTLUV/qsuwG/N0zNFf4ZwvOMyCoP+mn5HcPpGEht pB25ME62T9qb7/oNdgv0sfZ0i+u9GRw= X-Google-Smtp-Source: AMsMyM5qNEluRhS7QKFWTOvy41GiEHaYUT0ydWTishvpXmRnJKjIU8Cs0lqnFvk4wZM2iqmoOyga+rGwBhU= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:200:8539:aadd:13be:6e82]) (user=drosen job=sendgmr) by 2002:a25:1181:0:b0:6bf:bd96:2b01 with SMTP id 123-20020a251181000000b006bfbd962b01mr63735903ybr.17.1669083388163; Mon, 21 Nov 2022 18:16:28 -0800 (PST) Date: Mon, 21 Nov 2022 18:15:30 -0800 In-Reply-To: <20221122021536.1629178-1-drosen@google.com> Mime-Version: 1.0 References: <20221122021536.1629178-1-drosen@google.com> X-Mailer: git-send-email 2.38.1.584.g0f3c55d4c2-goog Message-ID: <20221122021536.1629178-16-drosen@google.com> Subject: [RFC PATCH v2 15/21] fuse-bpf: Add support for sync operations From: Daniel Rosenberg To: Miklos Szeredi Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, bpf@vger.kernel.org, kernel-team@android.com, Daniel Rosenberg , Paul Lawrence Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/dir.c | 3 + fs/fuse/file.c | 6 ++ fs/fuse/fuse_i.h | 18 ++++++ 4 files changed, 169 insertions(+) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index a15b5c107cfe..719292e03b18 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -652,6 +652,58 @@ int fuse_bpf_releasedir(int *out, struct inode *inode, struct file *file) fuse_release_backing, fuse_release_finalize, inode, file); } +static int fuse_flush_initialize_in(struct fuse_args *fa, struct fuse_flush_in *ffi, + struct file *file, fl_owner_t id) +{ + struct fuse_file *fuse_file = file->private_data; + + *ffi = (struct fuse_flush_in) { + .fh = fuse_file->fh, + }; + + *fa = (struct fuse_args) { + .nodeid = get_node_id(file->f_inode), + .opcode = FUSE_FLUSH, + .in_numargs = 1, + .in_args[0].size = sizeof(*ffi), + .in_args[0].value = ffi, + .force = true, + }; + + return 0; +} + +static int fuse_flush_initialize_out(struct fuse_args *fa, struct fuse_flush_in *ffi, + struct file *file, fl_owner_t id) +{ + return 0; +} + +static int fuse_flush_backing(struct fuse_args *fa, int *out, struct file *file, fl_owner_t id) +{ + struct fuse_file *fuse_file = file->private_data; + struct file *backing_file = fuse_file->backing_file; + + *out = 0; + if (backing_file->f_op->flush) + *out = backing_file->f_op->flush(backing_file, id); + return *out; +} + +static int fuse_flush_finalize(struct fuse_args *fa, int *out, struct file *file, fl_owner_t id) +{ + return 0; +} + +int fuse_bpf_flush(int *out, struct inode *inode, struct file *file, fl_owner_t id) +{ + return fuse_bpf_backing(inode, struct fuse_flush_in, out, + fuse_flush_initialize_in, fuse_flush_initialize_out, + fuse_flush_backing, + fuse_flush_finalize, + file, id); +} + struct fuse_lseek_io { struct fuse_lseek_in fli; struct fuse_lseek_out flo; @@ -740,6 +792,96 @@ int fuse_bpf_lseek(loff_t *out, struct inode *inode, struct file *file, loff_t o file, offset, whence); } +static int fuse_fsync_initialize_in(struct fuse_args *fa, struct fuse_fsync_in *ffi, + struct file *file, loff_t start, loff_t end, int datasync) +{ + struct fuse_file *fuse_file = file->private_data; + + *ffi = (struct fuse_fsync_in) { + .fh = fuse_file->fh, + .fsync_flags = datasync ? FUSE_FSYNC_FDATASYNC : 0, + }; + + *fa = (struct fuse_args) { + .nodeid = get_fuse_inode(file->f_inode)->nodeid, + .opcode = FUSE_FSYNC, + .in_numargs = 1, + .in_args[0].size = sizeof(*ffi), + .in_args[0].value = ffi, + .force = true, + }; + + return 0; +} + +static int fuse_fsync_initialize_out(struct fuse_args *fa, struct fuse_fsync_in *ffi, + struct file *file, loff_t start, loff_t end, int datasync) +{ + return 0; +} + +static int fuse_fsync_backing(struct fuse_args *fa, int *out, + struct file *file, loff_t start, loff_t end, int datasync) +{ + struct fuse_file *fuse_file = file->private_data; + struct file *backing_file = fuse_file->backing_file; + const struct fuse_fsync_in *ffi = fa->in_args[0].value; + int new_datasync = (ffi->fsync_flags & FUSE_FSYNC_FDATASYNC) ? 1 : 0; + + *out = vfs_fsync(backing_file, new_datasync); + return 0; +} + +static int fuse_fsync_finalize(struct fuse_args *fa, int *out, + struct file *file, loff_t start, loff_t end, int datasync) +{ + return 0; +} + +int fuse_bpf_fsync(int *out, struct inode *inode, struct file *file, loff_t start, loff_t end, int datasync) +{ + return fuse_bpf_backing(inode, struct fuse_fsync_in, out, + fuse_fsync_initialize_in, fuse_fsync_initialize_out, + fuse_fsync_backing, fuse_fsync_finalize, + file, start, end, datasync); +} + +static int fuse_dir_fsync_initialize_in(struct fuse_args *fa, struct fuse_fsync_in *ffi, + struct file *file, loff_t start, loff_t end, int datasync) +{ + struct fuse_file *fuse_file = file->private_data; + + *ffi = (struct fuse_fsync_in) { + .fh = fuse_file->fh, + .fsync_flags = datasync ? FUSE_FSYNC_FDATASYNC : 0, + }; + + *fa = (struct fuse_args) { + .nodeid = get_fuse_inode(file->f_inode)->nodeid, + .opcode = FUSE_FSYNCDIR, + .in_numargs = 1, + .in_args[0].size = sizeof(*ffi), + .in_args[0].value = ffi, + .force = true, + }; + + return 0; +} + +static int fuse_dir_fsync_initialize_out(struct fuse_args *fa, struct fuse_fsync_in *ffi, + struct file *file, loff_t start, loff_t end, int datasync) +{ + return 0; +} + +int fuse_bpf_dir_fsync(int *out, struct inode *inode, struct file *file, loff_t start, loff_t end, int datasync) +{ + return fuse_bpf_backing(inode, struct fuse_fsync_in, out, + fuse_dir_fsync_initialize_in, fuse_dir_fsync_initialize_out, + fuse_fsync_backing, fuse_fsync_finalize, + file, start, end, datasync); +} + static inline void fuse_bpf_aio_put(struct fuse_bpf_aio_req *aio_req) { if (refcount_dec_and_test(&aio_req->ref)) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 729a0348fa01..55ed3fb9d4a3 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1591,6 +1591,9 @@ static int fuse_dir_fsync(struct file *file, loff_t start, loff_t end, if (fuse_is_bad(inode)) return -EIO; + if (fuse_bpf_dir_fsync(&err, inode, file, start, end, datasync)) + return err; + if (fc->no_fsyncdir) return 0; diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 59f3d85106d3..fa9ee2740a42 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -502,6 +502,9 @@ static int fuse_flush(struct file *file, fl_owner_t id) if (fuse_is_bad(inode)) return -EIO; + if (fuse_bpf_flush(&err, file_inode(file), file, id)) + return err; + if (ff->open_flags & FOPEN_NOFLUSH && !fm->fc->writeback_cache) return 0; @@ -577,6 +580,9 @@ static int fuse_fsync(struct file *file, loff_t start, loff_t end, if (fuse_is_bad(inode)) return -EIO; + if (fuse_bpf_fsync(&err, inode, file, start, end, datasync)) + return err; + inode_lock(inode); /* diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 0ea3fb74caab..cb087364e9bb 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1409,7 +1409,10 @@ int fuse_bpf_rmdir(int *out, struct inode *dir, struct dentry *entry); int fuse_bpf_unlink(int *out, struct inode *dir, struct dentry *entry); int fuse_bpf_release(int *out, struct inode *inode, struct file *file); int fuse_bpf_releasedir(int *out, struct inode *inode, struct file *file); +int fuse_bpf_flush(int *out, struct inode *inode, struct file *file, fl_owner_t id); int fuse_bpf_lseek(loff_t *out, struct inode *inode, struct file *file, loff_t offset, int whence); +int fuse_bpf_fsync(int *out, struct inode *inode, struct file *file, loff_t start, loff_t end, int datasync); +int fuse_bpf_dir_fsync(int *out, struct inode *inode, struct file *file, loff_t start, loff_t end, int datasync); int fuse_bpf_file_read_iter(ssize_t *out, struct inode *inode, struct kiocb *iocb, struct iov_iter *to); int fuse_bpf_file_write_iter(ssize_t *out, struct inode *inode, struct kiocb *iocb, struct iov_iter *from); int fuse_bpf_file_fallocate(int *out, struct inode *inode, struct file *file, int mode, loff_t offset, loff_t length); @@ -1460,11 +1463,26 @@ static inline int fuse_bpf_releasedir(int *out, struct inode *inode, struct file return 0; } +static inline int fuse_bpf_flush(int *out, struct inode *inode, struct file *file, fl_owner_t id) +{ + return 0; +} + static inline int fuse_bpf_lseek(loff_t *out, struct inode *inode, struct file *file, loff_t offset, int whence) { return 0; } +static inline int fuse_bpf_fsync(int *out, struct inode *inode, struct file *file, loff_t start, loff_t end, int datasync) +{ + return 0; +} + +static inline int fuse_bpf_dir_fsync(int *out, struct inode *inode, struct file *file, loff_t start, loff_t end, int datasync) +{ + return 0; +} + static inline int fuse_bpf_file_read_iter(ssize_t *out, struct inode *inode, struct kiocb *iocb, struct iov_iter *to) { return 0; From patchwork Tue Nov 22 02:15:31 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13051837 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 E7A00C433FE for ; Tue, 22 Nov 2022 02:19:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232660AbiKVCTm (ORCPT ); Mon, 21 Nov 2022 21:19:42 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42354 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232600AbiKVCSM (ORCPT ); Mon, 21 Nov 2022 21:18:12 -0500 Received: from mail-yw1-x1149.google.com (mail-yw1-x1149.google.com [IPv6:2607:f8b0:4864:20::1149]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E9E02E223E for ; Mon, 21 Nov 2022 18:16:31 -0800 (PST) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-3a15bec5d25so43106217b3.19 for ; Mon, 21 Nov 2022 18:16:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=3ZCLjcJ4R3eU9/lQk3QpcBgc0zLovUHl4lozHOuR1ro=; b=Onag8LXKUEYkNQDXASyE2XfkV9WriZxyqN3f46RXUNf+xJTp5wc9/Ps0FZY4IkN/So AbmMbh0t8c0JnZvIieoFSE1EBshobKjZxKzNu97r2WkhJB1X/Ia6lpQBhufU7YzfmkAv YDjy4vUmCdmgGRkRHuzn1To8UaEOwRZ6WUtH7P8oqZpARoArEyNJrPPn+4HH4pmP+F1c d+CZeBxp97d6qmi6opkagbdWjO+WAhv/lud28ORlHNp68nkXZc+2xBT0Tpc9iMS6lhWa 0NWCnjXNlGUVxaYC99FCDe6hQ6b17FEytdtdSgwGyJeul8NCakUZyFsIDAsf540osdoG 0f+w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=3ZCLjcJ4R3eU9/lQk3QpcBgc0zLovUHl4lozHOuR1ro=; b=lRcfdk3KaIBV2fsS63dw7KOPtVYkv5Va6HN/dTNz/qIDjd704kkIfG5pugRd0z+lGS +I1sHkITy4glnx9ftYUDgYGkmZDqoDa8BV3bovsR4qn4zi/YcEpW4znFHVryFjv8Sp7v ApB4vk1KJooX0qpqE4JYgRM5ATBYPAogT7UCXYfYhOfWCGthEluOc+JvrSpCmzratcEO Lu+CNYzGFOFwBvG/Z/yQmWL1FAQDK6f1Xe1GmwVda8lgzFSYEiCYndfqGvj3JCU/pwZb 8zijh9tWD/vOISYVnc8n91Ue0qFmD2tWFXbHzbOLm3kItw/0t3LCGM/Mga4PE/9wQz92 zPAw== X-Gm-Message-State: ANoB5pn7z9jjmQBiKqffD7qYmJlw8OO4AKrptHyWj3wEO2x+YbzUkj2u JXMEaVVA4yZZMrJdIJKA4AtybAebgoc= X-Google-Smtp-Source: AA0mqf6bOX01h/HY8m5g2/RqyJvAEQUaFiNQzJkxznyZMuDkIxQAJ34RzPLeCX9TjkXsyVZO4lIS883KTcc= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:200:8539:aadd:13be:6e82]) (user=drosen job=sendgmr) by 2002:a0d:ebcd:0:b0:39b:9c96:b6b7 with SMTP id u196-20020a0debcd000000b0039b9c96b6b7mr3ywe.450.1669083390850; Mon, 21 Nov 2022 18:16:30 -0800 (PST) Date: Mon, 21 Nov 2022 18:15:31 -0800 In-Reply-To: <20221122021536.1629178-1-drosen@google.com> Mime-Version: 1.0 References: <20221122021536.1629178-1-drosen@google.com> X-Mailer: git-send-email 2.38.1.584.g0f3c55d4c2-goog Message-ID: <20221122021536.1629178-17-drosen@google.com> Subject: [RFC PATCH v2 16/21] fuse-bpf: Add Rename support From: Daniel Rosenberg To: Miklos Szeredi Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, bpf@vger.kernel.org, kernel-team@android.com, Daniel Rosenberg , Paul Lawrence Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 210 ++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/dir.c | 7 ++ fs/fuse/fuse_i.h | 18 ++++ 3 files changed, 235 insertions(+) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index 719292e03b18..333181d6ad73 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -1677,6 +1677,216 @@ int fuse_bpf_rmdir(int *out, struct inode *dir, struct dentry *entry) dir, entry); } +static int fuse_rename_backing_common(struct inode *olddir, + struct dentry *oldent, + struct inode *newdir, + struct dentry *newent, unsigned int flags) +{ + int err = 0; + struct path old_backing_path; + struct path new_backing_path; + struct dentry *old_backing_dir_dentry; + struct dentry *old_backing_dentry; + struct dentry *new_backing_dir_dentry; + struct dentry *new_backing_dentry; + struct dentry *trap = NULL; + struct inode *target_inode; + struct renamedata rd; + + //TODO Actually deal with changing anything that isn't a flag + get_fuse_backing_path(oldent, &old_backing_path); + if (!old_backing_path.dentry) + return -EBADF; + get_fuse_backing_path(newent, &new_backing_path); + if (!new_backing_path.dentry) { + /* + * TODO A file being moved from a backing path to another + * backing path which is not yet instrumented with FUSE-BPF. + * This may be slow and should be substituted with something + * more clever. + */ + err = -EXDEV; + goto put_old_path; + } + if (new_backing_path.mnt != old_backing_path.mnt) { + err = -EXDEV; + goto put_new_path; + } + old_backing_dentry = old_backing_path.dentry; + new_backing_dentry = new_backing_path.dentry; + old_backing_dir_dentry = dget_parent(old_backing_dentry); + new_backing_dir_dentry = dget_parent(new_backing_dentry); + target_inode = d_inode(newent); + + trap = lock_rename(old_backing_dir_dentry, new_backing_dir_dentry); + if (trap == old_backing_dentry) { + err = -EINVAL; + goto put_parents; + } + if (trap == new_backing_dentry) { + err = -ENOTEMPTY; + goto put_parents; + } + + rd = (struct renamedata) { + .old_mnt_userns = &init_user_ns, + .old_dir = d_inode(old_backing_dir_dentry), + .old_dentry = old_backing_dentry, + .new_mnt_userns = &init_user_ns, + .new_dir = d_inode(new_backing_dir_dentry), + .new_dentry = new_backing_dentry, + .flags = flags, + }; + err = vfs_rename(&rd); + if (err) + goto unlock; + if (target_inode) + fsstack_copy_attr_all(target_inode, + get_fuse_inode(target_inode)->backing_inode); + fsstack_copy_attr_all(d_inode(oldent), d_inode(old_backing_dentry)); +unlock: + unlock_rename(old_backing_dir_dentry, new_backing_dir_dentry); +put_parents: + dput(new_backing_dir_dentry); + dput(old_backing_dir_dentry); +put_new_path: + path_put(&new_backing_path); +put_old_path: + path_put(&old_backing_path); + return err; +} + +static int fuse_rename2_initialize_in(struct fuse_args *fa, struct fuse_rename2_in *fri, + struct inode *olddir, struct dentry *oldent, + struct inode *newdir, struct dentry *newent, + unsigned int flags) +{ + *fri = (struct fuse_rename2_in) { + .newdir = get_node_id(newdir), + .flags = flags, + }; + *fa = (struct fuse_args) { + .nodeid = get_node_id(olddir), + .opcode = FUSE_RENAME2, + .in_numargs = 3, + .in_args[0] = (struct fuse_in_arg) { + .size = sizeof(*fri), + .value = fri, + }, + .in_args[1] = (struct fuse_in_arg) { + .size = oldent->d_name.len + 1, + .value = (void *) oldent->d_name.name, + }, + .in_args[2] = (struct fuse_in_arg) { + .size = newent->d_name.len + 1, + .value = (void *) newent->d_name.name, + }, + }; + + return 0; +} + +static int fuse_rename2_initialize_out(struct fuse_args *fa, struct fuse_rename2_in *fri, + struct inode *olddir, struct dentry *oldent, + struct inode *newdir, struct dentry *newent, + unsigned int flags) +{ + return 0; +} + +static int fuse_rename2_backing(struct fuse_args *fa, int *out, + struct inode *olddir, struct dentry *oldent, + struct inode *newdir, struct dentry *newent, + unsigned int flags) +{ + const struct fuse_rename2_in *fri = fa->in_args[0].value; + + /* TODO: deal with changing dirs/ents */ + *out = fuse_rename_backing_common(olddir, oldent, newdir, newent, + fri->flags); + return *out; +} + +static int fuse_rename2_finalize(struct fuse_args *fa, int *out, + struct inode *olddir, struct dentry *oldent, + struct inode *newdir, struct dentry *newent, + unsigned int flags) +{ + return 0; +} + +int fuse_bpf_rename2(int *out, struct inode *olddir, struct dentry *oldent, + struct inode *newdir, struct dentry *newent, + unsigned int flags) +{ + return fuse_bpf_backing(olddir, struct fuse_rename2_in, out, + fuse_rename2_initialize_in, + fuse_rename2_initialize_out, fuse_rename2_backing, + fuse_rename2_finalize, + olddir, oldent, newdir, newent, flags); +} + +static int fuse_rename_initialize_in(struct fuse_args *fa, struct fuse_rename_in *fri, + struct inode *olddir, struct dentry *oldent, + struct inode *newdir, struct dentry *newent) +{ + *fri = (struct fuse_rename_in) { + .newdir = get_node_id(newdir), + }; + *fa = (struct fuse_args) { + .nodeid = get_node_id(olddir), + .opcode = FUSE_RENAME, + .in_numargs = 3, + .in_args[0] = (struct fuse_in_arg) { + .size = sizeof(*fri), + .value = fri, + }, + .in_args[1] = (struct fuse_in_arg) { + .size = oldent->d_name.len + 1, + .value = (void *) oldent->d_name.name, + }, + .in_args[2] = (struct fuse_in_arg) { + .size = newent->d_name.len + 1, + .value = (void *) newent->d_name.name, + }, + }; + + return 0; +} + +static int fuse_rename_initialize_out(struct fuse_args *fa, struct fuse_rename_in *fri, + struct inode *olddir, struct dentry *oldent, + struct inode *newdir, struct dentry *newent) +{ + return 0; +} + +static int fuse_rename_backing(struct fuse_args *fa, int *out, + struct inode *olddir, struct dentry *oldent, + struct inode *newdir, struct dentry *newent) +{ + /* TODO: deal with changing dirs/ents */ + *out = fuse_rename_backing_common(olddir, oldent, newdir, newent, 0); + return *out; +} + +static int fuse_rename_finalize(struct fuse_args *fa, int *out, + struct inode *olddir, struct dentry *oldent, + struct inode *newdir, struct dentry *newent) +{ + return 0; +} + +int fuse_bpf_rename(int *out, struct inode *olddir, struct dentry *oldent, + struct inode *newdir, struct dentry *newent) +{ + return fuse_bpf_backing(olddir, struct fuse_rename_in, out, + fuse_rename_initialize_in, + fuse_rename_initialize_out, fuse_rename_backing, + fuse_rename_finalize, + olddir, oldent, newdir, newent); +} + static int fuse_unlink_initialize_in(struct fuse_args *fa, struct fuse_unused_io *unused, struct inode *dir, struct dentry *entry) { diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 55ed3fb9d4a3..6ad0eb92de3b 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1116,6 +1116,10 @@ static int fuse_rename2(struct user_namespace *mnt_userns, struct inode *olddir, return -EINVAL; if (flags) { + if (fuse_bpf_rename2(&err, olddir, oldent, newdir, newent, flags)) + return err; + + /* TODO: how should this go with bpfs involved? */ if (fc->no_rename2 || fc->minor < 23) return -EINVAL; @@ -1127,6 +1131,9 @@ static int fuse_rename2(struct user_namespace *mnt_userns, struct inode *olddir, err = -EINVAL; } } else { + if (fuse_bpf_rename(&err, olddir, oldent, newdir, newent)) + return err; + err = fuse_rename_common(olddir, oldent, newdir, newent, 0, FUSE_RENAME, sizeof(struct fuse_rename_in)); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index cb087364e9bb..3338ac84d083 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1406,6 +1406,11 @@ int fuse_bpf_create_open(int *out, struct inode *dir, struct dentry *entry, int fuse_bpf_mknod(int *out, struct inode *dir, struct dentry *entry, umode_t mode, dev_t rdev); int fuse_bpf_mkdir(int *out, struct inode *dir, struct dentry *entry, umode_t mode); int fuse_bpf_rmdir(int *out, struct inode *dir, struct dentry *entry); +int fuse_bpf_rename2(int *out, struct inode *olddir, struct dentry *oldent, + struct inode *newdir, struct dentry *newent, + unsigned int flags); +int fuse_bpf_rename(int *out, struct inode *olddir, struct dentry *oldent, + struct inode *newdir, struct dentry *newent); int fuse_bpf_unlink(int *out, struct inode *dir, struct dentry *entry); int fuse_bpf_release(int *out, struct inode *inode, struct file *file); int fuse_bpf_releasedir(int *out, struct inode *inode, struct file *file); @@ -1448,6 +1453,19 @@ static inline int fuse_bpf_rmdir(int *out, struct inode *dir, struct dentry *ent return 0; } +static inline int fuse_bpf_rename2(int *out, struct inode *olddir, struct dentry *oldent, + struct inode *newdir, struct dentry *newent, + unsigned int flags) +{ + return 0; +} + +static inline int fuse_bpf_rename(int *out, struct inode *olddir, struct dentry *oldent, + struct inode *newdir, struct dentry *newent) +{ + return 0; +} + static inline int fuse_bpf_unlink(int *out, struct inode *dir, struct dentry *entry) { return 0; From patchwork Tue Nov 22 02:15:32 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13051838 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 5020DC433FE for ; Tue, 22 Nov 2022 02:20:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231788AbiKVCT7 (ORCPT ); Mon, 21 Nov 2022 21:19:59 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42682 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232647AbiKVCSY (ORCPT ); Mon, 21 Nov 2022 21:18:24 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6F735E8723 for ; Mon, 21 Nov 2022 18:16:34 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id e198-20020a2537cf000000b006e699f20c51so12384359yba.7 for ; Mon, 21 Nov 2022 18:16:34 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=vK6IEU0rl0A24X4GIdUEvjRzL9edAkzNEa8n9WiUMOU=; b=nMVdkL0Xs5RgzWRONSx7pNImE+bdCfORjqA/LqFEd467SrKJfeWf6eN7nWhCoYvm8i lKE791H13iGM9kcYULB5UfWIT7xYH8K0/qG2RNjqHoa3PwzjAgp201p8gC7hDeLwDbuI KRMgvcAME8kkAHQkq1IKt6UoKtL5lk/lrppet4xXxkvnIZ2jZRAIf1ZlzdvA9a9T+pVJ CnUcjD9wra0H99CSqH1Q8CksG93wEUjbHpF1gS0mzvHoaJLRNXhBi/dkAY0BQfB0sSqQ P5RX2FYAas3eiojJoqiNofyNGKolBNM/dV9mQ70c1wBz5OKscuESwcZHX6KvA+cdNL2V 4DpA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=vK6IEU0rl0A24X4GIdUEvjRzL9edAkzNEa8n9WiUMOU=; b=wmWVP2iQWgHM5eaZqBz4Jk/WNV/zoU7m3x1KrkOm0jLWommHgA6eDhncO0Y2IH6S62 n1o7mI52nC1j21RZ3x9UBOFapdZdwlm/KHB8+4iy8VK3Z99uCM7/01dzufAfSWv+X6wB a4b7uBP09K2j4/K67/oBrFbZp0TX/UQeN/0wI8mAJcklJyOJXBr8z0vN1gVhO35yyGms ajfVdGgLamJsrD7cZ+aj+/vI3+FNq1i53C4Bww4cuHGUPSr43NzS+7L5+599oDHVifZM CyEa5uBxUx8FUdwCgPiYY09qPb/mtYsMazpr8GeEVWlf+Id4bJfu4ObJL49q94lYFK+N fcSA== X-Gm-Message-State: ANoB5pl+ecCzgLkEzg55YCspWhh1Kc+6KXMkeCefV4UdyjxNz4+xtALW /sMxL4wvmtV/nv8UhuGgJMRyXIOeVfY= X-Google-Smtp-Source: AA0mqf4juP2sH3loBkPBVMzJjWmoJtUT4+2VIy9N+HQ0k5t+bQfVKfppkSGU+H9HgW3UD4/H4KKDeH9fU+Y= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:200:8539:aadd:13be:6e82]) (user=drosen job=sendgmr) by 2002:a81:4e14:0:b0:39d:5ff1:4418 with SMTP id c20-20020a814e14000000b0039d5ff14418mr2ywb.381.1669083393403; Mon, 21 Nov 2022 18:16:33 -0800 (PST) Date: Mon, 21 Nov 2022 18:15:32 -0800 In-Reply-To: <20221122021536.1629178-1-drosen@google.com> Mime-Version: 1.0 References: <20221122021536.1629178-1-drosen@google.com> X-Mailer: git-send-email 2.38.1.584.g0f3c55d4c2-goog Message-ID: <20221122021536.1629178-18-drosen@google.com> Subject: [RFC PATCH v2 17/21] fuse-bpf: Add attr support From: Daniel Rosenberg To: Miklos Szeredi Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, bpf@vger.kernel.org, kernel-team@android.com, Daniel Rosenberg , Paul Lawrence Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 281 ++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/dir.c | 68 ++--------- fs/fuse/fuse_i.h | 102 +++++++++++++++++ fs/fuse/inode.c | 17 +-- 4 files changed, 398 insertions(+), 70 deletions(-) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index 333181d6ad73..e2fe8c3aac2d 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -1949,6 +1949,287 @@ int fuse_bpf_unlink(int *out, struct inode *dir, struct dentry *entry) dir, entry); } +struct fuse_getattr_io { + struct fuse_getattr_in fgi; + struct fuse_attr_out fao; +}; + +static int fuse_getattr_initialize_in(struct fuse_args *fa, struct fuse_getattr_io *fgio, + const struct dentry *entry, struct kstat *stat, + u32 request_mask, unsigned int flags) +{ + fgio->fgi = (struct fuse_getattr_in) { + .getattr_flags = flags, + .fh = -1, /* TODO is this OK? */ + }; + + *fa = (struct fuse_args) { + .nodeid = get_node_id(entry->d_inode), + .opcode = FUSE_GETATTR, + .in_numargs = 1, + .in_args[0] = (struct fuse_in_arg) { + .size = sizeof(fgio->fgi), + .value = &fgio->fgi, + }, + }; + + return 0; +} + +static int fuse_getattr_initialize_out(struct fuse_args *fa, struct fuse_getattr_io *fgio, + const struct dentry *entry, struct kstat *stat, + u32 request_mask, unsigned int flags) +{ + fgio->fao = (struct fuse_attr_out) { 0 }; + + fa->out_numargs = 1; + fa->out_args[0] = (struct fuse_arg) { + .size = sizeof(fgio->fao), + .value = &fgio->fao, + }; + + return 0; +} + +static int fuse_getattr_backing(struct fuse_args *fa, int *out, + const struct dentry *entry, struct kstat *stat, + u32 request_mask, unsigned int flags) +{ + struct path *backing_path = &get_fuse_dentry(entry)->backing_path; + struct inode *backing_inode = backing_path->dentry->d_inode; + struct fuse_attr_out *fao = fa->out_args[0].value; + struct kstat tmp; + + if (!stat) + stat = &tmp; + + *out = vfs_getattr(backing_path, stat, request_mask, flags); + + if (!*out) + fuse_stat_to_attr(get_fuse_conn(entry->d_inode), backing_inode, + stat, &fao->attr); + + return 0; +} + +static int finalize_attr(struct inode *inode, struct fuse_attr_out *outarg, + u64 attr_version, struct kstat *stat) +{ + int err = 0; + + if (fuse_invalid_attr(&outarg->attr) || + ((inode->i_mode ^ outarg->attr.mode) & S_IFMT)) { + fuse_make_bad(inode); + err = -EIO; + } else { + fuse_change_attributes(inode, &outarg->attr, + attr_timeout(outarg), + attr_version); + if (stat) + fuse_fillattr(inode, &outarg->attr, stat); + } + return err; +} + +static int fuse_getattr_finalize(struct fuse_args *fa, int *out, + const struct dentry *entry, struct kstat *stat, + u32 request_mask, unsigned int flags) +{ + struct fuse_attr_out *outarg = fa->out_args[0].value; + struct inode *inode = entry->d_inode; + u64 attr_version = fuse_get_attr_version(get_fuse_mount(inode)->fc); + + /* TODO: Ensure this doesn't happen if we had an error getting attrs in + * backing. + */ + *out = finalize_attr(inode, outarg, attr_version, stat); + return 0; +} + +int fuse_bpf_getattr(int *out, struct inode *inode, const struct dentry *entry, struct kstat *stat, + u32 request_mask, unsigned int flags) +{ + return fuse_bpf_backing(inode, struct fuse_getattr_io, out, + fuse_getattr_initialize_in, fuse_getattr_initialize_out, + fuse_getattr_backing, fuse_getattr_finalize, + entry, stat, request_mask, flags); +} + +static void fattr_to_iattr(struct fuse_conn *fc, + const struct fuse_setattr_in *arg, + struct iattr *iattr) +{ + unsigned int fvalid = arg->valid; + + if (fvalid & FATTR_MODE) + iattr->ia_valid |= ATTR_MODE, iattr->ia_mode = arg->mode; + if (fvalid & FATTR_UID) { + iattr->ia_valid |= ATTR_UID; + iattr->ia_uid = make_kuid(fc->user_ns, arg->uid); + } + if (fvalid & FATTR_GID) { + iattr->ia_valid |= ATTR_GID; + iattr->ia_gid = make_kgid(fc->user_ns, arg->gid); + } + if (fvalid & FATTR_SIZE) + iattr->ia_valid |= ATTR_SIZE, iattr->ia_size = arg->size; + if (fvalid & FATTR_ATIME) { + iattr->ia_valid |= ATTR_ATIME; + iattr->ia_atime.tv_sec = arg->atime; + iattr->ia_atime.tv_nsec = arg->atimensec; + if (!(fvalid & FATTR_ATIME_NOW)) + iattr->ia_valid |= ATTR_ATIME_SET; + } + if (fvalid & FATTR_MTIME) { + iattr->ia_valid |= ATTR_MTIME; + iattr->ia_mtime.tv_sec = arg->mtime; + iattr->ia_mtime.tv_nsec = arg->mtimensec; + if (!(fvalid & FATTR_MTIME_NOW)) + iattr->ia_valid |= ATTR_MTIME_SET; + } + if (fvalid & FATTR_CTIME) { + iattr->ia_valid |= ATTR_CTIME; + iattr->ia_ctime.tv_sec = arg->ctime; + iattr->ia_ctime.tv_nsec = arg->ctimensec; + } +} + +struct fuse_setattr_io { + struct fuse_setattr_in fsi; + struct fuse_attr_out fao; +}; + +static int fuse_setattr_initialize_in(struct fuse_args *fa, struct fuse_setattr_io *fsio, + struct dentry *dentry, struct iattr *attr, struct file *file) +{ + struct fuse_conn *fc = get_fuse_conn(dentry->d_inode); + + *fsio = (struct fuse_setattr_io) { 0 }; + iattr_to_fattr(fc, attr, &fsio->fsi, true); + + *fa = (struct fuse_args) { + .opcode = FUSE_SETATTR, + .nodeid = get_node_id(dentry->d_inode), + .in_numargs = 1, + .in_args[0].size = sizeof(fsio->fsi), + .in_args[0].value = &fsio->fsi, + }; + + return 0; +} + +static int fuse_setattr_initialize_out(struct fuse_args *fa, struct fuse_setattr_io *fsio, + struct dentry *dentry, struct iattr *attr, struct file *file) +{ + fa->out_numargs = 1; + fa->out_args[0].size = sizeof(fsio->fao); + fa->out_args[0].value = &fsio->fao; + + return 0; +} + +static int fuse_setattr_backing(struct fuse_args *fa, int *out, + struct dentry *dentry, struct iattr *attr, struct file *file) +{ + struct fuse_conn *fc = get_fuse_conn(dentry->d_inode); + const struct fuse_setattr_in *fsi = fa->in_args[0].value; + struct iattr new_attr = { 0 }; + struct path *backing_path = &get_fuse_dentry(dentry)->backing_path; + + fattr_to_iattr(fc, fsi, &new_attr); + /* TODO: Some info doesn't get saved by the attr->fattr->attr transition + * When we actually allow the bpf to change these, we may have to consider + * the extra flags more, or pass more info into the bpf. Until then we can + * keep everything except for ATTR_FILE, since we'd need a file on the + * lower fs. For what it's worth, neither f2fs nor ext4 make use of that + * even if it is present. + */ + new_attr.ia_valid = attr->ia_valid & ~ATTR_FILE; + inode_lock(d_inode(backing_path->dentry)); + *out = notify_change(&init_user_ns, backing_path->dentry, &new_attr, + NULL); + inode_unlock(d_inode(backing_path->dentry)); + + if (*out == 0 && (new_attr.ia_valid & ATTR_SIZE)) + i_size_write(dentry->d_inode, new_attr.ia_size); + return 0; +} + +static int fuse_setattr_finalize(struct fuse_args *fa, int *out, + struct dentry *dentry, struct iattr *attr, struct file *file) +{ + return 0; +} + +int fuse_bpf_setattr(int *out, struct inode *inode, struct dentry *dentry, struct iattr *attr, struct file *file) +{ + return fuse_bpf_backing(inode, struct fuse_setattr_io, out, + fuse_setattr_initialize_in, fuse_setattr_initialize_out, + fuse_setattr_backing, fuse_setattr_finalize, dentry, attr, file); +} + +static int fuse_statfs_initialize_in(struct fuse_args *fa, struct fuse_statfs_out *fso, + struct dentry *dentry, struct kstatfs *buf) +{ + *fa = (struct fuse_args) { + .nodeid = get_node_id(d_inode(dentry)), + .opcode = FUSE_STATFS, + }; + + return 0; +} + +static int fuse_statfs_initialize_out(struct fuse_args *fa, struct fuse_statfs_out *fso, + struct dentry *dentry, struct kstatfs *buf) +{ + *fso = (struct fuse_statfs_out) { 0 }; + + fa->out_numargs = 1; + fa->out_args[0].size = sizeof(fso); + fa->out_args[0].value = fso; + + return 0; +} + +static int fuse_statfs_backing(struct fuse_args *fa, int *out, + struct dentry *dentry, struct kstatfs *buf) +{ + struct path backing_path; + struct fuse_statfs_out *fso = fa->out_args[0].value; + + *out = 0; + get_fuse_backing_path(dentry, &backing_path); + if (!backing_path.dentry) + return -EBADF; + *out = vfs_statfs(&backing_path, buf); + path_put(&backing_path); + buf->f_type = FUSE_SUPER_MAGIC; + + //TODO Provide postfilter opportunity to modify + if (!*out) + convert_statfs_to_fuse(&fso->st, buf); + + return 0; +} + +static int fuse_statfs_finalize(struct fuse_args *fa, int *out, + struct dentry *dentry, struct kstatfs *buf) +{ + struct fuse_statfs_out *fso = fa->out_args[0].value; + + if (!fa->error_in) + convert_fuse_statfs(buf, &fso->st); + return 0; +} + +int fuse_bpf_statfs(int *out, struct inode *inode, struct dentry *dentry, struct kstatfs *buf) +{ + return fuse_bpf_backing(dentry->d_inode, struct fuse_statfs_out, out, + fuse_statfs_initialize_in, fuse_statfs_initialize_out, + fuse_statfs_backing, fuse_statfs_finalize, + dentry, buf); +} + struct fuse_read_io { struct fuse_read_in fri; struct fuse_read_out fro; diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 6ad0eb92de3b..899de6c84c2e 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1168,7 +1168,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir, return err; } -static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr, +void fuse_fillattr(struct inode *inode, struct fuse_attr *attr, struct kstat *stat) { unsigned int blkbits; @@ -1245,6 +1245,7 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat, } static int fuse_update_get_attr(struct inode *inode, struct file *file, + const struct path *path, struct kstat *stat, u32 request_mask, unsigned int flags) { @@ -1254,6 +1255,9 @@ static int fuse_update_get_attr(struct inode *inode, struct file *file, u32 inval_mask = READ_ONCE(fi->inval_mask); u32 cache_mask = fuse_get_cache_mask(inode); + if (fuse_bpf_getattr(&err, inode, path->dentry, stat, request_mask, flags)) + return err; + if (flags & AT_STATX_FORCE_SYNC) sync = true; else if (flags & AT_STATX_DONT_SYNC) @@ -1277,7 +1281,7 @@ static int fuse_update_get_attr(struct inode *inode, struct file *file, int fuse_update_attributes(struct inode *inode, struct file *file, u32 mask) { - return fuse_update_get_attr(inode, file, NULL, mask, 0); + return fuse_update_get_attr(inode, file, &file->f_path, NULL, mask, 0); } int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, @@ -1639,58 +1643,6 @@ static long fuse_dir_compat_ioctl(struct file *file, unsigned int cmd, FUSE_IOCTL_COMPAT | FUSE_IOCTL_DIR); } -static inline bool update_mtime(unsigned int ivalid, bool trust_local_mtime) -{ - /* Always update if mtime is explicitly set */ - if (ivalid & ATTR_MTIME_SET) - return true; - - /* Or if kernel i_mtime is the official one */ - if (trust_local_mtime) - return true; - - /* If it's an open(O_TRUNC) or an ftruncate(), don't update */ - if ((ivalid & ATTR_SIZE) && (ivalid & (ATTR_OPEN | ATTR_FILE))) - return false; - - /* In all other cases update */ - return true; -} - -static void iattr_to_fattr(struct fuse_conn *fc, struct iattr *iattr, - struct fuse_setattr_in *arg, bool trust_local_cmtime) -{ - unsigned ivalid = iattr->ia_valid; - - if (ivalid & ATTR_MODE) - arg->valid |= FATTR_MODE, arg->mode = iattr->ia_mode; - if (ivalid & ATTR_UID) - arg->valid |= FATTR_UID, arg->uid = from_kuid(fc->user_ns, iattr->ia_uid); - if (ivalid & ATTR_GID) - arg->valid |= FATTR_GID, arg->gid = from_kgid(fc->user_ns, iattr->ia_gid); - if (ivalid & ATTR_SIZE) - arg->valid |= FATTR_SIZE, arg->size = iattr->ia_size; - if (ivalid & ATTR_ATIME) { - arg->valid |= FATTR_ATIME; - arg->atime = iattr->ia_atime.tv_sec; - arg->atimensec = iattr->ia_atime.tv_nsec; - if (!(ivalid & ATTR_ATIME_SET)) - arg->valid |= FATTR_ATIME_NOW; - } - if ((ivalid & ATTR_MTIME) && update_mtime(ivalid, trust_local_cmtime)) { - arg->valid |= FATTR_MTIME; - arg->mtime = iattr->ia_mtime.tv_sec; - arg->mtimensec = iattr->ia_mtime.tv_nsec; - if (!(ivalid & ATTR_MTIME_SET) && !trust_local_cmtime) - arg->valid |= FATTR_MTIME_NOW; - } - if ((ivalid & ATTR_CTIME) && trust_local_cmtime) { - arg->valid |= FATTR_CTIME; - arg->ctime = iattr->ia_ctime.tv_sec; - arg->ctimensec = iattr->ia_ctime.tv_nsec; - } -} - /* * Prevent concurrent writepages on inode * @@ -1805,6 +1757,9 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr, bool trust_local_cmtime = is_wb; bool fault_blocked = false; + if (fuse_bpf_setattr(&err, inode, dentry, attr, file)) + return err; + if (!fc->default_permissions) attr->ia_valid |= ATTR_FORCE; @@ -1984,7 +1939,8 @@ static int fuse_setattr(struct user_namespace *mnt_userns, struct dentry *entry, * ia_mode calculation may have used stale i_mode. * Refresh and recalculate. */ - ret = fuse_do_getattr(inode, NULL, file); + if (!fuse_bpf_getattr(&ret, inode, entry, NULL, 0, 0)) + ret = fuse_do_getattr(inode, NULL, file); if (ret) return ret; @@ -2041,7 +1997,7 @@ static int fuse_getattr(struct user_namespace *mnt_userns, return -EACCES; } - return fuse_update_get_attr(inode, NULL, stat, request_mask, flags); + return fuse_update_get_attr(inode, NULL, path, stat, request_mask, flags); } static const struct inode_operations fuse_dir_inode_operations = { diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 3338ac84d083..8ecaf55e4632 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1422,6 +1422,10 @@ int fuse_bpf_file_read_iter(ssize_t *out, struct inode *inode, struct kiocb *ioc int fuse_bpf_file_write_iter(ssize_t *out, struct inode *inode, struct kiocb *iocb, struct iov_iter *from); int fuse_bpf_file_fallocate(int *out, struct inode *inode, struct file *file, int mode, loff_t offset, loff_t length); int fuse_bpf_lookup(struct dentry **out, struct inode *dir, struct dentry *entry, unsigned int flags); +int fuse_bpf_getattr(int *out, struct inode *inode, const struct dentry *entry, struct kstat *stat, + u32 request_mask, unsigned int flags); +int fuse_bpf_setattr(int *out, struct inode *inode, struct dentry *dentry, struct iattr *attr, struct file *file); +int fuse_bpf_statfs(int *out, struct inode *inode, struct dentry *dentry, struct kstatfs *buf); int fuse_bpf_readdir(int *out, struct inode *inode, struct file *file, struct dir_context *ctx); int fuse_bpf_access(int *out, struct inode *inode, int mask); @@ -1521,6 +1525,22 @@ static inline int fuse_bpf_lookup(struct dentry **out, struct inode *dir, struct return 0; } +static inline int fuse_bpf_getattr(int *out, struct inode *inode, const struct dentry *entry, struct kstat *stat, + u32 request_mask, unsigned int flags) +{ + return 0; +} + +static inline int fuse_bpf_setattr(int *out, struct inode *inode, struct dentry *dentry, struct iattr *attr, struct file *file) +{ + return 0; +} + +static inline int fuse_bpf_statfs(int *out, struct inode *inode, struct dentry *dentry, struct kstatfs *buf) +{ + return 0; +} + static inline int fuse_bpf_readdir(int *out, struct inode *inode, struct file *file, struct dir_context *ctx) { return 0; @@ -1566,6 +1586,88 @@ static inline u64 attr_timeout(struct fuse_attr_out *o) return time_to_jiffies(o->attr_valid, o->attr_valid_nsec); } +static inline bool update_mtime(unsigned int ivalid, bool trust_local_mtime) +{ + /* Always update if mtime is explicitly set */ + if (ivalid & ATTR_MTIME_SET) + return true; + + /* Or if kernel i_mtime is the official one */ + if (trust_local_mtime) + return true; + + /* If it's an open(O_TRUNC) or an ftruncate(), don't update */ + if ((ivalid & ATTR_SIZE) && (ivalid & (ATTR_OPEN | ATTR_FILE))) + return false; + + /* In all other cases update */ + return true; +} + +void fuse_fillattr(struct inode *inode, struct fuse_attr *attr, + struct kstat *stat); + +static inline void iattr_to_fattr(struct fuse_conn *fc, struct iattr *iattr, + struct fuse_setattr_in *arg, bool trust_local_cmtime) +{ + unsigned int ivalid = iattr->ia_valid; + + if (ivalid & ATTR_MODE) + arg->valid |= FATTR_MODE, arg->mode = iattr->ia_mode; + if (ivalid & ATTR_UID) + arg->valid |= FATTR_UID, arg->uid = from_kuid(fc->user_ns, iattr->ia_uid); + if (ivalid & ATTR_GID) + arg->valid |= FATTR_GID, arg->gid = from_kgid(fc->user_ns, iattr->ia_gid); + if (ivalid & ATTR_SIZE) + arg->valid |= FATTR_SIZE, arg->size = iattr->ia_size; + if (ivalid & ATTR_ATIME) { + arg->valid |= FATTR_ATIME; + arg->atime = iattr->ia_atime.tv_sec; + arg->atimensec = iattr->ia_atime.tv_nsec; + if (!(ivalid & ATTR_ATIME_SET)) + arg->valid |= FATTR_ATIME_NOW; + } + if ((ivalid & ATTR_MTIME) && update_mtime(ivalid, trust_local_cmtime)) { + arg->valid |= FATTR_MTIME; + arg->mtime = iattr->ia_mtime.tv_sec; + arg->mtimensec = iattr->ia_mtime.tv_nsec; + if (!(ivalid & ATTR_MTIME_SET) && !trust_local_cmtime) + arg->valid |= FATTR_MTIME_NOW; + } + if ((ivalid & ATTR_CTIME) && trust_local_cmtime) { + arg->valid |= FATTR_CTIME; + arg->ctime = iattr->ia_ctime.tv_sec; + arg->ctimensec = iattr->ia_ctime.tv_nsec; + } +} + +static inline void convert_statfs_to_fuse(struct fuse_kstatfs *attr, struct kstatfs *stbuf) +{ + attr->bsize = stbuf->f_bsize; + attr->frsize = stbuf->f_frsize; + attr->blocks = stbuf->f_blocks; + attr->bfree = stbuf->f_bfree; + attr->bavail = stbuf->f_bavail; + attr->files = stbuf->f_files; + attr->ffree = stbuf->f_ffree; + attr->namelen = stbuf->f_namelen; + /* fsid is left zero */ +} + +static inline void convert_fuse_statfs(struct kstatfs *stbuf, struct fuse_kstatfs *attr) +{ + stbuf->f_type = FUSE_SUPER_MAGIC; + stbuf->f_bsize = attr->bsize; + stbuf->f_frsize = attr->frsize; + stbuf->f_blocks = attr->blocks; + stbuf->f_bfree = attr->bfree; + stbuf->f_bavail = attr->bavail; + stbuf->f_files = attr->files; + stbuf->f_ffree = attr->ffree; + stbuf->f_namelen = attr->namelen; + /* fsid is left zero */ +} + #ifdef CONFIG_FUSE_BPF int __init fuse_bpf_init(void); void __exit fuse_bpf_cleanup(void); diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 9781faff6df6..1e7d45977144 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -616,20 +616,6 @@ static void fuse_send_destroy(struct fuse_mount *fm) } } -static void convert_fuse_statfs(struct kstatfs *stbuf, struct fuse_kstatfs *attr) -{ - stbuf->f_type = FUSE_SUPER_MAGIC; - stbuf->f_bsize = attr->bsize; - stbuf->f_frsize = attr->frsize; - stbuf->f_blocks = attr->blocks; - stbuf->f_bfree = attr->bfree; - stbuf->f_bavail = attr->bavail; - stbuf->f_files = attr->files; - stbuf->f_ffree = attr->ffree; - stbuf->f_namelen = attr->namelen; - /* fsid is left zero */ -} - static int fuse_statfs(struct dentry *dentry, struct kstatfs *buf) { struct super_block *sb = dentry->d_sb; @@ -643,6 +629,9 @@ static int fuse_statfs(struct dentry *dentry, struct kstatfs *buf) return 0; } + if (fuse_bpf_statfs(&err, dentry->d_inode, dentry, buf)) + return err; + memset(&outarg, 0, sizeof(outarg)); args.in_numargs = 0; args.opcode = FUSE_STATFS; From patchwork Tue Nov 22 02:15:33 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13051839 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 0B972C43219 for ; Tue, 22 Nov 2022 02:20:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231946AbiKVCUA (ORCPT ); Mon, 21 Nov 2022 21:20:00 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42862 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231911AbiKVCTA (ORCPT ); Mon, 21 Nov 2022 21:19:00 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5E09EE2B41 for ; Mon, 21 Nov 2022 18:16:36 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id y6-20020a25b9c6000000b006c1c6161716so12407510ybj.8 for ; Mon, 21 Nov 2022 18:16:36 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=JFeUA3PbRtSHLNTEDynEdKNNWrqo68m/Sv/Ri911v1M=; b=jg7q/4HfLHsfBt1WL7tTNuB5B3bVu3bIH4ZmZLOvwtQ++pw/Tx1jZZa4cToMI7GJuy /+lbOSx4g82Rlg9a5y/azwCt2yFOG98n9bt6C1ANmyhGJMlJppMM0j7/OkqkOY8Em2Pn ACwO37txtQ7G0zdyJFLcw3XG2ghjR6lyLUPfcylAaC8vAuJdx7dhnSLyv+no1VmDeTWi 6x51K9Xxu5mQSw28bsPkIZ4RefCbZJ2aA8BR8/zYcigYEEaPEl4Vfh4Ii4NdyiJlzW+2 gqqz3L8Phg2zStOowpAC0DLKwASkP01kQ/jECUID7+e7lkmH/w1sWUT5Upoo7ejloHCI YOkg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=JFeUA3PbRtSHLNTEDynEdKNNWrqo68m/Sv/Ri911v1M=; b=3oOtXdATsl781VNWj42Vwx4MMnSi+DkaWKFJxWylkLJThBu2Bh2+DCW/unbkexFzos tNgPwHkgPK3MEWxYHoOP30DkykuqMbECT6Kf1liiqV9WW8hG7auV6mpTb2iERKLzRbWv aAKVwBjWYRILwOD0Yktmc3SMXcSuW30+KT7D9d2CxkdgETZ4NOKEa+6M4Q1D7XEuAXek k2MBXFtvCuMKyVA3CSLF0aj+HZdQbcc/kej2qAf0fsLLquy1h3UJC/B7YRme0mbI4nfx 9Mf9DG6m17zLXiQzI/aKr+UKKc8eRg2835EJsm1Ni4XGxBJfCXINaauGa3PvcU3KJROg SxGw== X-Gm-Message-State: ANoB5pkFE+OGsue3GH4e3aCtErGASx4viBB/MtalEzfrPg9m6IVIQJEr /7l1O1Iwcp2FxuCo5tshtXkVAC4fa4k= X-Google-Smtp-Source: AA0mqf4OS7F3eu10sNB7hfjo5VTZ9mtZoxQ9rM/JwW9FGxFhh+kWW83TQuk9YPaDe/K1Dc9yRli+1XuvdQU= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:200:8539:aadd:13be:6e82]) (user=drosen job=sendgmr) by 2002:a05:6902:1201:b0:6ec:73bd:1376 with SMTP id s1-20020a056902120100b006ec73bd1376mr1ybu.40.1669083395835; Mon, 21 Nov 2022 18:16:35 -0800 (PST) Date: Mon, 21 Nov 2022 18:15:33 -0800 In-Reply-To: <20221122021536.1629178-1-drosen@google.com> Mime-Version: 1.0 References: <20221122021536.1629178-1-drosen@google.com> X-Mailer: git-send-email 2.38.1.584.g0f3c55d4c2-goog Message-ID: <20221122021536.1629178-19-drosen@google.com> Subject: [RFC PATCH v2 18/21] fuse-bpf: Add support for FUSE_COPY_FILE_RANGE From: Daniel Rosenberg To: Miklos Szeredi Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, bpf@vger.kernel.org, kernel-team@android.com, Daniel Rosenberg , Paul Lawrence Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/file.c | 4 +++ fs/fuse/fuse_i.h | 10 ++++++ 3 files changed, 99 insertions(+) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index e2fe8c3aac2d..36c8688c4463 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -792,6 +792,91 @@ int fuse_bpf_lseek(loff_t *out, struct inode *inode, struct file *file, loff_t o file, offset, whence); } +struct fuse_copy_file_range_io { + struct fuse_copy_file_range_in fci; + struct fuse_write_out fwo; +}; + +static int fuse_copy_file_range_initialize_in(struct fuse_args *fa, + struct fuse_copy_file_range_io *fcf, + struct file *file_in, loff_t pos_in, struct file *file_out, + loff_t pos_out, size_t len, unsigned int flags) +{ + struct fuse_file *fuse_file_in = file_in->private_data; + struct fuse_file *fuse_file_out = file_out->private_data; + + fcf->fci = (struct fuse_copy_file_range_in) { + .fh_in = fuse_file_in->fh, + .off_in = pos_in, + .nodeid_out = fuse_file_out->nodeid, + .fh_out = fuse_file_out->fh, + .off_out = pos_out, + .len = len, + .flags = flags, + }; + + *fa = (struct fuse_args) { + .nodeid = get_node_id(file_in->f_inode), + .opcode = FUSE_COPY_FILE_RANGE, + .in_numargs = 1, + .in_args[0].size = sizeof(fcf->fci), + .in_args[0].value = &fcf->fci, + }; + + return 0; +} + +static int fuse_copy_file_range_initialize_out(struct fuse_args *fa, + struct fuse_copy_file_range_io *fcf, + struct file *file_in, loff_t pos_in, struct file *file_out, + loff_t pos_out, size_t len, unsigned int flags) +{ + fa->out_numargs = 1; + fa->out_args[0].size = sizeof(fcf->fwo); + fa->out_args[0].value = &fcf->fwo; + + return 0; +} + +static int fuse_copy_file_range_backing(struct fuse_args *fa, ssize_t *out, struct file *file_in, + loff_t pos_in, struct file *file_out, loff_t pos_out, size_t len, + unsigned int flags) +{ + const struct fuse_copy_file_range_in *fci = fa->in_args[0].value; + struct fuse_file *fuse_file_in = file_in->private_data; + struct file *backing_file_in = fuse_file_in->backing_file; + struct fuse_file *fuse_file_out = file_out->private_data; + struct file *backing_file_out = fuse_file_out->backing_file; + + /* TODO: Handle changing of in/out files */ + if (backing_file_out) + *out = vfs_copy_file_range(backing_file_in, fci->off_in, backing_file_out, + fci->off_out, fci->len, fci->flags); + else + *out = generic_copy_file_range(file_in, pos_in, file_out, pos_out, len, + flags); + return 0; +} + +static int fuse_copy_file_range_finalize(struct fuse_args *fa, ssize_t *out, struct file *file_in, + loff_t pos_in, struct file *file_out, loff_t pos_out, size_t len, + unsigned int flags) +{ + return 0; +} + +int fuse_bpf_copy_file_range(ssize_t *out, struct inode *inode, struct file *file_in, + loff_t pos_in, struct file *file_out, loff_t pos_out, size_t len, + unsigned int flags) +{ + return fuse_bpf_backing(inode, struct fuse_copy_file_range_io, out, + fuse_copy_file_range_initialize_in, + fuse_copy_file_range_initialize_out, + fuse_copy_file_range_backing, + fuse_copy_file_range_finalize, + file_in, pos_in, file_out, pos_out, len, flags); +} + static int fuse_fsync_initialize_in(struct fuse_args *fa, struct fuse_fsync_in *ffi, struct file *file, loff_t start, loff_t end, int datasync) { diff --git a/fs/fuse/file.c b/fs/fuse/file.c index fa9ee2740a42..8153e78ff1d6 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -3127,6 +3127,10 @@ static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in, bool is_unstable = (!fc->writeback_cache) && ((pos_out + len) > inode_out->i_size); + if (fuse_bpf_copy_file_range(&err, file_inode(file_in), file_in, pos_in, + file_out, pos_out, len, flags)) + return err; + if (fc->no_copy_file_range) return -EOPNOTSUPP; diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 8ecaf55e4632..275b649bb5ed 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1416,6 +1416,9 @@ int fuse_bpf_release(int *out, struct inode *inode, struct file *file); int fuse_bpf_releasedir(int *out, struct inode *inode, struct file *file); int fuse_bpf_flush(int *out, struct inode *inode, struct file *file, fl_owner_t id); int fuse_bpf_lseek(loff_t *out, struct inode *inode, struct file *file, loff_t offset, int whence); +int fuse_bpf_copy_file_range(ssize_t *out, struct inode *inode, struct file *file_in, loff_t pos_in, + struct file *file_out, loff_t pos_out, + size_t len, unsigned int flags); int fuse_bpf_fsync(int *out, struct inode *inode, struct file *file, loff_t start, loff_t end, int datasync); int fuse_bpf_dir_fsync(int *out, struct inode *inode, struct file *file, loff_t start, loff_t end, int datasync); int fuse_bpf_file_read_iter(ssize_t *out, struct inode *inode, struct kiocb *iocb, struct iov_iter *to); @@ -1495,6 +1498,13 @@ static inline int fuse_bpf_lseek(loff_t *out, struct inode *inode, struct file * return 0; } +static inline int fuse_bpf_copy_file_range(ssize_t *out, struct inode *inode, struct file *file_in, loff_t pos_in, + struct file *file_out, loff_t pos_out, + size_t len, unsigned int flags) +{ + return 0; +} + static inline int fuse_bpf_fsync(int *out, struct inode *inode, struct file *file, loff_t start, loff_t end, int datasync) { return 0; From patchwork Tue Nov 22 02:15:34 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13051840 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 1E475C433FE for ; Tue, 22 Nov 2022 02:20:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231981AbiKVCUE (ORCPT ); Mon, 21 Nov 2022 21:20:04 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42722 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232645AbiKVCTN (ORCPT ); Mon, 21 Nov 2022 21:19:13 -0500 Received: from mail-yw1-x114a.google.com (mail-yw1-x114a.google.com [IPv6:2607:f8b0:4864:20::114a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E10D2E8731 for ; Mon, 21 Nov 2022 18:16:38 -0800 (PST) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-3a0e59c5ad7so44821357b3.20 for ; Mon, 21 Nov 2022 18:16:38 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=0L2EwBSVK6gMt275n9P0FS+q+J4F8LKg12isFlJjWcc=; b=b2iwtmjGXWqJQaJjBO+sHKmft5jbP35hxZFYnha0+A9WixhUAm4CdAuHnP0ktaGaw4 DDxlj91SlXqXiMIedrhYVc2MuXzDPTsnIapc/W6VBjn+gCAUuxdwguG/qDR0gk2IMiFx 4zlKrB5b/CIOsG6Yg3noh99/LJ+k8c1790XSDPWNzJytJ674BCoCHnx1vgBpMOFC61qD UeJr1CjS0bLFcMtQZ4tCQ2SiMd/ng2/ofy1NpEx9Qe7R9S2LRnZG4EncOLnYnc8GdMs0 CR3tu66M4z4AqJQB6L3NkrD9AQ05bbvhFJXPpmH/7syYS4jeqfUywFufcq8EY9q8BLLN vk/w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=0L2EwBSVK6gMt275n9P0FS+q+J4F8LKg12isFlJjWcc=; b=Xqd8QoahVE0/mtm24fV2AL6/zKWSci9rLUqgAmimCmzB+bK3whLqo993qRrvwyjXot Py5MLeWf5G3C5649KOCzTz07R5jc7k3n+zkHhycsD9sm+tIqhq/EJJA4axyI6bqQBKFN R4Bg3JULQyz/KalExIMyk2itl9ZAOXXas5WQ/nnh0DzvzkUOr2JSxUcWsy/Fx0lLNPlv CeCrlh5ACTVf6ftN0vGIXpJrkp0zrFlXBazY96Cg+9kIZ+2Rrhw4QxtypW3DI3EK4U9s OSz18AxCiIdUxfnyzKQTEBM3As47JVWQEq0JNZCmoI4yI+n8v5/9NlhYIV5OgKZ6VLPS JSYA== X-Gm-Message-State: ANoB5pnfbG0pw/ethHCK/+zEnpc1PAQkpm7NxpFX9AHeLNe5H10W4PxI 2He7TKDgFwwRnMYkYpPExXvAJ2WzAGk= X-Google-Smtp-Source: AA0mqf4pEXHKRMLLpP2NZX30KTDAY/ErO0cp90kyzkQhus0BdDLQ4WHMIDRUYeTiMXMyrFbVNmQccySgIJE= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:200:8539:aadd:13be:6e82]) (user=drosen job=sendgmr) by 2002:a25:ab14:0:b0:6e9:916d:3f4d with SMTP id u20-20020a25ab14000000b006e9916d3f4dmr6ybi.346.1669083398353; Mon, 21 Nov 2022 18:16:38 -0800 (PST) Date: Mon, 21 Nov 2022 18:15:34 -0800 In-Reply-To: <20221122021536.1629178-1-drosen@google.com> Mime-Version: 1.0 References: <20221122021536.1629178-1-drosen@google.com> X-Mailer: git-send-email 2.38.1.584.g0f3c55d4c2-goog Message-ID: <20221122021536.1629178-20-drosen@google.com> Subject: [RFC PATCH v2 19/21] fuse-bpf: Add xattr support From: Daniel Rosenberg To: Miklos Szeredi Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, bpf@vger.kernel.org, kernel-team@android.com, Daniel Rosenberg , Paul Lawrence Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 285 ++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/fuse_i.h | 30 +++++ fs/fuse/xattr.c | 18 +++ 3 files changed, 333 insertions(+) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index 36c8688c4463..05fb88865289 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -967,6 +967,291 @@ int fuse_bpf_dir_fsync(int *out, struct inode *inode, struct file *file, loff_t file, start, end, datasync); } +struct fuse_getxattr_io { + struct fuse_getxattr_in fgi; + struct fuse_getxattr_out fgo; +}; + +static int fuse_getxattr_initialize_in(struct fuse_args *fa, + struct fuse_getxattr_io *fgio, + struct dentry *dentry, const char *name, void *value, + size_t size) +{ + *fgio = (struct fuse_getxattr_io) { + .fgi.size = size, + }; + + *fa = (struct fuse_args) { + .nodeid = get_fuse_inode(dentry->d_inode)->nodeid, + .opcode = FUSE_GETXATTR, + .in_numargs = 2, + .in_args[0] = (struct fuse_in_arg) { + .size = sizeof(fgio->fgi), + .value = &fgio->fgi, + }, + .in_args[1] = (struct fuse_in_arg) { + .size = strlen(name) + 1, + .value = (void *) name, + }, + }; + + return 0; +} + +static int fuse_getxattr_initialize_out(struct fuse_args *fa, + struct fuse_getxattr_io *fgio, + struct dentry *dentry, const char *name, void *value, + size_t size) +{ + fa->out_numargs = 1; + if (size) { + fa->out_argvar = true; + fa->out_args[0].size = size; + fa->out_args[0].value = value; + } else { + fa->out_args[0].size = sizeof(fgio->fgo); + fa->out_args[0].value = &fgio->fgo; + } + return 0; +} + +static int fuse_getxattr_backing(struct fuse_args *fa, int *out, + struct dentry *dentry, const char *name, void *value, + size_t size) +{ + ssize_t ret = vfs_getxattr(&init_user_ns, + get_fuse_dentry(dentry)->backing_path.dentry, + fa->in_args[1].value, value, size); + + if (fa->out_argvar) + fa->out_args[0].size = ret; + else + ((struct fuse_getxattr_out *)fa->out_args[0].value)->size = ret; + + return 0; +} + +static int fuse_getxattr_finalize(struct fuse_args *fa, int *out, + struct dentry *dentry, const char *name, void *value, + size_t size) +{ + struct fuse_getxattr_out *fgo; + + if (fa->out_argvar) { + *out = fa->out_args[0].size; + return 0; + } + + fgo = fa->out_args[0].value; + + *out = fgo->size; + return 0; +} + +int fuse_bpf_getxattr(int *out, struct inode *inode, struct dentry *dentry, const char *name, + void *value, size_t size) +{ + return fuse_bpf_backing(inode, struct fuse_getxattr_io, out, + fuse_getxattr_initialize_in, fuse_getxattr_initialize_out, + fuse_getxattr_backing, fuse_getxattr_finalize, + dentry, name, value, size); +} + +static int fuse_listxattr_initialize_in(struct fuse_args *fa, + struct fuse_getxattr_io *fgio, + struct dentry *dentry, char *list, size_t size) +{ + *fgio = (struct fuse_getxattr_io) { + .fgi.size = size, + }; + + *fa = (struct fuse_args) { + .nodeid = get_fuse_inode(dentry->d_inode)->nodeid, + .opcode = FUSE_LISTXATTR, + .in_numargs = 1, + .in_args[0] = + (struct fuse_in_arg) { + .size = sizeof(fgio->fgi), + .value = &fgio->fgi, + }, + }; + + return 0; +} + +static int fuse_listxattr_initialize_out(struct fuse_args *fa, + struct fuse_getxattr_io *fgio, + struct dentry *dentry, char *list, size_t size) +{ + fa->out_numargs = 1; + + if (size) { + fa->out_argvar = true; + fa->out_args[0].size = size; + fa->out_args[0].value = (void *)list; + } else { + fa->out_args[0].size = sizeof(fgio->fgo); + fa->out_args[0].value = &fgio->fgo; + } + return 0; +} + +static int fuse_listxattr_backing(struct fuse_args *fa, ssize_t *out, struct dentry *dentry, + char *list, size_t size) +{ + *out = vfs_listxattr(get_fuse_dentry(dentry)->backing_path.dentry, list, size); + + if (*out < 0) + return *out; + + if (fa->out_argvar) + fa->out_args[0].size = *out; + else + ((struct fuse_getxattr_out *)fa->out_args[0].value)->size = *out; + + return 0; +} + +static int fuse_listxattr_finalize(struct fuse_args *fa, ssize_t *out, struct dentry *dentry, + char *list, size_t size) +{ + struct fuse_getxattr_out *fgo; + + if (fa->error_in) + return 0; + + if (fa->out_argvar) { + *out = fa->out_args[0].size; + return 0; + } + + fgo = fa->out_args[0].value; + *out = fgo->size; + return 0; +} + +int fuse_bpf_listxattr(ssize_t *out, struct inode *inode, struct dentry *dentry, + char *list, size_t size) +{ + return fuse_bpf_backing(inode, struct fuse_getxattr_io, out, + fuse_listxattr_initialize_in, fuse_listxattr_initialize_out, + fuse_listxattr_backing, fuse_listxattr_finalize, + dentry, list, size); +} + +static int fuse_setxattr_initialize_in(struct fuse_args *fa, + struct fuse_setxattr_in *fsxi, + struct dentry *dentry, const char *name, + const void *value, size_t size, int flags) +{ + *fsxi = (struct fuse_setxattr_in) { + .size = size, + .flags = flags, + }; + + *fa = (struct fuse_args) { + .nodeid = get_fuse_inode(dentry->d_inode)->nodeid, + .opcode = FUSE_SETXATTR, + .in_numargs = 3, + .in_args[0] = (struct fuse_in_arg) { + .size = sizeof(*fsxi), + .value = fsxi, + }, + .in_args[1] = (struct fuse_in_arg) { + .size = strlen(name) + 1, + .value = (void *) name, + }, + .in_args[2] = (struct fuse_in_arg) { + .size = size, + .value = (void *) value, + }, + }; + + return 0; +} + +static int fuse_setxattr_initialize_out(struct fuse_args *fa, + struct fuse_setxattr_in *fsxi, + struct dentry *dentry, const char *name, + const void *value, size_t size, int flags) +{ + return 0; +} + +static int fuse_setxattr_backing(struct fuse_args *fa, int *out, struct dentry *dentry, + const char *name, const void *value, size_t size, + int flags) +{ + *out = vfs_setxattr(&init_user_ns, + get_fuse_dentry(dentry)->backing_path.dentry, name, + value, size, flags); + return 0; +} + +static int fuse_setxattr_finalize(struct fuse_args *fa, int *out, struct dentry *dentry, + const char *name, const void *value, size_t size, + int flags) +{ + return 0; +} + +int fuse_bpf_setxattr(int *out, struct inode *inode, struct dentry *dentry, + const char *name, const void *value, size_t size, int flags) +{ + return fuse_bpf_backing(inode, struct fuse_setxattr_in, out, + fuse_setxattr_initialize_in, fuse_setxattr_initialize_out, + fuse_setxattr_backing, fuse_setxattr_finalize, + dentry, name, value, size, flags); +} + +static int fuse_removexattr_initialize_in(struct fuse_args *fa, + struct fuse_unused_io *unused, + struct dentry *dentry, const char *name) +{ + *fa = (struct fuse_args) { + .nodeid = get_fuse_inode(dentry->d_inode)->nodeid, + .opcode = FUSE_REMOVEXATTR, + .in_numargs = 1, + .in_args[0] = (struct fuse_in_arg) { + .size = strlen(name) + 1, + .value = (void *) name, + }, + }; + + return 0; +} + +static int fuse_removexattr_initialize_out(struct fuse_args *fa, + struct fuse_unused_io *unused, + struct dentry *dentry, const char *name) +{ + return 0; +} + +static int fuse_removexattr_backing(struct fuse_args *fa, int *out, + struct dentry *dentry, const char *name) +{ + struct path *backing_path = &get_fuse_dentry(dentry)->backing_path; + + /* TODO account for changes of the name by prefilter */ + *out = vfs_removexattr(&init_user_ns, backing_path->dentry, name); + return 0; +} + +static int fuse_removexattr_finalize(struct fuse_args *fa, int *out, + struct dentry *dentry, const char *name) +{ + return 0; +} + +int fuse_bpf_removexattr(int *out, struct inode *inode, struct dentry *dentry, const char *name) +{ + return fuse_bpf_backing(inode, struct fuse_unused_io, out, + fuse_removexattr_initialize_in, fuse_removexattr_initialize_out, + fuse_removexattr_backing, fuse_removexattr_finalize, + dentry, name); +} + static inline void fuse_bpf_aio_put(struct fuse_bpf_aio_req *aio_req) { if (refcount_dec_and_test(&aio_req->ref)) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 275b649bb5ed..37b29a3ea330 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1421,6 +1421,13 @@ int fuse_bpf_copy_file_range(ssize_t *out, struct inode *inode, struct file *fil size_t len, unsigned int flags); int fuse_bpf_fsync(int *out, struct inode *inode, struct file *file, loff_t start, loff_t end, int datasync); int fuse_bpf_dir_fsync(int *out, struct inode *inode, struct file *file, loff_t start, loff_t end, int datasync); +int fuse_bpf_getxattr(int *out, struct inode *inode, struct dentry *dentry, + const char *name, void *value, size_t size); +int fuse_bpf_listxattr(ssize_t *out, struct inode *inode, struct dentry *dentry, char *list, size_t size); +int fuse_bpf_setxattr(int *out, struct inode *inode, struct dentry *dentry, + const char *name, const void *value, size_t size, + int flags); +int fuse_bpf_removexattr(int *out, struct inode *inode, struct dentry *dentry, const char *name); int fuse_bpf_file_read_iter(ssize_t *out, struct inode *inode, struct kiocb *iocb, struct iov_iter *to); int fuse_bpf_file_write_iter(ssize_t *out, struct inode *inode, struct kiocb *iocb, struct iov_iter *from); int fuse_bpf_file_fallocate(int *out, struct inode *inode, struct file *file, int mode, loff_t offset, loff_t length); @@ -1515,6 +1522,29 @@ static inline int fuse_bpf_dir_fsync(int *out, struct inode *inode, struct file return 0; } +static inline int fuse_bpf_getxattr(int *out, struct inode *inode, struct dentry *dentry, + const char *name, void *value, size_t size) +{ + return 0; +} + +static inline int fuse_bpf_listxattr(ssize_t *out, struct inode *inode, struct dentry *dentry, char *list, size_t size) +{ + return 0; +} + +static inline int fuse_bpf_setxattr(int *out, struct inode *inode, struct dentry *dentry, + const char *name, const void *value, size_t size, + int flags) +{ + return 0; +} + +static inline int fuse_bpf_removexattr(int *out, struct inode *inode, struct dentry *dentry, const char *name) +{ + return 0; +} + static inline int fuse_bpf_file_read_iter(ssize_t *out, struct inode *inode, struct kiocb *iocb, struct iov_iter *to) { return 0; diff --git a/fs/fuse/xattr.c b/fs/fuse/xattr.c index 0d3e7177fce0..857e7d3a0dab 100644 --- a/fs/fuse/xattr.c +++ b/fs/fuse/xattr.c @@ -118,6 +118,9 @@ ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size) if (fuse_is_bad(inode)) return -EIO; + if (fuse_bpf_listxattr(&ret, inode, entry, list, size)) + return ret; + if (!fuse_allow_current_process(fm->fc)) return -EACCES; @@ -182,9 +185,14 @@ static int fuse_xattr_get(const struct xattr_handler *handler, struct dentry *dentry, struct inode *inode, const char *name, void *value, size_t size) { + int err; + if (fuse_is_bad(inode)) return -EIO; + if (fuse_bpf_getxattr(&err, inode, dentry, name, value, size)) + return err; + return fuse_getxattr(inode, name, value, size); } @@ -194,9 +202,19 @@ static int fuse_xattr_set(const struct xattr_handler *handler, const char *name, const void *value, size_t size, int flags) { + int err; + bool handled; + if (fuse_is_bad(inode)) return -EIO; + if (value) + handled = fuse_bpf_setxattr(&err, inode, dentry, name, value, size, flags); + else + handled = fuse_bpf_removexattr(&err, inode, dentry, name); + if (handled) + return err; + if (!value) return fuse_removexattr(inode, name); From patchwork Tue Nov 22 02:15:35 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13051841 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 7CE44C4332F for ; Tue, 22 Nov 2022 02:20:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232696AbiKVCUo (ORCPT ); Mon, 21 Nov 2022 21:20:44 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42630 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232561AbiKVCTi (ORCPT ); Mon, 21 Nov 2022 21:19:38 -0500 Received: from mail-yw1-x1149.google.com (mail-yw1-x1149.google.com [IPv6:2607:f8b0:4864:20::1149]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B2BEDE9145 for ; Mon, 21 Nov 2022 18:16:41 -0800 (PST) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-39ce6773614so58076787b3.2 for ; Mon, 21 Nov 2022 18:16:41 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=xynMcMsTuZqMLXL5ELmNHCW5CNZGRvCuOQzJlNlxPiE=; b=OUOhjEq4CnL9PVEr0QtLZtsjmGIU43rGzRaSI9yqkOCe/Tq6OgBYhpEJftwd6UWyQ/ 5eeE3FmCGczyreI5r/W4v9N5jqJoGlmQM2q02oW82bjyNtSfMiVAKuuRlyzzRCpqIjcK JqE5A8cs7Nov5Mp1Pr5ppPvmjSjUDvqdNwrEJcxCVXa1nPeGGAqVPzHzrRRvLfekRmG7 QN92GztkE/9jyU1ktQ0mdS+rYMrfjbUKE1XfIg7zDourWrv+MzvWMpNiFOUi1VzoyY63 oGRcclGwP2xBOMc//CIF4OX7JWonfe7495+rdT4v40NBqMMb/aLuZjA/x9Bb7/9mHKQn NAIQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=xynMcMsTuZqMLXL5ELmNHCW5CNZGRvCuOQzJlNlxPiE=; b=TOl3u2c+uohwS1FSyKqHfrxygLhg7kGE8K7B4VW9mWx2IAnYdmdcpcfX0OGB6KD45D KwnztPWKMx/Tp7ocFF8kypCQIsGiVblKhxnb+LepVw8wmB/uwF8R+5g70BATdvPIemR7 1501okgUtpXe8tbx7r96YqdfqYI+jQGvFinkukdiiAqJqj4sAnAsj/O2pNBGx1nVxNgV KpfZdfXaV22S9HrXGEKBG3MqEkHdcDBcBBn3Sv9PtgX+raYF9yqDYzghFJRIM3/QuNOu nUodapfbi3OYhtsiLtIdDm2uCYra2GtJeiFYGwROSGvcCWmn5Do4CvbLJcfI2at6EjIq YVZA== X-Gm-Message-State: ANoB5pkGQpjX7MVx2benRCGbaeKHfnsgEvBrXZDrmbw69SQZcX/+9c6O 4g4rUzAvxtwjuyzrrQJSOsSJVxtnpTQ= X-Google-Smtp-Source: AA0mqf4LGEir7+sUn3JQztooPiTqpoL0ZtXzAn9x3BZieAdky+AC347ieh3djGRKoZYQgLnIiAdZ6bEKgno= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:200:8539:aadd:13be:6e82]) (user=drosen job=sendgmr) by 2002:a25:ae12:0:b0:6d0:704:f19f with SMTP id a18-20020a25ae12000000b006d00704f19fmr4415884ybj.191.1669083400889; Mon, 21 Nov 2022 18:16:40 -0800 (PST) Date: Mon, 21 Nov 2022 18:15:35 -0800 In-Reply-To: <20221122021536.1629178-1-drosen@google.com> Mime-Version: 1.0 References: <20221122021536.1629178-1-drosen@google.com> X-Mailer: git-send-email 2.38.1.584.g0f3c55d4c2-goog Message-ID: <20221122021536.1629178-21-drosen@google.com> Subject: [RFC PATCH v2 20/21] fuse-bpf: Add symlink/link support From: Daniel Rosenberg To: Miklos Szeredi Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, bpf@vger.kernel.org, kernel-team@android.com, Daniel Rosenberg , Paul Lawrence Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 271 ++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/dir.c | 11 ++ fs/fuse/fuse_i.h | 20 ++++ 3 files changed, 302 insertions(+) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index 05fb88865289..a77414e8f3df 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -2319,6 +2319,104 @@ int fuse_bpf_unlink(int *out, struct inode *dir, struct dentry *entry) dir, entry); } +static int fuse_link_initialize_in(struct fuse_args *fa, struct fuse_link_in *fli, + struct dentry *entry, struct inode *dir, + struct dentry *newent) +{ + struct inode *src_inode = entry->d_inode; + + *fli = (struct fuse_link_in) { + .oldnodeid = get_node_id(src_inode), + }; + + fa->opcode = FUSE_LINK; + fa->in_numargs = 2; + fa->in_args[0].size = sizeof(*fli); + fa->in_args[0].value = fli; + fa->in_args[1].size = newent->d_name.len + 1; + fa->in_args[1].value = (void *) newent->d_name.name; + + return 0; +} + +static int fuse_link_initialize_out(struct fuse_args *fa, struct fuse_link_in *fli, + struct dentry *entry, struct inode *dir, + struct dentry *newent) +{ + return 0; +} + +static int fuse_link_backing(struct fuse_args *fa, int *out, struct dentry *entry, + struct inode *dir, struct dentry *newent) +{ + struct path backing_old_path; + struct path backing_new_path; + struct dentry *backing_dir_dentry; + struct inode *fuse_new_inode = NULL; + struct fuse_inode *fuse_dir_inode = get_fuse_inode(dir); + struct inode *backing_dir_inode = fuse_dir_inode->backing_inode; + + *out = 0; + get_fuse_backing_path(entry, &backing_old_path); + if (!backing_old_path.dentry) + return -EBADF; + + get_fuse_backing_path(newent, &backing_new_path); + if (!backing_new_path.dentry) { + *out = -EBADF; + goto err_dst_path; + } + + backing_dir_dentry = dget_parent(backing_new_path.dentry); + backing_dir_inode = d_inode(backing_dir_dentry); + + inode_lock_nested(backing_dir_inode, I_MUTEX_PARENT); + *out = vfs_link(backing_old_path.dentry, &init_user_ns, + backing_dir_inode, backing_new_path.dentry, NULL); + inode_unlock(backing_dir_inode); + if (*out) + goto out; + + if (d_really_is_negative(backing_new_path.dentry) || + unlikely(d_unhashed(backing_new_path.dentry))) { + *out = -EINVAL; + /** + * TODO: overlayfs responds to this situation with a + * lookupOneLen. Should we do that too? + */ + goto out; + } + + fuse_new_inode = fuse_iget_backing(dir->i_sb, fuse_dir_inode->nodeid, backing_dir_inode); + if (IS_ERR(fuse_new_inode)) { + *out = PTR_ERR(fuse_new_inode); + goto out; + } + d_instantiate(newent, fuse_new_inode); + +out: + dput(backing_dir_dentry); + path_put(&backing_new_path); +err_dst_path: + path_put(&backing_old_path); + return *out; +} + +static int fuse_link_finalize(struct fuse_args *fa, int *out, struct dentry *entry, + struct inode *dir, struct dentry *newent) +{ + return 0; +} + +int fuse_bpf_link(int *out, struct inode *inode, struct dentry *entry, + struct inode *newdir, struct dentry *newent) +{ + return fuse_bpf_backing(inode, struct fuse_link_in, out, + fuse_link_initialize_in, fuse_link_initialize_out, + fuse_link_backing, fuse_link_finalize, entry, + newdir, newent); +} + struct fuse_getattr_io { struct fuse_getattr_in fgi; struct fuse_attr_out fao; @@ -2600,6 +2698,179 @@ int fuse_bpf_statfs(int *out, struct inode *inode, struct dentry *dentry, struct dentry, buf); } +static int fuse_get_link_initialize_in(struct fuse_args *fa, struct fuse_unused_io *unused, + struct inode *inode, struct dentry *dentry, + struct delayed_call *callback) +{ + /* + * TODO + * If we want to handle changing these things, we'll need to copy + * the lower fs's data into our own buffer, and provide our own callback + * to free that buffer. + * + * Pre could change the name we're looking at + * postfilter can change the name we return + * + * We ought to only make that buffer if it's been requested, so leaving + * this unimplemented for the moment + */ + *fa = (struct fuse_args) { + .opcode = FUSE_READLINK, + .nodeid = get_node_id(inode), + .in_numargs = 1, + .in_args[0] = (struct fuse_in_arg) { + .size = dentry->d_name.len + 1, + .value = (void *) dentry->d_name.name, + }, + /* + * .out_argvar = 1, + * .out_numargs = 1, + * .out_args[0].size = , + * .out_args[0].value = , + */ + }; + + return 0; +} + +static int fuse_get_link_initialize_out(struct fuse_args *fa, struct fuse_unused_io *unused, + struct inode *inode, struct dentry *dentry, + struct delayed_call *callback) +{ + /* + * .out_argvar = 1, + * .out_numargs = 1, + * .out_args[0].size = , + * .out_args[0].value = , + */ + + return 0; +} + +static int fuse_get_link_backing(struct fuse_args *fa, const char **out, + struct inode *inode, struct dentry *dentry, + struct delayed_call *callback) +{ + struct path backing_path; + + if (!dentry) { + *out = ERR_PTR(-ECHILD); + return PTR_ERR(*out); + } + + get_fuse_backing_path(dentry, &backing_path); + if (!backing_path.dentry) { + *out = ERR_PTR(-ECHILD); + return PTR_ERR(*out); + } + + /* + * TODO: If we want to do our own thing, copy the data and then call the + * callback + */ + *out = vfs_get_link(backing_path.dentry, callback); + + path_put(&backing_path); + return 0; +} + +static int fuse_get_link_finalize(struct fuse_args *fa, const char **out, + struct inode *inode, struct dentry *dentry, + struct delayed_call *callback) +{ + return 0; +} + +int fuse_bpf_get_link(const char **out, struct inode *inode, struct dentry *dentry, + struct delayed_call *callback) +{ + return fuse_bpf_backing(inode, struct fuse_unused_io, out, + fuse_get_link_initialize_in, fuse_get_link_initialize_out, + fuse_get_link_backing, + fuse_get_link_finalize, + inode, dentry, callback); +} + +static int fuse_symlink_initialize_in(struct fuse_args *fa, struct fuse_unused_io *unused, + struct inode *dir, struct dentry *entry, const char *link, int len) +{ + *fa = (struct fuse_args) { + .nodeid = get_node_id(dir), + .opcode = FUSE_SYMLINK, + .in_numargs = 2, + .in_args[0] = (struct fuse_in_arg) { + .size = entry->d_name.len + 1, + .value = (void *) entry->d_name.name, + }, + .in_args[1] = (struct fuse_in_arg) { + .size = len, + .value = (void *) link, + }, + }; + + return 0; +} + +static int fuse_symlink_initialize_out(struct fuse_args *fa, struct fuse_unused_io *unused, + struct inode *dir, struct dentry *entry, const char *link, int len) +{ + return 0; +} + +static int fuse_symlink_backing(struct fuse_args *fa, int *out, + struct inode *dir, struct dentry *entry, const char *link, int len) +{ + struct fuse_inode *fuse_inode = get_fuse_inode(dir); + struct inode *backing_inode = fuse_inode->backing_inode; + struct path backing_path; + struct inode *inode = NULL; + + *out = 0; + //TODO Actually deal with changing the backing entry in symlink + get_fuse_backing_path(entry, &backing_path); + if (!backing_path.dentry) + return -EBADF; + + inode_lock_nested(backing_inode, I_MUTEX_PARENT); + *out = vfs_symlink(&init_user_ns, backing_inode, backing_path.dentry, + link); + inode_unlock(backing_inode); + if (*out) + goto out; + if (d_really_is_negative(backing_path.dentry) || + unlikely(d_unhashed(backing_path.dentry))) { + *out = -EINVAL; + /** + * TODO: overlayfs responds to this situation with a + * lookupOneLen. Should we do that too? + */ + goto out; + } + inode = fuse_iget_backing(dir->i_sb, fuse_inode->nodeid, backing_inode); + if (IS_ERR(inode)) { + *out = PTR_ERR(inode); + goto out; + } + d_instantiate(entry, inode); +out: + path_put(&backing_path); + return *out; +} + +static int fuse_symlink_finalize(struct fuse_args *fa, int *out, + struct inode *dir, struct dentry *entry, const char *link, int len) +{ + return 0; +} + +int fuse_bpf_symlink(int *out, struct inode *dir, struct dentry *entry, const char *link, int len) +{ + return fuse_bpf_backing(dir, struct fuse_unused_io, out, + fuse_symlink_initialize_in, fuse_symlink_initialize_out, + fuse_symlink_backing, fuse_symlink_finalize, + dir, entry, link, len); +} + struct fuse_read_io { struct fuse_read_in fri; struct fuse_read_out fro; diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 899de6c84c2e..1f9105edc7e2 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -945,6 +945,10 @@ static int fuse_symlink(struct user_namespace *mnt_userns, struct inode *dir, struct fuse_mount *fm = get_fuse_mount(dir); unsigned len = strlen(link) + 1; FUSE_ARGS(args); + int err; + + if (fuse_bpf_symlink(&err, dir, entry, link, len)) + return err; args.opcode = FUSE_SYMLINK; args.in_numargs = 2; @@ -1151,6 +1155,9 @@ static int fuse_link(struct dentry *entry, struct inode *newdir, struct fuse_mount *fm = get_fuse_mount(inode); FUSE_ARGS(args); + if (fuse_bpf_link(&err, inode, entry, newdir, newent)) + return err; + memset(&inarg, 0, sizeof(inarg)); inarg.oldnodeid = get_node_id(inode); args.opcode = FUSE_LINK; @@ -1543,12 +1550,16 @@ static const char *fuse_get_link(struct dentry *dentry, struct inode *inode, { struct fuse_conn *fc = get_fuse_conn(inode); struct page *page; + const char *out = NULL; int err; err = -EIO; if (fuse_is_bad(inode)) goto out_err; + if (fuse_bpf_get_link(&out, inode, dentry, callback)) + return out; + if (fc->cache_symlinks) return page_get_link(dentry, inode, callback); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 37b29a3ea330..99c9231ec98b 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1412,6 +1412,7 @@ int fuse_bpf_rename2(int *out, struct inode *olddir, struct dentry *oldent, int fuse_bpf_rename(int *out, struct inode *olddir, struct dentry *oldent, struct inode *newdir, struct dentry *newent); int fuse_bpf_unlink(int *out, struct inode *dir, struct dentry *entry); +int fuse_bpf_link(int *out, struct inode *inode, struct dentry *entry, struct inode *dir, struct dentry *newent); int fuse_bpf_release(int *out, struct inode *inode, struct file *file); int fuse_bpf_releasedir(int *out, struct inode *inode, struct file *file); int fuse_bpf_flush(int *out, struct inode *inode, struct file *file, fl_owner_t id); @@ -1436,6 +1437,9 @@ int fuse_bpf_getattr(int *out, struct inode *inode, const struct dentry *entry, u32 request_mask, unsigned int flags); int fuse_bpf_setattr(int *out, struct inode *inode, struct dentry *dentry, struct iattr *attr, struct file *file); int fuse_bpf_statfs(int *out, struct inode *inode, struct dentry *dentry, struct kstatfs *buf); +int fuse_bpf_get_link(const char **out, struct inode *inode, struct dentry *dentry, + struct delayed_call *callback); +int fuse_bpf_symlink(int *out, struct inode *dir, struct dentry *entry, const char *link, int len); int fuse_bpf_readdir(int *out, struct inode *inode, struct file *file, struct dir_context *ctx); int fuse_bpf_access(int *out, struct inode *inode, int mask); @@ -1485,6 +1489,11 @@ static inline int fuse_bpf_unlink(int *out, struct inode *dir, struct dentry *en return 0; } +static inline int fuse_bpf_link(int *out, struct inode *inode, struct dentry *entry, struct inode *dir, struct dentry *newent) +{ + return 0; +} + static inline int fuse_bpf_release(int *out, struct inode *inode, struct file *file) { return 0; @@ -1581,6 +1590,17 @@ static inline int fuse_bpf_statfs(int *out, struct inode *inode, struct dentry * return 0; } +static inline int fuse_bpf_get_link(const char **out, struct inode *inode, struct dentry *dentry, + struct delayed_call *callback) +{ + return 0; +} + +static inline int fuse_bpf_symlink(int *out, struct inode *dir, struct dentry *entry, const char *link, int len) +{ + return 0; +} + static inline int fuse_bpf_readdir(int *out, struct inode *inode, struct file *file, struct dir_context *ctx) { return 0; From patchwork Tue Nov 22 02:15:36 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13051842 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 E08E6C4332F for ; Tue, 22 Nov 2022 02:20:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232575AbiKVCUq (ORCPT ); Mon, 21 Nov 2022 21:20:46 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41968 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232617AbiKVCTn (ORCPT ); Mon, 21 Nov 2022 21:19:43 -0500 Received: from mail-yw1-x114a.google.com (mail-yw1-x114a.google.com [IPv6:2607:f8b0:4864:20::114a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 36904E9153 for ; Mon, 21 Nov 2022 18:16:44 -0800 (PST) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-3985077640fso80993057b3.4 for ; Mon, 21 Nov 2022 18:16:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=tJXvhRhAgfuVfe2WyHPR4tz1JKdoKAJyNeGFfWGwJfA=; b=qGkvx8TFpzkUW6hJpxPR4PKq3xLOu9Os7QX2lTMWCilalOFz9Xa1h5bFbtSpucKkWm TltFT8llXWJ7GhU2/JCvGPxUKKyf+M5vl3oAklLsvD1oxP8MTw156iiPERz5BlwYWBaw /Aotf3Al2u6liOXujyYy4ynyF1St9jqgbgFkLtP2NvI1Bj6udJvaG0Xmp52Kx7gzMg1Z adtT/ZnVZK6ZkF1A3IJqIRE5Nn89VCvBJgCK2auE41NBM56Tw2QLxlXT3TEm6s4rCvbA y+aBIj+GuWxJUkSGWY++ynL0CiCHeRY6X5vmgLh4cigCoA8tF6uoSbhGFr6AmKQfLhY7 hKQQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=tJXvhRhAgfuVfe2WyHPR4tz1JKdoKAJyNeGFfWGwJfA=; b=0w7ADyy+yE8LrdEt/UkOhqQbM4C6YcVQvzobyKX0H6w4t7Y+haH3yKoJMV77PJ8ZIt xGclVtTz1CPTU2EwLc+4+v9ADqOHh8SXDOBPoLUE5X/acJaQz286UyHGXbYNflh0sEiR Scu/MImI+O7GGEFQjTftBncIw/spYe9EoEbk2yLxQZJPMJwgwhf9pmlV2D5MQMSDVEW/ ljbw50gWq0jCztB+V64ItuL4Z6fQI8m+HGbDTp5et8BerDzVxSh0MmnngivQOGKh9jvd +Pj1dq1UL82+WoQvq1anJOyJvdDWjsuijy9w9qYNZZms4EdpZob5/v9cVh3RkpT1Khs/ i9kQ== X-Gm-Message-State: ANoB5pmqitcf/ak43F33yoslK5dydr3TfJ/02DEHCbnyVZi6GckWo4As leT8kAAvZoLGchHwLMvVPA4BbK/I5xQ= X-Google-Smtp-Source: AA0mqf4ig5LKUxIEPBtpsrsS6x8pF+5RIGhWCLARqw1OqEfhOKW0oTQ5XDVHBGd5PrVtKUOP4DD+H6Fg8PM= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:200:8539:aadd:13be:6e82]) (user=drosen job=sendgmr) by 2002:a25:bd03:0:b0:6e6:9336:f565 with SMTP id f3-20020a25bd03000000b006e69336f565mr0ybk.598.1669083402808; Mon, 21 Nov 2022 18:16:42 -0800 (PST) Date: Mon, 21 Nov 2022 18:15:36 -0800 In-Reply-To: <20221122021536.1629178-1-drosen@google.com> Mime-Version: 1.0 References: <20221122021536.1629178-1-drosen@google.com> X-Mailer: git-send-email 2.38.1.584.g0f3c55d4c2-goog Message-ID: <20221122021536.1629178-22-drosen@google.com> Subject: [RFC PATCH v2 21/21] fuse-bpf: allow mounting with no userspace daemon From: Daniel Rosenberg To: Miklos Szeredi Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, bpf@vger.kernel.org, kernel-team@android.com, Daniel Rosenberg , Paul Lawrence Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC When using fuse-bpf in pure passthrough mode, we don't explicitly need a userspace daemon. This allows simple testing of the backing operations. Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/fuse_i.h | 4 ++++ fs/fuse/inode.c | 25 +++++++++++++++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 99c9231ec98b..402d80d35958 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -564,6 +564,7 @@ struct fuse_fs_context { bool no_control:1; bool no_force_umount:1; bool legacy_opts_show:1; + bool no_daemon:1; enum fuse_dax_mode dax_mode; unsigned int max_read; unsigned int blksize; @@ -842,6 +843,9 @@ struct fuse_conn { /* Is tmpfile not implemented by fs? */ unsigned int no_tmpfile:1; + /** BPF Only, no Daemon running */ + unsigned int no_daemon:1; + /** The number of requests waiting for completion */ atomic_t num_waiting; diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 1e7d45977144..4820edcc242a 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -749,6 +749,7 @@ enum { OPT_MAX_READ, OPT_BLKSIZE, OPT_ROOT_DIR, + OPT_NO_DAEMON, OPT_ERR }; @@ -764,6 +765,7 @@ static const struct fs_parameter_spec fuse_fs_parameters[] = { fsparam_u32 ("blksize", OPT_BLKSIZE), fsparam_string ("subtype", OPT_SUBTYPE), fsparam_u32 ("root_dir", OPT_ROOT_DIR), + fsparam_flag ("no_daemon", OPT_NO_DAEMON), {} }; @@ -853,6 +855,11 @@ static int fuse_parse_param(struct fs_context *fsc, struct fs_parameter *param) return invalfc(fsc, "Unable to open root directory"); break; + case OPT_NO_DAEMON: + ctx->no_daemon = true; + ctx->fd_present = true; + break; + default: return -EINVAL; } @@ -1411,7 +1418,7 @@ void fuse_send_init(struct fuse_mount *fm) ia->args.nocreds = true; ia->args.end = process_init_reply; - if (fuse_simple_background(fm, &ia->args, GFP_KERNEL) != 0) + if (unlikely(fm->fc->no_daemon) || fuse_simple_background(fm, &ia->args, GFP_KERNEL) != 0) process_init_reply(fm, &ia->args, -ENOTCONN); } EXPORT_SYMBOL_GPL(fuse_send_init); @@ -1693,6 +1700,7 @@ int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx) fc->destroy = ctx->destroy; fc->no_control = ctx->no_control; fc->no_force_umount = ctx->no_force_umount; + fc->no_daemon = ctx->no_daemon; err = -ENOMEM; root = fuse_get_root_inode(sb, ctx->rootmode, ctx->root_dir); @@ -1739,7 +1747,7 @@ static int fuse_fill_super(struct super_block *sb, struct fs_context *fsc) struct fuse_fs_context *ctx = fsc->fs_private; int err; - if (!ctx->file || !ctx->rootmode_present || + if (!!ctx->file == ctx->no_daemon || !ctx->rootmode_present || !ctx->user_id_present || !ctx->group_id_present) return -EINVAL; @@ -1747,10 +1755,12 @@ static int fuse_fill_super(struct super_block *sb, struct fs_context *fsc) * Require mount to happen from the same user namespace which * opened /dev/fuse to prevent potential attacks. */ - if ((ctx->file->f_op != &fuse_dev_operations) || - (ctx->file->f_cred->user_ns != sb->s_user_ns)) - return -EINVAL; - ctx->fudptr = &ctx->file->private_data; + if (ctx->file) { + if ((ctx->file->f_op != &fuse_dev_operations) || + (ctx->file->f_cred->user_ns != sb->s_user_ns)) + return -EINVAL; + ctx->fudptr = &ctx->file->private_data; + } err = fuse_fill_super_common(sb, ctx); if (err) @@ -1800,6 +1810,9 @@ static int fuse_get_tree(struct fs_context *fsc) fsc->s_fs_info = fm; + if (ctx->no_daemon) + return get_tree_nodev(fsc, fuse_fill_super); + if (ctx->fd_present) ctx->file = fget(ctx->fd);