From patchwork Tue Apr 18 01:40:01 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214942 X-Patchwork-Delegate: bpf@iogearbox.net 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 C9405C77B75 for ; Tue, 18 Apr 2023 01:40:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229958AbjDRBk4 (ORCPT ); Mon, 17 Apr 2023 21:40:56 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53010 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229602AbjDRBkz (ORCPT ); Mon, 17 Apr 2023 21:40:55 -0400 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 550A35273 for ; Mon, 17 Apr 2023 18:40:54 -0700 (PDT) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-54feaa94819so79473007b3.2 for ; Mon, 17 Apr 2023 18:40:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782053; x=1684374053; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=NfkPJzUlysoaTODKR2vMaar8Vfxo3RC8DMLPrNdWTm0=; b=egQ1giZWzQQU6IgqDiN0fqNaR+tCjtq8YtqkqBFfrhgCOHzoH5BL4EpM1SjL2V9YPT zw5vjo04SRS98y4aBh4IKDh/0Gc/NqpsqnvGnxUVTxf4LsjVKui23mBcXeJ9KXrvhZKA ZjR7hMSIiiecVTikf6jSWY5aIuy3zBgyY+o31s5RG669mrcwpbn4LneWBnlC4odlvOM6 uVEg8sDa7KRWgPiubS+g6d2Di3xIboxV1D2Yox6S+/NnnYnYXgpHb/utSPyJALD1t4u2 t3AZaYcWNjd484y99DBX0CWCj1HZ0+dTbwbIXfNH7ltNnMYthcViod3d8MoUwvFYMiDJ Wo3Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782053; x=1684374053; 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=NfkPJzUlysoaTODKR2vMaar8Vfxo3RC8DMLPrNdWTm0=; b=ER4CIaYBbZiCX0CsqwjI4++fiHXww2GJg8Ca5AsIRZdBwMay5yZmOlC1CUVGnruvNF mexZL/Pxlgviod+jae/VsVNlcYhZ1avGUKItOeuDFDYubb+eW+WOe3HW6RpnHdAku9v+ xW99KprE7u09fX3A2ayGwcQJdp7HLNq6MeYkSiL/kZ/EwD53/tUjnx7gjz0sF+LQ81e+ MeUAw7WJPhTQHSDfcsLPA+R9Sr0aCp9hw8XSVTKZL7Iy5/Hgv1ExZRMA/idRL9wJRQ1l ojsiNTT0rdJJdSB1cz3spAUn6JsOOR/z4W6HlbSRp7aQ8Oxa0ktcqbx2ebsc2BjXQoax tEIw== X-Gm-Message-State: AAQBX9cd38NsChiLw3NM57E+evdG8HB2ocMj91A4oyio0u6OnBza6saq 89khIv0awPsuopmGSBx9RdKtWB4e72o= X-Google-Smtp-Source: AKy350YmlxhwmblENoNjk9kVV5cLsVBPE97MdbSgohua40Z5njEAKzoWuQFLyOxBvwVm7Hfg/W6OWPDkKZ4= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a81:af53:0:b0:549:14b0:84af with SMTP id x19-20020a81af53000000b0054914b084afmr10738506ywj.1.1681782053642; Mon, 17 Apr 2023 18:40:53 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:01 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-2-drosen@google.com> Subject: [RFC PATCH v3 01/37] bpf: verifier: Accept dynptr mem as mem in herlpers From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , kernel-team@android.com, Daniel Rosenberg Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net X-Patchwork-State: RFC This allows using memory retrieved from dynptrs with helper functions that accept ARG_PTR_TO_MEM. For instance, results from bpf_dynptr_data can be passed along to bpf_strncmp. Signed-off-by: Daniel Rosenberg --- kernel/bpf/verifier.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 1e05355facdc..ebc638bfed87 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -7128,12 +7128,16 @@ static int check_reg_type(struct bpf_verifier_env *env, u32 regno, * ARG_PTR_TO_MEM + MAYBE_NULL is compatible with PTR_TO_MEM and PTR_TO_MEM + MAYBE_NULL, * but ARG_PTR_TO_MEM is compatible only with PTR_TO_MEM but NOT with PTR_TO_MEM + MAYBE_NULL * + * ARG_PTR_TO_MEM is compatible with PTR_TO_MEM that is tagged with a dynptr type. + * * Therefore we fold these flags depending on the arg_type before comparison. */ if (arg_type & MEM_RDONLY) type &= ~MEM_RDONLY; if (arg_type & PTR_MAYBE_NULL) type &= ~PTR_MAYBE_NULL; + if (base_type(arg_type) == ARG_PTR_TO_MEM) + type &= ~DYNPTR_TYPE_FLAG_MASK; if (meta->func_id == BPF_FUNC_kptr_xchg && type & MEM_ALLOC) type &= ~MEM_ALLOC; From patchwork Tue Apr 18 01:40:02 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214943 X-Patchwork-Delegate: bpf@iogearbox.net 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 BCBB6C77B7C for ; Tue, 18 Apr 2023 01:41:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230230AbjDRBlQ (ORCPT ); Mon, 17 Apr 2023 21:41:16 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53708 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229838AbjDRBlO (ORCPT ); Mon, 17 Apr 2023 21:41:14 -0400 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 DD97659EE for ; Mon, 17 Apr 2023 18:40:56 -0700 (PDT) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-54fbc270950so116942627b3.8 for ; Mon, 17 Apr 2023 18:40:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782055; x=1684374055; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=WjDDJW+GHYKcFKqxIZ+BL5gG6uAgwAshe83Msya4lZk=; b=NlOg4YH4aS6Jqn57OyoDroboiAPLs6DTDbrvGBZbNhHR10+m/WdBxEvhRm0xAGWYMp f9bQ4TPfKNqD7xy2km5YYg0yfmm/J4l/kQ9agtg79DmAEtF6sxG6YZL0P09rDUitmuJT kXa0oepXRXmncPOGL54W0tDR+viFUV7oZolHNgNGYjHS3czEp7wnPo/SRvxpM/LZwElo qtthAQBa5tFhv3tSpPuuHV9MiyJGbjl+TBimhWKh7FOD20dvSUG3x1gbWqH++RfHPFb1 qQwmzd7SdI+qv/79iFoti9uokwoWrsiLI5aQMJ9aVjw//OVLtTGARlAgnsUtTxomxwzx BL5A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782055; x=1684374055; 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=WjDDJW+GHYKcFKqxIZ+BL5gG6uAgwAshe83Msya4lZk=; b=cMc6mq9ENrhoFxq6QE6kY5Pnw7pRhaPi6+9bIj1Z+9Bm8W61ns3gXX2cK6mSYIavLj ymYYe20CAHqYxo99TL76eGfRSvRuCAmJGSZYXNO8i3jHrg7cmXe1/Y90dQoA2haZiGyR 0VGA9kubt9HAaktgR91inqTUvG9A4YFVzs1nt7FPD63tTZdAR9egbwPNq+tRhEaLoelE ymhMPD5QorFUyCTGEQYKupcAwkwUJfrcouYi+sllSwQ8DKEKNB+Prz3Zv+g6rqbDjDF2 8eAg4/sjUOFggCc0hkrDIyxnEpa8KVtA5eVyPVO6x8IO9JTq67TzQvkQ5xVaRPV4xaUj FMUg== X-Gm-Message-State: AAQBX9fVa5IVSXe4gf1zeyAiHC35NgjwXwFjP6uY9sz5zK4869uDgF3u 9kO+Kxkls59MDICMR+HT2avgRL2dQfA= X-Google-Smtp-Source: AKy350b6WbLfFOIpMTkeOSl2mlRWjzNHXOblepg4SNDqaWtzBw/uJNst+ZDP03Eg7cvOh/SLsMoc8N9aowY= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a05:690c:706:b0:545:5f92:f7ee with SMTP id bs6-20020a05690c070600b005455f92f7eemr11333022ywb.2.1681782055788; Mon, 17 Apr 2023 18:40:55 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:02 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-3-drosen@google.com> Subject: [RFC PATCH v3 02/37] bpf: Allow NULL buffers in bpf_dynptr_slice(_rw) From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , kernel-team@android.com, Daniel Rosenberg Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net X-Patchwork-State: RFC bpf_dynptr_slice(_rw) uses a user provided buffer if it can not provide a pointer to a block of contiguous memory. This buffer is unused in the case of local dynptrs, and may be unused in other cases as well. There is no need to require the buffer, as the kfunc can just return NULL if it was needed and not provided. This adds another kfunc annotation, __opt, which combines with __sz and __szk to allow the buffer associated with the size to be NULL. If the buffer is NULL, the verifier does not check that the buffer is of sufficient size. Signed-off-by: Daniel Rosenberg --- Documentation/bpf/kfuncs.rst | 23 ++++++++++++++++++++++- kernel/bpf/helpers.c | 32 ++++++++++++++++++++------------ kernel/bpf/verifier.c | 19 +++++++++++++++++++ 3 files changed, 61 insertions(+), 13 deletions(-) diff --git a/Documentation/bpf/kfuncs.rst b/Documentation/bpf/kfuncs.rst index ea2516374d92..7a3d9de5f315 100644 --- a/Documentation/bpf/kfuncs.rst +++ b/Documentation/bpf/kfuncs.rst @@ -100,7 +100,7 @@ Hence, whenever a constant scalar argument is accepted by a kfunc which is not a size parameter, and the value of the constant matters for program safety, __k suffix should be used. -2.2.2 __uninit Annotation +2.2.3 __uninit Annotation ------------------------- This annotation is used to indicate that the argument will be treated as @@ -117,6 +117,27 @@ Here, the dynptr will be treated as an uninitialized dynptr. Without this annotation, the verifier will reject the program if the dynptr passed in is not initialized. +2.2.4 __opt Annotation +------------------------- + +This annotation is used to indicate that the buffer associated with an __sz or __szk +argument may be null. If the function is passed a nullptr in place of the buffer, +the verifier will not check that length is appropriate for the buffer. The kfunc is +responsible for checking if this buffer is null before using it. + +An example is given below:: + + __bpf_kfunc void *bpf_dynptr_slice(..., void *buffer__opt, u32 buffer__szk) + { + ... + } + +Here, the buffer may be null. If buffer is not null, it at least of size buffer_szk. +Either way, the returned buffer is either NULL, or of size buffer_szk. Without this +annotation, the verifier will reject the program if a null pointer is passed in with +a nonzero size. + + .. _BPF_kfunc_nodef: 2.3 Using an existing kernel function diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 00e5fb0682ac..bfb75ecacb76 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -2167,13 +2167,15 @@ __bpf_kfunc struct task_struct *bpf_task_from_pid(s32 pid) * bpf_dynptr_slice() - Obtain a read-only pointer to the dynptr data. * @ptr: The dynptr whose data slice to retrieve * @offset: Offset into the dynptr - * @buffer: User-provided buffer to copy contents into - * @buffer__szk: Size (in bytes) of the buffer. This is the length of the - * requested slice. This must be a constant. + * @buffer__opt: User-provided buffer to copy contents into. May be NULL + * @buffer__szk: Size (in bytes) of the buffer if present. This is the + * length of the requested slice. This must be a constant. * * For non-skb and non-xdp type dynptrs, there is no difference between * bpf_dynptr_slice and bpf_dynptr_data. * + * If buffer__opt is NULL, the call will fail if buffer_opt was needed. + * * If the intention is to write to the data slice, please use * bpf_dynptr_slice_rdwr. * @@ -2190,7 +2192,7 @@ __bpf_kfunc struct task_struct *bpf_task_from_pid(s32 pid) * direct pointer) */ __bpf_kfunc void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset, - void *buffer, u32 buffer__szk) + void *buffer__opt, u32 buffer__szk) { enum bpf_dynptr_type type; u32 len = buffer__szk; @@ -2210,15 +2212,19 @@ __bpf_kfunc void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset case BPF_DYNPTR_TYPE_RINGBUF: return ptr->data + ptr->offset + offset; case BPF_DYNPTR_TYPE_SKB: - return skb_header_pointer(ptr->data, ptr->offset + offset, len, buffer); + if (!buffer__opt) + return NULL; + return skb_header_pointer(ptr->data, ptr->offset + offset, len, buffer__opt); case BPF_DYNPTR_TYPE_XDP: { void *xdp_ptr = bpf_xdp_pointer(ptr->data, ptr->offset + offset, len); if (xdp_ptr) return xdp_ptr; - bpf_xdp_copy_buf(ptr->data, ptr->offset + offset, buffer, len, false); - return buffer; + if (!buffer__opt) + return NULL; + bpf_xdp_copy_buf(ptr->data, ptr->offset + offset, buffer__opt, len, false); + return buffer__opt; } default: WARN_ONCE(true, "unknown dynptr type %d\n", type); @@ -2230,13 +2236,15 @@ __bpf_kfunc void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset * bpf_dynptr_slice_rdwr() - Obtain a writable pointer to the dynptr data. * @ptr: The dynptr whose data slice to retrieve * @offset: Offset into the dynptr - * @buffer: User-provided buffer to copy contents into - * @buffer__szk: Size (in bytes) of the buffer. This is the length of the - * requested slice. This must be a constant. + * @buffer__opt: User-provided buffer to copy contents into. May be NULL + * @buffer__szk: Size (in bytes) of the buffer if present. This is the + * length of the requested slice. This must be a constant. * * For non-skb and non-xdp type dynptrs, there is no difference between * bpf_dynptr_slice and bpf_dynptr_data. * + * If buffer__opt is NULL, the call will fail if buffer_opt was needed. + * * The returned pointer is writable and may point to either directly the dynptr * data at the requested offset or to the buffer if unable to obtain a direct * data pointer to (example: the requested slice is to the paged area of an skb @@ -2267,7 +2275,7 @@ __bpf_kfunc void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset * direct pointer) */ __bpf_kfunc void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr_kern *ptr, u32 offset, - void *buffer, u32 buffer__szk) + void *buffer__opt, u32 buffer__szk) { if (!ptr->data || bpf_dynptr_is_rdonly(ptr)) return NULL; @@ -2294,7 +2302,7 @@ __bpf_kfunc void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr_kern *ptr, u32 o * will be copied out into the buffer and the user will need to call * bpf_dynptr_write() to commit changes. */ - return bpf_dynptr_slice(ptr, offset, buffer, buffer__szk); + return bpf_dynptr_slice(ptr, offset, buffer__opt, buffer__szk); } __bpf_kfunc void *bpf_cast_to_kern_ctx(void *obj) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index ebc638bfed87..fd959824469d 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -9387,6 +9387,19 @@ static bool is_kfunc_arg_const_mem_size(const struct btf *btf, return __kfunc_param_match_suffix(btf, arg, "__szk"); } +static bool is_kfunc_arg_optional(const struct btf *btf, + const struct btf_param *arg, + const struct bpf_reg_state *reg) +{ + const struct btf_type *t; + + t = btf_type_skip_modifiers(btf, arg->type, NULL); + if (!btf_type_is_ptr(t) || reg->type != SCALAR_VALUE || reg->umax_value > 0) + return false; + + return __kfunc_param_match_suffix(btf, arg, "__opt"); +} + static bool is_kfunc_arg_constant(const struct btf *btf, const struct btf_param *arg) { return __kfunc_param_match_suffix(btf, arg, "__k"); @@ -10453,10 +10466,16 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ break; case KF_ARG_PTR_TO_MEM_SIZE: { + struct bpf_reg_state *buff_reg = ®s[regno]; + const struct btf_param *buff_arg = &args[i]; struct bpf_reg_state *size_reg = ®s[regno + 1]; const struct btf_param *size_arg = &args[i + 1]; ret = check_kfunc_mem_size_reg(env, size_reg, regno + 1); + if (ret < 0 && is_kfunc_arg_optional(meta->btf, buff_arg, buff_reg)) { + verbose(env, "error was %d", ret); + ret = 0; + } if (ret < 0) { verbose(env, "arg#%d arg#%d memory, len pair leads to invalid memory access\n", i, i + 1); return ret; From patchwork Tue Apr 18 01:40:03 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214944 X-Patchwork-Delegate: bpf@iogearbox.net 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 02599C77B72 for ; Tue, 18 Apr 2023 01:41:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230265AbjDRBlR (ORCPT ); Mon, 17 Apr 2023 21:41:17 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53718 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230094AbjDRBlO (ORCPT ); Mon, 17 Apr 2023 21:41:14 -0400 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 87F125FD5 for ; Mon, 17 Apr 2023 18:40:58 -0700 (PDT) Received: by mail-yb1-xb49.google.com with SMTP id c64-20020a254e43000000b00b92530ded91so3262176ybb.17 for ; Mon, 17 Apr 2023 18:40:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782058; x=1684374058; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=XBLJQNnM/sLgs4vCqBW41kO32Rg1yqnQ90D80Go8URk=; b=SnlbZJLk5Z7OPvXPJ7Etlra3omvGygVLH4yu2u9bDq4nCNczqjAQAtQj22wZ5lGEym a2kgjgQG7BqI5HdR0cpPUsEdGAS9YmKkxAht+4KyhF01JzBn+Q5S7f6M6omvM0kkv6X/ evFfdOnXQGFj9+0aeZ9WMkNy0KSNFXRwAthNf6y5bZ8rWO/w27CqQ5dQ154gW44ArDBX LVcYLjt6nZT7hh2nhtXt7Y7pRN6WJB/9+wNxQ55m08D8GmFVYpj4xvYPc2V9ZA197fka hn+T6Z4cpwohIn0+D00N0XUIWGvmcKaMk9jEbjb1w7J1OC1OpxepDIj+esRwnqQA0EyG F+LA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782058; x=1684374058; 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=XBLJQNnM/sLgs4vCqBW41kO32Rg1yqnQ90D80Go8URk=; b=Fdzd3QUOdWBc+OAgStR2yckgY5LttRaMOROWaBoL/KDInngEy0i5Q74NCJZkiwHuMO nIQ2HytPrbkcYrbAHdPyW8RkTEVQR73nV2r6UNX1mTOQOBzjq7UddgjQRh9+wnvPZhdf mG6w/IliKd8m1D2Jp2fHOT1p/iU5mwCBKnxwCYneCt+TKTrdr8erNSQnBZggOLsfYZTP vBfDUTzJgu6+LN4ZXFsvzHn7u/Xo4qAEmSGnOS7mrV0HfSr9LCniqvjSQ3EJspWw6S5u K6Ds4QDYAsYySYZOt0Ltrz6jzUxqc2phzasCXnj/Kr+vwhrFBhtJwCqsfm9TXGFYEzXb 9hgA== X-Gm-Message-State: AAQBX9ds3+VlE5PB7Kll8HFsglfQmr8gCXHD7RT6G4NiQUsgvCqTSyKh YQY7zDAix/t0ToyYCe4eimLYcrdGF+M= X-Google-Smtp-Source: AKy350aqzaT5PLfkv6k+7JDnNf8qfXfskwThdpbUGB15DarKosOyvH1tWqog8UU17iAVxBXL9PBghU8UzVE= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a25:d7d3:0:b0:b6d:80ab:8bb6 with SMTP id o202-20020a25d7d3000000b00b6d80ab8bb6mr11010336ybg.1.1681782057858; Mon, 17 Apr 2023 18:40:57 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:03 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-4-drosen@google.com> Subject: [RFC PATCH v3 03/37] selftests/bpf: Test allowing NULL buffer in dynptr slice From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , kernel-team@android.com, Daniel Rosenberg Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net X-Patchwork-State: RFC bpf_dynptr_slice(_rw) no longer requires a buffer for verification. If the buffer is needed, but not present, the function will return NULL. Signed-off-by: Daniel Rosenberg --- .../testing/selftests/bpf/prog_tests/dynptr.c | 1 + .../selftests/bpf/progs/dynptr_success.c | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/dynptr.c b/tools/testing/selftests/bpf/prog_tests/dynptr.c index d176c34a7d2e..db22cad32657 100644 --- a/tools/testing/selftests/bpf/prog_tests/dynptr.c +++ b/tools/testing/selftests/bpf/prog_tests/dynptr.c @@ -20,6 +20,7 @@ static struct { {"test_ringbuf", SETUP_SYSCALL_SLEEP}, {"test_skb_readonly", SETUP_SKB_PROG}, {"test_dynptr_skb_data", SETUP_SKB_PROG}, + {"test_dynptr_skb_nobuff", SETUP_SKB_PROG}, }; static void verify_success(const char *prog_name, enum test_setup_type setup_type) diff --git a/tools/testing/selftests/bpf/progs/dynptr_success.c b/tools/testing/selftests/bpf/progs/dynptr_success.c index b2fa6c47ecc0..a059ed8d4590 100644 --- a/tools/testing/selftests/bpf/progs/dynptr_success.c +++ b/tools/testing/selftests/bpf/progs/dynptr_success.c @@ -207,3 +207,24 @@ int test_dynptr_skb_data(struct __sk_buff *skb) return 1; } + +SEC("?cgroup_skb/egress") +int test_dynptr_skb_no_buff(struct __sk_buff *skb) +{ + struct bpf_dynptr ptr; + __u64 *data; + + if (bpf_dynptr_from_skb(skb, 0, &ptr)) { + err = 1; + return 1; + } + + /* This should return NULL. SKB may require a buffer */ + data = bpf_dynptr_slice(&ptr, 0, NULL, 1); + if (data) { + err = 2; + return 1; + } + + return 1; +} From patchwork Tue Apr 18 01:40:04 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214945 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 59A84C77B76 for ; Tue, 18 Apr 2023 01:41:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230291AbjDRBlS (ORCPT ); Mon, 17 Apr 2023 21:41:18 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53740 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230135AbjDRBlO (ORCPT ); Mon, 17 Apr 2023 21:41:14 -0400 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 43A5C59DC for ; Mon, 17 Apr 2023 18:41:00 -0700 (PDT) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-552e3fa8f2fso32389117b3.19 for ; Mon, 17 Apr 2023 18:41:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782060; x=1684374060; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=tI4xcmLYwpmFva3i/dRZyfQFdXSO1OOUgkc/BOul5Ro=; b=2V9e6mUN9Le2shS/G4YvhPJcIGT2cNDZPr1QkNJOacyGU//myujH64KDbejS/tMlXz 4E6nR0HLcdcTv09CWWYmtKIX5wVZpN0ReS4cFCAJVsgaJjGnWqRP+K97KcNMss7R5lc/ 8wSlpIQsxhUI3w04o4cPXpyg3kA0F6KHlojC/d7SlYfU/cCvQzPLbwOg1psSwLmSzRXn Qn3vxYWDKyf/Fn+ASbJ7PU1gdKO1me/kfleFJ2griqgfwNcqIJ9AF057vwZ+pRcgxCsX sF1c9SZLi/nimZi9xbCd1jVXTRuM8JHWnHdMqXKLly0BeGcWX5EH1LSs7jCfFcSZkogo szkw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782060; x=1684374060; 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=tI4xcmLYwpmFva3i/dRZyfQFdXSO1OOUgkc/BOul5Ro=; b=TsDkhRsmNIXWx9T2WMQvbwiH5TMVB9t2ttWL3MkEf8KEpwV9QXEcVKc7J0IRpNjBrW 1OX7F6DK0/aPTUjtrAKy1G4s0TeaR2ljrb9EgPlXYNybOIveQk4xAe0qM/fSAogXOrZB ULPU4vezMx03elAphzkQ8AVYhTNFVN6b/Aocf8kvp/o9udfkz75xMa7QVxz2lw/QaA8i vk70U2hYUVyKNsw6aJxrYF0rrUv4DzNr5QbtwEEJeP0uhPiBR+CZOVENU+L3dcuczTrz 3khaPve0ejKIHby7RsZehp1lOT9gycTESoMfHC7GtEgKA+/QyTPRUX0NXOJJZJDiSEhM Xrkg== X-Gm-Message-State: AAQBX9eA7QO869hM+gRkbKNQXJ3yXkUwJJhLwylAyA3okvNw6E8OtETq vSgyL5wjanWNkxptc8yC2ShnMHBMZG8= X-Google-Smtp-Source: AKy350YKpzErYw3bq9/BlI+znfoyrC46XNLf6K9cGOqwUJLaLH9t7tNWGgZpyi5xKkrMqdSo1O9knK3ZgUU= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a25:d70a:0:b0:b77:ae4b:1b27 with SMTP id o10-20020a25d70a000000b00b77ae4b1b27mr8727839ybg.8.1681782059999; Mon, 17 Apr 2023 18:40:59 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:04 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-5-drosen@google.com> Subject: [RFC PATCH v3 04/37] fs: Generic function to convert iocb to rw flags From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , 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 7c04f033aadd..759893e4da04 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; @@ -241,22 +243,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)) { @@ -316,7 +302,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; @@ -380,7 +367,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 c85916e9f7db..c849074f44b7 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -3009,6 +3009,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 Apr 18 01:40:05 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214946 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 56AFCC77B7C for ; Tue, 18 Apr 2023 01:41:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229517AbjDRBlW (ORCPT ); Mon, 17 Apr 2023 21:41:22 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53786 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230211AbjDRBlQ (ORCPT ); Mon, 17 Apr 2023 21:41:16 -0400 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 A65C26A62 for ; Mon, 17 Apr 2023 18:41:03 -0700 (PDT) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-54f3e30726cso211598217b3.22 for ; Mon, 17 Apr 2023 18:41:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782062; x=1684374062; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=lsQkf1cfcx1tPqZWAOmJuqvsGQVDqkZnL5eNvi6K+GA=; b=UQ9uxhJbkgCQxRon02p9V9Zp2euXuXeVILgWOJj+rbvZ3QD7E30qpSzuqQRtpHzIcs 8tnRPiCMZyAQmeuciCikVJLw4VTasapz/7TmXSqFJVxajgMpiDPL3jaC69UU0eF3Pmld suwPAwdtbxwHDIwMmZV1+ebFmsf+0c1mQqM0wqAbBAcWmTR/pu/B+pWRBe6CMDjEcF28 52pROSussOyzq+qdDWeLqKG/NteVXVxS9qOWASMLH+0EyOqr9kwiYoF84wPX73T5L1ct 9vIqWaFSNn9DOrA0aD+R+WbFQQl5+R6HM2rzlIC/vsjZJqWw523DmBw3bRNkdvwjG4sw pLVQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782062; x=1684374062; 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=lsQkf1cfcx1tPqZWAOmJuqvsGQVDqkZnL5eNvi6K+GA=; b=YsrkzMxvDnb2yRJ/tukPRwpWVHf53TRP2LYi+CNklTY/X4pYapjZG9KkBD6jko/ZbW d+huBhOHuMx329C6b0Ixkuj/6Q+ceaB1C2FOFHh3x0vZnPsW1FshEZWNGjM3e6mw2Vay j6xuC9t+cQRxWSqzMI1+jfq4F27GVmrdyQp0gAuVk78OUZEIhhHE4IZmSPoIr389PVVO LOc1Z+OOxusOOxxjk/0YX6Vq9PPECs/ylw+UOT8RB65HXfyISRnRycFUHKl95cYKho/J zHY5qMh5INZ5xs3Te/6T2P6Nq/939k4dAOE9HeiSNVqEG+TS1sT90j5tyy7WGGRCDUwi HrYg== X-Gm-Message-State: AAQBX9efOaJDw32cqnCjNtFhUeJYBs2y5oNRyaGoaY9hE1RrtC4TDNVu 2IEdl+JBCSa8xdKTfTTzimvYGROYpI8= X-Google-Smtp-Source: AKy350ZwWg/HHOg0CI32Fv3NaozHYx5kISDmv+5KnVkQwLfS05n+gGaQY14q9hCSUWer05c4Oi37kCTsPHw= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a81:c406:0:b0:54d:913b:c9e8 with SMTP id j6-20020a81c406000000b0054d913bc9e8mr10100712ywi.5.1681782062021; Mon, 17 Apr 2023 18:41:02 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:05 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-6-drosen@google.com> Subject: [RFC PATCH v3 05/37] fuse-bpf: Update fuse side uapi From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , 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 | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index 1b9d0dfae72d..04d96f34e9a1 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -607,6 +607,29 @@ 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, +}; + +#define BPF_FUSE_NAME_MAX 15 + +struct fuse_bpf_entry_out { + uint32_t entry_type; + uint32_t unused; + union { + struct { + uint64_t unused2; + uint64_t fd; + }; + char name[BPF_FUSE_NAME_MAX + 1]; + }; +}; + struct fuse_forget_in { uint64_t nlookup; }; From patchwork Tue Apr 18 01:40:06 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214947 X-Patchwork-Delegate: bpf@iogearbox.net 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 6C561C77B75 for ; Tue, 18 Apr 2023 01:41:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230357AbjDRBlc (ORCPT ); Mon, 17 Apr 2023 21:41:32 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53744 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230244AbjDRBlR (ORCPT ); Mon, 17 Apr 2023 21:41:17 -0400 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 79BFF6A76 for ; Mon, 17 Apr 2023 18:41:04 -0700 (PDT) Received: by mail-yb1-xb49.google.com with SMTP id c64-20020a254e43000000b00b92530ded91so3262310ybb.17 for ; Mon, 17 Apr 2023 18:41:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782064; x=1684374064; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=rGQ9sB/Rwqr1E4RbtpywhCeCd351RoNViaB6XX6OAcM=; b=7a7FthehtIU1LDZHaPCjM+OU4rQTnwIJO65M0avbzwBgquVMpBou6qbE9n76s23acW 0ZLBEZD6ViB5CGK7UFFeuO58ubsaYIEMTp4GXx/KH8KrYV2koGO24wTVipTnAT5jPPv2 vDoFrFqHAHBzZbYHVKyk17HtLm24Tprum8PLjWK078LB0eS6ATINZq6HTShyS6UJyTbI Pz0+UzgNTfLj4UnUR7Pl9WJGy4IzEKBoqH51/+5DCpzyuozlPN46l34XGNt300XMi2vv 0zHLgyVCJ6YKv4vRZGoF3hq5Tl/PhsQ2JO+iP5xq437JP1RbmlU4dS+oNmu6RXuBXKdD Wvyw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782064; x=1684374064; 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=rGQ9sB/Rwqr1E4RbtpywhCeCd351RoNViaB6XX6OAcM=; b=GRHnBtEUZZqpVET+RpXZZ90lgHXbh+PtIzLjMxISS5rmr7Pfxee+uKNsI2OLV/npt/ MYHMDYfrHyr19lDhbR0BSWdnA/UCjV3E2pVh/7FvhtEF8ymlns4hzrW4YLsaI1GP3P3N xfkTLEhd0l8UyoBsVVIRZ1KkpzYzoz/jD+i2jdRh8Ah9JJEpHqRGaJ+S//+S50QlvACb Vm+0s2Fq4If/JXTBlBHAIjlBN5vC6DNDBDEy/eKUm3FdNIlvLJ3mh06W3SfgCQU3zCjA 2SwM8NXE+1MzllL3lJcG3Dni4N9kOxSniWDXoQJ5Bd+jbl7pYp2vOxhTlfmmU7UtC46B JYXA== X-Gm-Message-State: AAQBX9dsuTHqr7ZO3zHdV0WdTPctLkS7NZW3gdbKpEvv6BnYY91uErm+ Xv/T0IfASf300y2xEWs5xn8Ym+7Qd0k= X-Google-Smtp-Source: AKy350bEg5Yq5yN301eh/701p5EkdiweIgQB8V883Xlr2DXOiBdY6xkRbjC+SVvzsfu/0C5Gu4BhRNTqFN8= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a25:d10e:0:b0:b8f:3881:1638 with SMTP id i14-20020a25d10e000000b00b8f38811638mr8759594ybg.7.1681782064219; Mon, 17 Apr 2023 18:41:04 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:06 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-7-drosen@google.com> Subject: [RFC PATCH v3 06/37] fuse-bpf: Add data structures for fuse-bpf From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , kernel-team@android.com, Daniel Rosenberg Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net X-Patchwork-State: RFC These structures will be used to interact between the fuse bpf calls and normal userspace calls Signed-off-by: Daniel Rosenberg --- include/linux/bpf_fuse.h | 84 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 include/linux/bpf_fuse.h diff --git a/include/linux/bpf_fuse.h b/include/linux/bpf_fuse.h new file mode 100644 index 000000000000..ce8b1b347496 --- /dev/null +++ b/include/linux/bpf_fuse.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2022 Google LLC. + */ + +#ifndef _BPF_FUSE_H +#define _BPF_FUSE_H + +#include +#include + +struct fuse_buffer { + void *data; + unsigned size; + unsigned alloc_size; + unsigned max_size; + int flags; +}; + +/* These flags are used internally to track information about the fuse buffers. + * Fuse sets some of the flags in init. The helper functions sets others, depending on what + * was requested by the bpf program. + */ +// Flags set by FUSE +#define BPF_FUSE_IMMUTABLE (1 << 0) // Buffer may not be written to +#define BPF_FUSE_VARIABLE_SIZE (1 << 1) // Buffer length may be changed (growth requires alloc) +#define BPF_FUSE_MUST_ALLOCATE (1 << 2) // Buffer must be re allocated before allowing writes + +// Flags set by helper function +#define BPF_FUSE_MODIFIED (1 << 3) // The helper function allowed writes to the buffer +#define BPF_FUSE_ALLOCATED (1 << 4) // The helper function allocated the buffer + +/* + * BPF Fuse Args + * + * Used to translate between bpf program parameters and their userspace equivalent calls. + * Variable sized arguments are held in fuse_buffers. To access these, bpf programs must + * use kfuncs to access them as dynptrs. + * + */ + +#define FUSE_MAX_ARGS_IN 3 +#define FUSE_MAX_ARGS_OUT 2 + +struct bpf_fuse_arg { + union { + void *value; + struct fuse_buffer *buffer; + }; + unsigned size; + bool is_buffer; +}; + +struct bpf_fuse_meta_info { + uint64_t nodeid; + uint32_t opcode; + uint32_t error_in; +}; + +struct bpf_fuse_args { + struct bpf_fuse_meta_info info; + uint32_t in_numargs; + uint32_t out_numargs; + uint32_t flags; + struct bpf_fuse_arg in_args[FUSE_MAX_ARGS_IN]; + struct bpf_fuse_arg out_args[FUSE_MAX_ARGS_OUT]; +}; + +// Mirrors for struct fuse_args flags +#define FUSE_BPF_FORCE (1 << 0) +#define FUSE_BPF_OUT_ARGVAR (1 << 6) +#define FUSE_BPF_IS_LOOKUP (1 << 11) + +static inline void *bpf_fuse_arg_value(const struct bpf_fuse_arg *arg) +{ + return arg->is_buffer ? arg->buffer : arg->value; +} + +static inline unsigned bpf_fuse_arg_size(const struct bpf_fuse_arg *arg) +{ + return arg->is_buffer ? arg->buffer->size : arg->size; +} + +#endif /* _BPF_FUSE_H */ From patchwork Tue Apr 18 01:40:07 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214948 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 5500EC77B72 for ; Tue, 18 Apr 2023 01:41:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230463AbjDRBl4 (ORCPT ); Mon, 17 Apr 2023 21:41:56 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53870 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230149AbjDRBlT (ORCPT ); Mon, 17 Apr 2023 21:41:19 -0400 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 8F272619C for ; Mon, 17 Apr 2023 18:41:07 -0700 (PDT) Received: by mail-yb1-xb49.google.com with SMTP id d132-20020a254f8a000000b00b868826cdfeso2897574ybb.0 for ; Mon, 17 Apr 2023 18:41:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782066; x=1684374066; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=Ma6LP7l9KdDYxD0yEhLpdRJpUgrAcoFlwvcgm95S2g0=; b=d51SS6WG6e8IhBQxgiwtwAX6W6u5b/p5f3qAWDcVPo2jEbJQRrKfqB7fD2WB7ABXfm f1QDkxS0AxwE1oa6FQ4jYY7ixPpnGwXCPx+AsK15KrITTIQeNrqN2cahcwamg4mHST/1 dLSOe/MQEeIXaOymPStG/HcmyjMuppTVyWgNDT62/a0q/oG3tDfVWH7rMGNawaD0o+Tp Ni0kuHRqOhAOcNAtD4v/LtaKrjnY1QvynB5VRgotRKQA6MFysty3xo5ey1gq6oFLlnLv /nnda2RIAREqoKgyxD+dqXX80U30yBXf5e+3lSppGCBf17IWxLGBotULgIChHzE5mTEv issw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782066; x=1684374066; 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=Ma6LP7l9KdDYxD0yEhLpdRJpUgrAcoFlwvcgm95S2g0=; b=GoYYSEk7dTdjNNUSSy9Hx+HRWrcqGtbpWa2myf+B+/KwvTRPBjvzVp0mGfB6ocZ3Ub 9MpwPVYSMSXhrF5EM009tvlveBPdDD+/IQC3urFZwXxE95264PsIRiQuIwHz0wIvHi3y I5s8ivcONVBgHC5GV95m2kJWBDbQsb9AabUUWj2pURNL9DVzoFRvytm9bEHZrHuuev9K s7Itwb8c+Big+t1KV8rIwQU7FqILBVXXdaRDheCLxHMyPPxsD9EtryUit8CeYSVza63r jFS+yM5TIWEmgTXDr8YH8jRxLO4Uew9ykGp4HrcPReF81hrcqjp7YPy5iGMCphtbJmPI Etaw== X-Gm-Message-State: AAQBX9eVj1vUNB+6sCtEIww2nF0theXFuRaYFf8d6sZ3nRgEnxsDjytN tBMB44lntFWwPEz5sQOqn26OLAKwThM= X-Google-Smtp-Source: AKy350al/X5V7uOGTMzTFULq0yhZ2D34oueue8qfbReq7qB7OLV2hrBAIi9Rsio853MG4jn2Pq7Glue+WGc= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a25:aaad:0:b0:b8f:6a46:537d with SMTP id t42-20020a25aaad000000b00b8f6a46537dmr8996331ybi.6.1681782066667; Mon, 17 Apr 2023 18:41:06 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:07 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-8-drosen@google.com> Subject: [RFC PATCH v3 07/37] fuse-bpf: Prepare for fuse-bpf patch From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , 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 35bc174f9ba2..55dd6e8b2e43 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 9b7fc7d3c7f1..01ca8bb87b4f 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 */ @@ -1324,4 +1332,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 d66070af145d..a824ca100047 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) { @@ -1394,28 +1416,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 Apr 18 01:40:08 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214949 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 3E9C3C77B7C for ; Tue, 18 Apr 2023 01:42:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230128AbjDRBmB (ORCPT ); Mon, 17 Apr 2023 21:42:01 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54268 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230364AbjDRBlm (ORCPT ); Mon, 17 Apr 2023 21:41:42 -0400 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 ABAAA6EB7 for ; Mon, 17 Apr 2023 18:41:10 -0700 (PDT) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-54ee1fd7876so256045977b3.23 for ; Mon, 17 Apr 2023 18:41:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782069; x=1684374069; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=43QxFVc50DAyWgu57dIHIILS8j92JgJ+spQAbuJ/QvU=; b=Ltmi58KxaJRM+xuYPqy2BRHGm5n20ZyE8MpSN8weeGUxH/6LjHoVp2zzmJoBXYUfVN nuhTslW/LLX6Ujijwmez5/vmwwEhdVBm0s06X6nvnlpk9zrYLXM5Jnc7bWhzXSPPJIxE +Zl1xzhrFSUBn2Xhvypy54vX8yGiq2KmWBJyVpTywwAWOaOehypOEjmz9oszH+GzW8DF mJ8v6T/dexXI+6FblRvM/5OCKtMR8pTbO/upGDZVyVxo+97DDjn/8vWsVXeYAHBRhp/A OCBVMetoYH0EK/RbHiNUkJQKGANQO1GGhtRFSlXXYOKcU07jMdn1BmqALJ0gBteEBBFQ gNNw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782069; x=1684374069; 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=43QxFVc50DAyWgu57dIHIILS8j92JgJ+spQAbuJ/QvU=; b=DqnWqfRGCVYEyQtJY5aMo4bMhRrieIkbf5mH1eUGCUJ4S/DRVRoYMgc0cQjnAQ8GVN z02ktCZhnrhrPszaHSZM0arLrYP2pUu/BOwtNE/rjaCUgkfYG60aSfb2VGfG7o+YEp+z G4Wrc0t7CW41BR7u6AoFLp3q7PgvzzF5wX/cPQZY6aDgfLoDsaCxCmNDpq1KNlTofR+p Q5suVRAY5JKlDXPk1l2vLvSQ3lq8vfzUCeqnTaTvcSYlcKNeQapfCWnSZdn0tvwq9TBf JWBMYYVRmEzxcj1/kk3+5gd1cVLHbs+2jpl3AR256a99SiT51wpN77j/etRG46m4sbKu I4GQ== X-Gm-Message-State: AAQBX9czn1gDSfuTLb8ysPdkRORk92ZraNhs75Q6YmTdRIs/QkQu/h1M WILvbDPc3itwC2WuD6PJIVvIRlOKYTc= X-Google-Smtp-Source: AKy350Y/8JtCHjP1QW/pvn4hJ1FgSbBVMwhqN5jn2gWMFPYp2imD4c1za6dXD+I2jmUpdindf2WdmxacnOA= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a05:690c:d91:b0:54f:e88d:79ba with SMTP id da17-20020a05690c0d9100b0054fe88d79bamr9713625ywb.5.1681782069111; Mon, 17 Apr 2023 18:41:09 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:08 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-9-drosen@google.com> Subject: [RFC PATCH v3 08/37] fuse: Add fuse-bpf, a stacked fs extension for FUSE From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , 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 | 419 ++++++++++++++++++++++++++++++++++++++++++++++ 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, 888 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..3d895957b5ce --- /dev/null +++ b/fs/fuse/backing.c @@ -0,0 +1,419 @@ +// 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 +#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 bpf_fuse_args *fa, io *in_out, args...): function that sets + * up fa and io based on args + * void initialize_out(struct bpf_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 bpf_fuse_backing(inode, io, out, \ + initialize_in, initialize_out, \ + backing, finalize, args...) \ +({ \ + struct fuse_inode *fuse_inode = get_fuse_inode(inode); \ + struct bpf_fuse_args fa = { 0 }; \ + bool initialized = false; \ + bool handled = false; \ + ssize_t res; \ + io feo = { 0 }; \ + int error = 0; \ + \ + do { \ + if (!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.info.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_args { + struct fuse_buffer name; + struct fuse_entry_out out; + struct fuse_bpf_entry entries_storage; + struct fuse_buffer bpf_entries; +}; + +static int fuse_lookup_initialize_in(struct bpf_fuse_args *fa, struct fuse_lookup_args *args, + struct inode *dir, struct dentry *entry, unsigned int flags) +{ + *args = (struct fuse_lookup_args) { + .name = (struct fuse_buffer) { + .data = (void *) entry->d_name.name, + .size = entry->d_name.len + 1, + .max_size = NAME_MAX + 1, + .flags = BPF_FUSE_VARIABLE_SIZE | BPF_FUSE_MUST_ALLOCATE, + }, + }; + *fa = (struct bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .nodeid = get_fuse_inode(dir)->nodeid, + .opcode = FUSE_LOOKUP, + }, + .in_numargs = 1, + .in_args[0] = (struct bpf_fuse_arg) { + .is_buffer = true, + .buffer = &args->name, + }, + }; + + return 0; +} + +static int fuse_lookup_initialize_out(struct bpf_fuse_args *fa, struct fuse_lookup_args *args, + struct inode *dir, struct dentry *entry, unsigned int flags) +{ + args->bpf_entries = (struct fuse_buffer) { + .data = args->entries_storage.out, + .size = 0, + .alloc_size = sizeof(args->entries_storage.out), + .max_size = sizeof(args->entries_storage.out), + .flags = BPF_FUSE_VARIABLE_SIZE, + }, + + fa->out_numargs = 2; + fa->flags = FUSE_BPF_OUT_ARGVAR | FUSE_BPF_IS_LOOKUP; + fa->out_args[0] = (struct bpf_fuse_arg) { + .size = sizeof(args->out), + .value = &args->out, + }; + fa->out_args[1] = (struct bpf_fuse_arg) { + .is_buffer = true, + .buffer = &args->bpf_entries, + }; + + return 0; +} + +static int fuse_lookup_backing(struct bpf_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); + if (fa->in_args[0].buffer->flags & BPF_FUSE_MODIFIED) { + name = (char *)fa->in_args[0].buffer->data; + len = strnlen(name, fa->in_args[0].buffer->size); + } else { + 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)) { + fa->info.error_in = -ENOENT; + 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 bpf_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].buffer->data; + 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 bpf_fuse_backing(dir, struct fuse_lookup_args, 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 eb4f88e3dc97..a3029824c24f 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; @@ -1009,10 +1014,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; @@ -1021,6 +1054,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); } @@ -1298,7 +1333,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); @@ -1837,7 +1872,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 55dd6e8b2e43..73ebe3498fb9 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 | LOOKUP_RENAME_TARGET))) { 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; @@ -1370,6 +1487,7 @@ static int fuse_permission(struct mnt_idmap *idmap, 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; @@ -1382,7 +1500,6 @@ static int fuse_permission(struct mnt_idmap *idmap, */ 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) || @@ -1559,7 +1676,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 de37a3a06a71..25fb49f0a9f7 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -8,6 +8,7 @@ #include "fuse_i.h" +#include #include #include #include @@ -127,13 +128,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); @@ -171,7 +177,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; @@ -1948,6 +1954,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 01ca8bb87b4f..c24878f4a89f 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; // May need adjustments??? uint8_t in_numargs; uint8_t out_numargs; uint8_t ext_idx; @@ -271,6 +315,7 @@ struct fuse_args { bool page_replace:1; bool may_block:1; bool is_ext: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); @@ -524,6 +569,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; @@ -970,12 +1016,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 @@ -1120,6 +1170,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); /** @@ -1328,10 +1379,46 @@ int fuse_fileattr_set(struct mnt_idmap *idmap, /* 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 a824ca100047..b71e8758fab5 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, }; } @@ -352,8 +359,7 @@ 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(); /* @@ -364,22 +370,100 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr, inode->i_acl = inode->i_default_acl = ACL_DONT_CACHE; } +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, fc); + 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) @@ -387,6 +471,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 @@ -408,7 +495,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; @@ -440,13 +527,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; @@ -676,6 +766,7 @@ enum { OPT_ALLOW_OTHER, OPT_MAX_READ, OPT_BLKSIZE, + OPT_ROOT_DIR, OPT_ERR }; @@ -690,6 +781,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), {} }; @@ -773,6 +865,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; } @@ -785,6 +883,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); } @@ -912,15 +1012,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 { @@ -935,11 +1049,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); @@ -948,7 +1065,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) { @@ -1042,13 +1159,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); @@ -1452,7 +1570,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 @@ -1581,11 +1699,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 8e01bfdfc430..3542d992bde6 100644 --- a/fs/fuse/ioctl.c +++ b/fs/fuse/ioctl.c @@ -428,7 +428,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 Apr 18 01:40:09 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214950 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 0BA8EC77B76 for ; Tue, 18 Apr 2023 01:42:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230120AbjDRBmR (ORCPT ); Mon, 17 Apr 2023 21:42:17 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54308 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230280AbjDRBlo (ORCPT ); Mon, 17 Apr 2023 21:41:44 -0400 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 BA36D6A66 for ; Mon, 17 Apr 2023 18:41:12 -0700 (PDT) Received: by mail-yb1-xb4a.google.com with SMTP id v200-20020a252fd1000000b00b8f548a72bbso12435866ybv.9 for ; Mon, 17 Apr 2023 18:41:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782071; x=1684374071; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=w19M7mxJAre5VfcOLhOZhg9JC6HtCZspBwvR3oTaBtM=; b=O/uzOqnDmS58QEJVz0T7PNXTX0Mtqd93rP+7E7na6jJ7QBcC3+3JWqA624W+JCI64R g5UeaSIEQ2e9zoM9bLjzbRQ4TX4IcKtelfNTRYpmePqs0g/0s18ad9gEBxKqugTmTnXJ LzySXi8ehZLsjwTOWb1Bm2cDRdGh9W+hMegnr4ssDEOtKk1HcW9S90nfvbwuqyU9afp8 r/LRBntSZet4HdYZYg9L+SpnqePNzq+J51zllKN3K7oc10u2cosiIPhI7KlqZt97mhR3 m9YfjttiB71iUr6bziRAvbTY79chCtR9d3tTTN8GEdsXPPkdN4l+BdlYow6MfQfY3GLw IdrQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782071; x=1684374071; 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=w19M7mxJAre5VfcOLhOZhg9JC6HtCZspBwvR3oTaBtM=; b=FnNOzMA6l1B5lw3ZDM+i75YHngqNXLbAWgoGJQ1TGYYnmGIG5+G2AG0Q8dr0riTbLf Lur1KAxIA4TdKexVMEPMuviS//1mApbi2F7gy5av4lByeIZYbry/vVMPP/QWPNJSe4fQ 6pkfiDn7/4v46aFr22J89otnalkawuhE5lzkLKqroDhmGGCnXBVkP0XiryCxEV2T+QoY xN2R62+b3Hy9imdS7nfeYb67jGg0oFl703GwsELhO/XO8KzpfyA96tAz6JvjU7VFYlLo I/K1kSf0F1EL8TELeqTi4PtRbZj/i1iizOBfqkzJl/HhzdQp4+iH9avMU0WmOz6u4KQY Nj6g== X-Gm-Message-State: AAQBX9c6G3zHUooaMU8VnjVWYps/T3uTIXSAkJwQYKd3xGunMvyVp/R5 BclMwwGo5KJ+OMnZMRnzPI+b+dtW2Ko= X-Google-Smtp-Source: AKy350YsucDg8BHeY6V0IoVOR0pulMc5pZV3Sps9tjCFUe04Pre+til+90iCxm0xGbwDMnHRM3ERPECUwNE= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a25:cbd5:0:b0:b95:630:197b with SMTP id b204-20020a25cbd5000000b00b950630197bmr691791ybg.10.1681782071432; Mon, 17 Apr 2023 18:41:11 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:09 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-10-drosen@google.com> Subject: [RFC PATCH v3 09/37] fuse-bpf: Add ioctl interface for /dev/fuse From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , 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 a3029824c24f..ad7d9d1e6da5 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1016,18 +1016,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; @@ -1036,7 +1037,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); } @@ -1045,7 +1046,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; @@ -1055,7 +1056,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); } @@ -1333,7 +1334,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); @@ -1872,7 +1873,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); } /* @@ -1882,7 +1884,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; @@ -1954,6 +1956,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 @@ -1992,7 +1995,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, @@ -2073,7 +2076,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: @@ -2286,6 +2289,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) { @@ -2318,6 +2348,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 c24878f4a89f..39a9fdf2a752 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -316,6 +316,7 @@ struct fuse_args { bool may_block:1; bool is_ext: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 04d96f34e9a1..3ad725a3e968 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -1012,6 +1012,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 Apr 18 01:40:10 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214951 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 CFB9DC77B7F for ; Tue, 18 Apr 2023 01:42:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230425AbjDRBmT (ORCPT ); Mon, 17 Apr 2023 21:42:19 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54328 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230302AbjDRBlp (ORCPT ); Mon, 17 Apr 2023 21:41:45 -0400 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 57CAA6A7C for ; Mon, 17 Apr 2023 18:41:14 -0700 (PDT) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-54faf2e22afso139554247b3.7 for ; Mon, 17 Apr 2023 18:41:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782073; x=1684374073; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=k+WSpIn3NUEIdVcNWDxlTZBL9IhEMs5qHwjDTSF5QyA=; b=gnEAvtWYbBPrlRDvmMhwWHAU4VOPAZNhzEbReBa4tJPpc5X3cQyQnOkcj+IZxU+jtd Q+YCDhkPu6ZImWiqrO7KC2hZnhfm/LoPf2Qk+B0v/m+gmZs6ydm6J9MF3xNYweVlnDD8 kzJrv+qZegriRdHZmr6G27dsKx70i7W4bP34J1/ZwAQen87rca+5p+yPyWMoLaX9Dau9 nVVsiKpQ1gJzqgxY8b3BMTPSECOFqN5iCxmNf5WcJMJVHe+OKuTEuYOpIO0xE/kXViUv CKddSMZuirEzlq3i+5fDaLh4bYaIBJcIpoMpmtH8zmyBRsHn6nqpB1ZlzCdIk6aTFlgp Gt8Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782073; x=1684374073; 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=k+WSpIn3NUEIdVcNWDxlTZBL9IhEMs5qHwjDTSF5QyA=; b=YxsGYSKk00osXa19OHDZhlDM6VoN56h8h2JPoXWdGjSrTWS3UtkV9lbPwqmJkHMKKL 4Fn7WGIkgaSPfSzA/I8pYC9kvqaHjq/ZW+Hj6IWiydJIz6EG0Ow4tcjtRsfZ9ciHC/Ww ZiRXyI7OHFo6WRkWgvZ8jbHbAGaMJoHpYPouS/yBcm/opVSDuc403IxvJ20xrFYzXjYk mc2GjqTJNx7w9grP+R8dLPPoxfkmcU3ySolosMW8OHp/PALp/5uK77HgAOT6G38c4Inx nqxM5veuv7wS+63Ws80gFITJaeX/fkG1N/bmL09itTjlDDS8GkX8ZKHw5fxWGfz8L6l9 rluA== X-Gm-Message-State: AAQBX9cXsfnwzjYpeGgpwZceuBI73iS3Zx3lBRCO891g+tzP7Z8MrWZQ eNIbxANWa7r3ZYJdp3/Ak0RUf/QPomg= X-Google-Smtp-Source: AKy350ZEiZ4KiwaDQvoDBVRd0pyWtBmDHMYqWI/Cx2Am4cudHmQsbbtrl8pLWWl9gfM9hNxwPXm0gu6TXWo= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a25:cfcc:0:b0:b78:1b26:a642 with SMTP id f195-20020a25cfcc000000b00b781b26a642mr8808364ybg.1.1681782073411; Mon, 17 Apr 2023 18:41:13 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:10 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-11-drosen@google.com> Subject: [RFC PATCH v3 10/37] fuse-bpf: Don't support export_operations From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , 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 b71e8758fab5..fe80984f099a 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -1107,6 +1107,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 Apr 18 01:40:11 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214952 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 07794C77B75 for ; Tue, 18 Apr 2023 01:42:32 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229901AbjDRBma (ORCPT ); Mon, 17 Apr 2023 21:42:30 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53884 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230000AbjDRBl5 (ORCPT ); Mon, 17 Apr 2023 21:41:57 -0400 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 D1C995FD6 for ; Mon, 17 Apr 2023 18:41:16 -0700 (PDT) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-552d922f0faso34404417b3.14 for ; Mon, 17 Apr 2023 18:41:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782075; x=1684374075; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=1Zzn8o1i1Qipa77dzB0ZPLXp5K2/As2sG8h87DoyjGM=; b=2+I98OIJuVmlxxZnL112cFQtwT3jCQDjIow9U7tinQ0J7PnT7CW2nk5rnzuaqyfrh7 t3DlfVAZR6sD5E+Luhvth8wbofyEZeHmBpkUkBocSpBEVttlXwuggShqUO9kj7Yq0/h/ aX7QFH/O8g9rnwLpOW9lAXc/V85QKHAhuJlpvE7i1s3Gysd2bhwkmvNvqQ40s9IHS+fW iwFwXKp/SUQPTFNVB9+Xbi1KWg/VuO0heZfOSN2cvyCPhvHY7TR+CKbNpElBlFthzn2Y NCkq3jwcH9x/45VO8uba3X5M5Kc8fLUu98eMXLHD5qQZ70F2CYgsgu/Pb70DPzEmoCIC +4Yw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782075; x=1684374075; 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=1Zzn8o1i1Qipa77dzB0ZPLXp5K2/As2sG8h87DoyjGM=; b=Pg6eKtu/oRRAV4J7nyd2OWORzNtR/Qr2ioZ7tWi3OGrqw8srrvYmg+U4sn6nS6ScES Lmpi+7375/6XI1lzJZO4Mh7buOiOg8wxE3bO5ap6J22JeE+5i0/LI/fr4iKALNbM/LPV /XkqKBZkRqhpNm/GhnmY/mzBIUQLwc1mMfkmlBD9oP0ukqxUnSm8OGcKsYZKSriRVtqZ 3cCFQvtm3QCnqy/lsToMIDDjvXarrerY2sw/QHAROT9FZSnW8dPD1vcRVuJoUjod8cNZ +rat3yDaO09XJ/+Mj+SKruWtNWpG5KATXoTqDab9J1toOYVjytjZTQCBuCpknzMyM9Rd Ub0Q== X-Gm-Message-State: AAQBX9c/E1rmh5LH/YYlDKt+k48Yan2yM0z1qR0dczQgHXUiDR9TVbNL TD9/fKekCINWmssD1FJqSBr9WkGVnPU= X-Google-Smtp-Source: AKy350a3U2lTLEFmBlzns8CqFoaKv7hHvFxu3WPdR2gkRwnkhckHyeUhIddcfAh3AVrbzcBi92gpNo88opU= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a25:74d0:0:b0:b92:25a0:90d with SMTP id p199-20020a2574d0000000b00b9225a0090dmr5872731ybc.0.1681782075783; Mon, 17 Apr 2023 18:41:15 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:11 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-12-drosen@google.com> Subject: [RFC PATCH v3 11/37] fuse-bpf: Add support for access From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , 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 backing support for FUSE_ACCESS Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/dir.c | 6 ++++++ fs/fuse/fuse_i.h | 6 ++++++ 3 files changed, 59 insertions(+) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index 3d895957b5ce..e42622584037 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -417,3 +417,50 @@ 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 bpf_fuse_args *fa, struct fuse_access_in *in, + struct inode *inode, int mask) +{ + *in = (struct fuse_access_in) { + .mask = mask, + }; + + *fa = (struct bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .opcode = FUSE_ACCESS, + .nodeid = get_node_id(inode), + }, + .in_numargs = 1, + .in_args[0].size = sizeof(*in), + .in_args[0].value = in, + }; + + return 0; +} + +static int fuse_access_initialize_out(struct bpf_fuse_args *fa, struct fuse_access_in *in, + struct inode *inode, int mask) +{ + return 0; +} + +static int fuse_access_backing(struct bpf_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(&nop_mnt_idmap, fi->backing_inode, fai->mask); + return 0; +} + +static int fuse_access_finalize(struct bpf_fuse_args *fa, int *out, struct inode *inode, int mask) +{ + return 0; +} + +int fuse_bpf_access(int *out, struct inode *inode, int mask) +{ + return bpf_fuse_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 73ebe3498fb9..535e6cf9e970 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1439,6 +1439,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) @@ -1495,6 +1498,9 @@ static int fuse_permission(struct mnt_idmap *idmap, 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 39a9fdf2a752..cb166168f9c2 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1406,6 +1406,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 @@ -1414,6 +1415,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 Apr 18 01:40:12 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214953 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 E32BCC77B7C for ; Tue, 18 Apr 2023 01:42:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231134AbjDRBmn (ORCPT ); Mon, 17 Apr 2023 21:42:43 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53720 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230352AbjDRBl7 (ORCPT ); Mon, 17 Apr 2023 21:41:59 -0400 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 B1CBC6A63 for ; Mon, 17 Apr 2023 18:41:19 -0700 (PDT) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-54f89e7de94so162583857b3.17 for ; Mon, 17 Apr 2023 18:41:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782078; x=1684374078; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=oN6x+PcjBowjEB90+rsteKN+eOkqOSAMj6ggqH7i4oE=; b=e2l+365Y5NfwgrmJ3glTREcW5dUMa28BQ3rkaEXIxzCaUVeE1IhaW2auQ6TAoZSlTV 2DPfi5gIQFGcRKYkma2DV8pvrKv+CMBRJ5PqPI/sdCdJlujipWSi8TX5fpC7CIATr/wd IZ08/Y5ullb70ZOKJIK0Fa3l1w37fzqtYaUA2LBOQ+U08RbTKlpgXJi+kOkZuc5emqyT txsRKHpNndZBshfMylH2G+PKKT4M75pZSQdvHa69EiYZG+SqYwtca9kBbOLbesWQNZOV ScLqr6LEl57hIDUX6HFB91pq60LCptprJlfycy0AaC9T8kF5M0PJh2968Q/Fdg6j+/Y2 nZBA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782078; x=1684374078; 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=oN6x+PcjBowjEB90+rsteKN+eOkqOSAMj6ggqH7i4oE=; b=jO8VFz6dgcFvv+WNVysDZKbob3eNJl5FX/1rDZL3j8IbuZaCJoC4bqkpiJKN/wGfdp Acad80Q47MDGg+jHYg8RNW62IipR+9BaiDCrTjHM7sd2BaKyzadNrw4uOnj2Tw9Knjez IZHGbWixr5Rn8a6LMsvZWrUs9eyr+sWb1cnROdS8wQqdjpbvjZrwNk6H4OTVQPx5rop9 ZWGyIzVmhUyWnCzH7RQ8Xh8H2D/AOVspvKB4a1m/temQEtRNumzbJI/DEUEQQP0hxqJe Yp+sK0/iVDYFnG7kjO+Y64fwjmi4b3j1Q198nS+8v8IVyW8ZNZGD9XtxOErLsJ95Snow IKEA== X-Gm-Message-State: AAQBX9eA8moOwGwhIi1o/fSFKpdVrq+RAysrG6B+hQ5gGWQpLQVY9U8d P6rXSeEn8ztknOtdx68AqOF8SqHiigM= X-Google-Smtp-Source: AKy350ZlkcIB5L39o8Rx8chg+be3fQChvnEEvP0sguYtyhqx+uHhdIzeUdU455ATM/2zMsWmD3vY1g4K4MY= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a25:74d4:0:b0:b8e:cb88:1b8a with SMTP id p203-20020a2574d4000000b00b8ecb881b8amr10919222ybc.8.1681782078105; Mon, 17 Apr 2023 18:41:18 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:12 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-13-drosen@google.com> Subject: [RFC PATCH v3 12/37] fuse-bpf: Partially add mapping support From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , 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 is not currently hooked into the infrastructure that will call the bpf programs. 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 e42622584037..930aa370e376 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -207,6 +207,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 25fb49f0a9f7..865167a80d35 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -2527,6 +2527,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 cb166168f9c2..5eb357f482dc 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1422,7 +1422,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 Apr 18 01:40:13 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214954 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 9B267C77B75 for ; Tue, 18 Apr 2023 01:42:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230476AbjDRBmx (ORCPT ); Mon, 17 Apr 2023 21:42:53 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54302 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229792AbjDRBmQ (ORCPT ); Mon, 17 Apr 2023 21:42:16 -0400 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 3986A6A62 for ; Mon, 17 Apr 2023 18:41:23 -0700 (PDT) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-54f89e7de94so162584427b3.17 for ; Mon, 17 Apr 2023 18:41:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782080; x=1684374080; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=NYL4IdJjh78Mx0ERjHXq1HM7WDhJyXfUI9ZLvdFWk98=; b=gDPc8KFBWBSDxYuCP48vLLO3Vue1Z2AubLCuuloL4zyW7pB2dhVnlUg0914m1YDuUd 6Ne1KGXCk4u/o9jyWFV2XXPimdtwupUUcvgkfbZVD+J5Tucff+wx4eEkpGMKftKAyHVg ggIWdp57XhPiL081zazQF4KJrhUtgo1n62jsm/nCPnSpTOEEa0QdfXLnA7/I0/JPpbtE VeS6W6NpNQNbK8tDqcm+zMNAS2xTiCwbBK1HqQrGP1vXMzw3/f3jYAJNVlOzDCvIkqDR jKpbPZPCr3xy77kbdxnHzURMl+sPneGV3POnYfCtJgR+D28OHQNxJkj6v0N/ovT13U9O wz4w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782080; x=1684374080; 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=NYL4IdJjh78Mx0ERjHXq1HM7WDhJyXfUI9ZLvdFWk98=; b=DXJ6lgXwUcA4IvCwGzJnYey8QugCBCzRzB4hmS2oYItJuADDqgJOjM5mhJoJcouE63 VFQ9Q2bDDol5DW65IHbsCSnVgBo5ocloCnpyILCN2kj4tu4TYuogGR7V2fu1ovSh7bxA qvDGepHKOyTydkDe662Byoq5LZS6RjwuHYoxxnqizMhSX790zPzHdt1bYOb6yifAnCUH vqdbvnzVg+A0tgHXvzqEboOh96FEwp5KKlLEriEafAJz4oG1z5U5eNrGDzTOy4An2u+i 5UQyTThbopfEgj0gFO80jgLMH/YuqShsjdhc/HL51nR9QuPyhUC62zt+hx418nwvMbqv zpPA== X-Gm-Message-State: AAQBX9e2jkcnhT2nQBXJFS+glSHkrZO0ueYW6MFoM1rZ1q0gNYHyaGET A9XrTm4vnT6+C78QYeOEahGPAfIl1gY= X-Google-Smtp-Source: AKy350YBlKkuvX66p/6m/H2JvUtFIa2769A+526n9Y5/glS4dC9W1cB3cMRNvaXNmAOriHpO5bnUIntTZ3U= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a0d:ec02:0:b0:54c:2723:560d with SMTP id q2-20020a0dec02000000b0054c2723560dmr10854311ywn.3.1681782080382; Mon, 17 Apr 2023 18:41:20 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:13 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-14-drosen@google.com> Subject: [RFC PATCH v3 13/37] fuse-bpf: Add lseek support From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , 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 backing support for FUSE_LSEEK Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/file.c | 3 ++ fs/fuse/fuse_i.h | 6 ++++ 3 files changed, 98 insertions(+) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index 930aa370e376..c4916dde48c8 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -207,6 +207,95 @@ static void fuse_stat_to_attr(struct fuse_conn *fc, struct inode *inode, attr->blksize = 1 << blkbits; } +struct fuse_lseek_args { + struct fuse_lseek_in in; + struct fuse_lseek_out out; +}; + +static int fuse_lseek_initialize_in(struct bpf_fuse_args *fa, struct fuse_lseek_args *args, + struct file *file, loff_t offset, int whence) +{ + struct fuse_file *fuse_file = file->private_data; + + args->in = (struct fuse_lseek_in) { + .fh = fuse_file->fh, + .offset = offset, + .whence = whence, + }; + + *fa = (struct bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .nodeid = get_node_id(file->f_inode), + .opcode = FUSE_LSEEK, + }, + .in_numargs = 1, + .in_args[0].size = sizeof(args->in), + .in_args[0].value = &args->in, + }; + + return 0; +} + +static int fuse_lseek_initialize_out(struct bpf_fuse_args *fa, struct fuse_lseek_args *args, + struct file *file, loff_t offset, int whence) +{ + fa->out_numargs = 1; + fa->out_args[0].size = sizeof(args->out); + fa->out_args[0].value = &args->out; + + return 0; +} + +static int fuse_lseek_backing(struct bpf_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 bpf_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->info.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 bpf_fuse_backing(inode, struct fuse_lseek_args, 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 865167a80d35..9758bd1665a6 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -2779,6 +2779,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 5eb357f482dc..624d0cebd287 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1405,11 +1405,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 Apr 18 01:40:14 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214955 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 9FCFCC77B7C for ; Tue, 18 Apr 2023 01:42:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230290AbjDRBm5 (ORCPT ); Mon, 17 Apr 2023 21:42:57 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53850 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230496AbjDRBmS (ORCPT ); Mon, 17 Apr 2023 21:42:18 -0400 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 796CC7D84 for ; Mon, 17 Apr 2023 18:41:25 -0700 (PDT) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-552cb2211bdso36593687b3.6 for ; Mon, 17 Apr 2023 18:41:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782082; x=1684374082; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=XffErqhYI2OU7fEkAttVu8sFpvDYdEUvBhfwhueehVM=; b=W64W7LYmODf/FRuZNRmhqRfU8jwPzAf2EgQjFr/GwQnfixQx1lnFWxTQHQr2157bLk bOZx0B3xbbAKBKptnu3DGWAHW7YUWHUgXmFNH0B57uqjJVKJbfuIf6siNBmXF+8KrPKl rq/fklWlDzNR/LfPz5La++65odFf+sqMFipQNJ9fbMo8fpXLvw0QrXhDgD89PZ+Bh4Cv ClbkWZWwvPPc53kzJNCAMgQof+PQW1mdRELbdWXfLwzm+gUieIJC3zqNnbis5j2Pkdx0 ai+ArKZhDaSGmGokepURyuQtc1NoAeDczoa0Mwj+K+es3hQOC2CkYjntXKPcSV6FWNzd ApiQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782082; x=1684374082; 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=XffErqhYI2OU7fEkAttVu8sFpvDYdEUvBhfwhueehVM=; b=AohfJ3D5Jj27URzF8CPXQARGgyjF2Lcc82IiYAH+Zfad7A6f9/hDVDjrp2UiTXw1bD QRYkgQpAQWPwiWrb5w5usHksLJb861PrqtL4RbWcy3lF+Co/wxPWU8F/5TaTgz/BxjlT 9GgJEN+wMsu7EYowVEWWf1Xa8EAQ/crLEQlXIUSmeD0rF5M8mHaClTu3GH6Y6beBcVIc p5GVDNPmleBhqWlBuvTMLK2Se8R/KcpxEywo8Xzhrawps8rDLRJl1pFvbKgL3CkJUygk R1kWIIpPS8qaVh+6Dr3yQvrSlQ4YFMoVIoJ42Ao1F5euiHUOgS8+wPML1CDtNSd9yKGF c1Fg== X-Gm-Message-State: AAQBX9frXzdkaFgFf30QutP6oY0a4alPspxZolHW4KzoAjOg9aWyRaCq 9xxtyQA0rUM/qyjPhI8EztUHSR6U+xk= X-Google-Smtp-Source: AKy350bK1wIQRL+1GOlGecQQ1VG4drCo01/C/N6m/UsccfrVXFYrTKOINwumV372LVk08vQB5twTN47yWjc= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a81:b303:0:b0:545:1d7f:abfe with SMTP id r3-20020a81b303000000b005451d7fabfemr10434358ywh.7.1681782082574; Mon, 17 Apr 2023 18:41:22 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:14 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-15-drosen@google.com> Subject: [RFC PATCH v3 14/37] fuse-bpf: Add support for fallocate From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , 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 backing support for FUSE_FALLOCATE Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/file.c | 3 +++ fs/fuse/fuse_i.h | 6 +++++ 3 files changed, 69 insertions(+) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index c4916dde48c8..ee315598bc3f 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -333,6 +333,66 @@ ssize_t fuse_backing_mmap(struct file *file, struct vm_area_struct *vma) return ret; } +static int fuse_file_fallocate_initialize_in(struct bpf_fuse_args *fa, + struct fuse_fallocate_in *in, + struct file *file, int mode, loff_t offset, loff_t length) +{ + struct fuse_file *ff = file->private_data; + + *in = (struct fuse_fallocate_in) { + .fh = ff->fh, + .offset = offset, + .length = length, + .mode = mode, + }; + + *fa = (struct bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .opcode = FUSE_FALLOCATE, + .nodeid = ff->nodeid, + }, + .in_numargs = 1, + .in_args[0].size = sizeof(*in), + .in_args[0].value = in, + }; + + return 0; +} + +static int fuse_file_fallocate_initialize_out(struct bpf_fuse_args *fa, + struct fuse_fallocate_in *in, + struct file *file, int mode, loff_t offset, loff_t length) +{ + return 0; +} + +static int fuse_file_fallocate_backing(struct bpf_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 bpf_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 bpf_fuse_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 9758bd1665a6..58cff04660db 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -3071,6 +3071,9 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, (!(mode & FALLOC_FL_KEEP_SIZE) || (mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE))); + 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 624d0cebd287..1dd9cc9720df 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1406,6 +1406,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); @@ -1416,6 +1417,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 Apr 18 01:40:15 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214956 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 5A8AFC77B76 for ; Tue, 18 Apr 2023 01:43:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230417AbjDRBnM (ORCPT ); Mon, 17 Apr 2023 21:43:12 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54332 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230505AbjDRBmT (ORCPT ); Mon, 17 Apr 2023 21:42:19 -0400 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 C09CA7DA1 for ; Mon, 17 Apr 2023 18:41:27 -0700 (PDT) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-54fd5d0ad7cso104337677b3.1 for ; Mon, 17 Apr 2023 18:41:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782085; x=1684374085; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=EclmRuCEs8AtdYsWbPtv7S1s8N7D3XoEPaguP9KMkfI=; b=mmwYVHjRAK5Z159C4imU0XQjkYtfnCBYjv6l4C4bOCpMXKwBtApNX02vwb7wGKRz4N 5XN3HQ4NzLI38FzrpsJ+pijncme7++sNr3Lhf5SYjX2+5hrrxG0qic8LTCsLQ/L3V1tl ZnXYn62ItIO97Ypuoa624x4qnYf6vElPXxFcygSYU7x1tbJdV1rnJIz2kFBABcJUHC/R ODYpnlJdo3GOx6g8lnndopkLGXfrP5bGFTxF2LWebBVsKgZtSOfuB1fZS4av1rYR4v8d dXRXapxs4gY9IzCAWPPhicID2CTLtG3lyLn96zZ5kfplK6rLfb34n9wqbKDNzu3jXRVL DyFQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782085; x=1684374085; 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=EclmRuCEs8AtdYsWbPtv7S1s8N7D3XoEPaguP9KMkfI=; b=S63PXflMfiLjxgYMfxXXL3VrVtEL9SgfFuK6axAxH13+5rSImLqYmQC3DWp55xuDD2 03UTpC2j27rAv1KrP6HbVFTFePpX03N+DscUr0LOcZq4DTbQ0zNlw+PsPkYhfrhTIiwT 1SwxEI4eV4LJAQ5VwX/mN8auKc58WtRdQ9/3qA3oKXnChzwx2SvqHwlS6n6YOtRKbViF NpszRiNVputjGfbzm6p1FbrnrAnGvMXJi/pMBxAe/uF+I30c3fISy+gFjzgTr1R6SjCM L7xIhROhPR0xNyuiZ1RXYDtbD/wpcsoiqSFVrJa7y6e0UAt1Sp4uW9vUZhOnRelNpI/A qrRA== X-Gm-Message-State: AAQBX9eRKEoscuD7TXlcPi9aUCBLBYuGq2ozBs3cyAAR4XGRVGiwtebt jLXv/XHa7rRzAo9X5HaS7CKWQjk3zgM= X-Google-Smtp-Source: AKy350ZZ9Icx+O5cAn8L7UL4LyCuWNAJhMF/AQzRrb3mRpbXzhEHNdWb1PPKet4jhSmTfS5WrLVoXSBCA6Y= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a25:d08d:0:b0:b7c:1144:a708 with SMTP id h135-20020a25d08d000000b00b7c1144a708mr10996656ybg.12.1681782084915; Mon, 17 Apr 2023 18:41:24 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:15 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-16-drosen@google.com> Subject: [RFC PATCH v3 15/37] fuse-bpf: Support file/dir open/close From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , 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 backing support for FUSE_OPEN, FUSE_OPENDIR, FUSE_CREATE, FUSE_RELEASE, and FUSE_RELEASEDIR Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 368 ++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/dir.c | 8 + fs/fuse/file.c | 7 + fs/fuse/fuse_i.h | 26 ++++ 4 files changed, 409 insertions(+) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index ee315598bc3f..d4a214cadc15 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -207,6 +207,374 @@ static void fuse_stat_to_attr(struct fuse_conn *fc, struct inode *inode, attr->blksize = 1 << blkbits; } +struct fuse_open_args { + struct fuse_open_in in; + struct fuse_open_out out; +}; + +static int fuse_open_initialize_in(struct bpf_fuse_args *fa, struct fuse_open_args *args, + struct inode *inode, struct file *file, bool isdir) +{ + args->in = (struct fuse_open_in) { + .flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY), + }; + *fa = (struct bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .nodeid = get_fuse_inode(inode)->nodeid, + .opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN, + }, + .in_numargs = 1, + .in_args[0] = (struct bpf_fuse_arg) { + .size = sizeof(args->in), + .value = &args->in, + }, + }; + + return 0; +} + +static int fuse_open_initialize_out(struct bpf_fuse_args *fa, struct fuse_open_args *args, + struct inode *inode, struct file *file, bool isdir) +{ + args->out = (struct fuse_open_out) { 0 }; + + fa->out_numargs = 1; + fa->out_args[0] = (struct bpf_fuse_arg) { + .size = sizeof(args->out), + .value = &args->out, + }; + + return 0; +} + +static int fuse_open_backing(struct bpf_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(&nop_mnt_idmap, + 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 bpf_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 bpf_fuse_backing(inode, struct fuse_open_args, out, + fuse_open_initialize_in, fuse_open_initialize_out, + fuse_open_backing, fuse_open_finalize, + inode, file, isdir); +} + +struct fuse_create_open_args { + struct fuse_create_in in; + struct fuse_buffer name; + struct fuse_entry_out entry_out; + struct fuse_open_out open_out; +}; + +static int fuse_create_open_initialize_in(struct bpf_fuse_args *fa, struct fuse_create_open_args *args, + struct inode *dir, struct dentry *entry, + struct file *file, unsigned int flags, umode_t mode) +{ + args->in = (struct fuse_create_in) { + .flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY), + .mode = mode, + }; + + args->name = (struct fuse_buffer) { + .data = (void *) entry->d_name.name, + .size = entry->d_name.len + 1, + .flags = BPF_FUSE_IMMUTABLE, + }; + + *fa = (struct bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .nodeid = get_node_id(dir), + .opcode = FUSE_CREATE, + }, + .in_numargs = 2, + .in_args[0] = (struct bpf_fuse_arg) { + .size = sizeof(args->in), + .value = &args->in, + }, + .in_args[1] = (struct bpf_fuse_arg) { + .is_buffer = true, + .buffer = &args->name, + }, + }; + + return 0; +} + +static int fuse_create_open_initialize_out(struct bpf_fuse_args *fa, struct fuse_create_open_args *args, + struct inode *dir, struct dentry *entry, + struct file *file, unsigned int flags, umode_t mode) +{ + args->entry_out = (struct fuse_entry_out) { 0 }; + args->open_out = (struct fuse_open_out) { 0 }; + + fa->out_numargs = 2; + fa->out_args[0] = (struct bpf_fuse_arg) { + .size = sizeof(args->entry_out), + .value = &args->entry_out, + }; + fa->out_args[1] = (struct bpf_fuse_arg) { + .size = sizeof(args->open_out), + .value = &args->open_out, + }; + + 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 bpf_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(&nop_mnt_idmap, 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 bpf_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 bpf_fuse_backing(dir, struct fuse_create_open_args, 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 bpf_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 bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .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 bpf_fuse_args *fa, struct fuse_release_in *fri, + struct inode *inode, struct file *file) +{ + return 0; +} + +static int fuse_releasedir_initialize_in(struct bpf_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 bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .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 bpf_fuse_args *fa, + struct fuse_release_in *fri, + struct inode *inode, struct file *file) +{ + return 0; +} + +static int fuse_release_backing(struct bpf_fuse_args *fa, int *out, + struct inode *inode, struct file *file) +{ + return 0; +} + +static int fuse_release_finalize(struct bpf_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 bpf_fuse_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 bpf_fuse_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_args { struct fuse_lseek_in in; struct fuse_lseek_out out; diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 535e6cf9e970..1df2bbc72396 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -719,6 +719,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) @@ -1629,6 +1632,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 58cff04660db..1836d09d9ce3 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -243,6 +243,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); @@ -351,6 +354,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 1dd9cc9720df..feecc1ebfdda 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1405,6 +1405,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); @@ -1412,6 +1417,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 Apr 18 01:40:16 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214958 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 7DA9FC77B72 for ; Tue, 18 Apr 2023 01:43:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230189AbjDRBn3 (ORCPT ); Mon, 17 Apr 2023 21:43:29 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53884 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230149AbjDRBma (ORCPT ); Mon, 17 Apr 2023 21:42:30 -0400 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 363F783C2 for ; Mon, 17 Apr 2023 18:41:35 -0700 (PDT) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-54feaa94819so79482327b3.2 for ; Mon, 17 Apr 2023 18:41:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782087; x=1684374087; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=NDDGgrzSklW9PeQhCnnSrKGQK+GKlru6fDSS3O5BjmA=; b=ZH5Or0LVxK0ACl0MYkteAzESD99/7Y6eEY7bCBEA2/UuGiSKlZfZQrdX8//nPc5nnK aJRB9zReYRfrzP6d9Tm5nolDNIPFi0ZArQix6zL92mKogJZGyyJ4PoxLe7+MZrdvb9wO kQzhsjswf9u1btEBhZRZNqtsp1rTkCG+uQxMOSb7QE3FpkXqeV2NOwqoXk4OAg1om9pg xbA94KAfpNG9a0xgBppvH0P/oMFCe049pKZOYRMru06j+Wx//wEy38p0FUuAuX2U+8I7 MW5H+7BpQvPjjs+oyFzPJ+0Ar9sgfNJqCfxtarg9uNdtUS4R8CyMoCC4J77InmpYcLjF bTpA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782087; x=1684374087; 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=NDDGgrzSklW9PeQhCnnSrKGQK+GKlru6fDSS3O5BjmA=; b=LBhjh8+lQHEfbi0nb1tzbxtUeruttQZfmUJq6S+TlxFL4+QJUXs8EAGolI9LetjMZu 7fGiEAZ2ZQQs1QWWzJ6U9ek3QRq4j8N0Q1iSyqKDYenFMtmIwkknhAisALS4p555bmPq clVeep3xb/qbPteX8v13hYdj1PLdaFDO/ayqAIdtrGZcCs3iiZD2L2bHN2lZ/1SEUr4m d5mLx3D3MaQ6dWjtfW+BK/QnF3oqQphZvAZtlZLXIuZPP8hpSrzNl8OnYBIrTmVtBNbH w9gydXvQp+8br/7YLjrfINFkLXkNKwOAZqsShCATzyWr94qaxDlTJ3P1lZeAnEBvO+Y2 uFrg== X-Gm-Message-State: AAQBX9cxpo4AbpBsIat43nc4jRLmwG4e5G0puUGukg/5Y0n0MKJB0w3B wvsOmIEMlUEMlJamy04fvpZrAdKc/JA= X-Google-Smtp-Source: AKy350b2QTc7qTknU5iRQ/AMnSRDgnYtVdT/oLHx5r2YmfDH4vM6DkZ756se8OX1GhvWk/AZEjOfYn0mUv0= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a25:d8c1:0:b0:b8b:f594:d512 with SMTP id p184-20020a25d8c1000000b00b8bf594d512mr8744186ybg.13.1681782087328; Mon, 17 Apr 2023 18:41:27 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:16 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-17-drosen@google.com> Subject: [RFC PATCH v3 16/37] fuse-bpf: Support mknod/unlink/mkdir/rmdir From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , 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 backing support for FUSE_MKNOD, FUSE_MKDIR, FUSE_RMDIR, and FUSE_UNLINK Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 342 ++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/dir.c | 14 ++ fs/fuse/fuse_i.h | 24 ++++ 3 files changed, 380 insertions(+) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index d4a214cadc15..c6ef10aeec15 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -972,6 +972,348 @@ int fuse_revalidate_backing(struct dentry *entry, unsigned int flags) return 1; } +struct fuse_mknod_args { + struct fuse_mknod_in in; + struct fuse_buffer name; +}; + +static int fuse_mknod_initialize_in(struct bpf_fuse_args *fa, struct fuse_mknod_args *args, + struct inode *dir, struct dentry *entry, umode_t mode, dev_t rdev) +{ + *args = (struct fuse_mknod_args) { + .in = (struct fuse_mknod_in) { + .mode = mode, + .rdev = new_encode_dev(rdev), + .umask = current_umask(), + }, + .name = (struct fuse_buffer) { + .data = (void *) entry->d_name.name, + .size = entry->d_name.len + 1, + .flags = BPF_FUSE_IMMUTABLE, + }, + }; + *fa = (struct bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .nodeid = get_node_id(dir), + .opcode = FUSE_MKNOD, + }, + .in_numargs = 2, + .in_args[0] = (struct bpf_fuse_arg) { + .size = sizeof(args->in), + .value = &args->in, + }, + .in_args[1] = (struct bpf_fuse_arg) { + .is_buffer = true, + .buffer = &args->name, + }, + }; + + return 0; +} + +static int fuse_mknod_initialize_out(struct bpf_fuse_args *fa, struct fuse_mknod_args *args, + struct inode *dir, struct dentry *entry, umode_t mode, dev_t rdev) +{ + return 0; +} + +static int fuse_mknod_backing(struct bpf_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(&nop_mnt_idmap, 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 bpf_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 bpf_fuse_backing(dir, struct fuse_mknod_args, out, + fuse_mknod_initialize_in, fuse_mknod_initialize_out, + fuse_mknod_backing, fuse_mknod_finalize, + dir, entry, mode, rdev); +} + +struct fuse_mkdir_args { + struct fuse_mkdir_in in; + struct fuse_buffer name; +}; + +static int fuse_mkdir_initialize_in(struct bpf_fuse_args *fa, struct fuse_mkdir_args *args, + struct inode *dir, struct dentry *entry, umode_t mode) +{ + *args = (struct fuse_mkdir_args) { + .in = (struct fuse_mkdir_in) { + .mode = mode, + .umask = current_umask(), + }, + .name = (struct fuse_buffer) { + .data = (void *) entry->d_name.name, + .size = entry->d_name.len + 1, + .flags = BPF_FUSE_IMMUTABLE, + }, + }; + + *fa = (struct bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .nodeid = get_node_id(dir), + .opcode = FUSE_MKDIR, + }, + .in_numargs = 2, + .in_args[0] = (struct bpf_fuse_arg) { + .size = sizeof(args->in), + .value = &args->in, + }, + .in_args[1] = (struct bpf_fuse_arg) { + .value = &args->name, + .is_buffer = true, + }, + }; + + return 0; +} + +static int fuse_mkdir_initialize_out(struct bpf_fuse_args *fa, struct fuse_mkdir_args *args, + struct inode *dir, struct dentry *entry, umode_t mode) +{ + return 0; +} + +static int fuse_mkdir_backing(struct bpf_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(&nop_mnt_idmap, 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 bpf_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 bpf_fuse_backing(dir, struct fuse_mkdir_args, 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 bpf_fuse_args *fa, struct fuse_buffer *name, + struct inode *dir, struct dentry *entry) +{ + *name = (struct fuse_buffer) { + .data = (void *) entry->d_name.name, + .size = entry->d_name.len + 1, + .flags = BPF_FUSE_IMMUTABLE, + }; + *fa = (struct bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .nodeid = get_node_id(dir), + .opcode = FUSE_RMDIR, + }, + .in_numargs = 1, + .in_args[0] = (struct bpf_fuse_arg) { + .is_buffer = true, + .buffer = name, + }, + }; + + return 0; +} + +static int fuse_rmdir_initialize_out(struct bpf_fuse_args *fa, struct fuse_buffer *name, + struct inode *dir, struct dentry *entry) +{ + return 0; +} + +static int fuse_rmdir_backing(struct bpf_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(&nop_mnt_idmap, 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 bpf_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 bpf_fuse_backing(dir, struct fuse_buffer, out, + fuse_rmdir_initialize_in, fuse_rmdir_initialize_out, + fuse_rmdir_backing, fuse_rmdir_finalize, + dir, entry); +} + +static int fuse_unlink_initialize_in(struct bpf_fuse_args *fa, struct fuse_buffer *name, + struct inode *dir, struct dentry *entry) +{ + *name = (struct fuse_buffer) { + .data = (void *) entry->d_name.name, + .size = entry->d_name.len + 1, + .flags = BPF_FUSE_IMMUTABLE, + }; + *fa = (struct bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .nodeid = get_node_id(dir), + .opcode = FUSE_UNLINK, + }, + .in_numargs = 1, + .in_args[0] = (struct bpf_fuse_arg) { + .is_buffer = true, + .buffer = name, + }, + }; + + return 0; +} + +static int fuse_unlink_initialize_out(struct bpf_fuse_args *fa, struct fuse_buffer *name, + struct inode *dir, struct dentry *entry) +{ + return 0; +} + +static int fuse_unlink_backing(struct bpf_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(&nop_mnt_idmap, 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 bpf_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 bpf_fuse_backing(dir, struct fuse_buffer, out, + fuse_unlink_initialize_in, fuse_unlink_initialize_out, + fuse_unlink_backing, fuse_unlink_finalize, + dir, entry); +} + static int fuse_access_initialize_in(struct bpf_fuse_args *fa, struct fuse_access_in *in, struct inode *inode, int mask) { diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 1df2bbc72396..a763a45fa973 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -937,6 +937,10 @@ static int fuse_mknod(struct mnt_idmap *idmap, 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(); @@ -983,6 +987,10 @@ static int fuse_mkdir(struct mnt_idmap *idmap, 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(); @@ -1069,6 +1077,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; @@ -1092,6 +1103,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 feecc1ebfdda..2cbe232c1048 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1408,6 +1408,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); @@ -1428,6 +1432,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 Apr 18 01:40:17 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214957 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 A3E61C77B7C for ; Tue, 18 Apr 2023 01:43:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230315AbjDRBnP (ORCPT ); Mon, 17 Apr 2023 21:43:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53790 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230432AbjDRBm3 (ORCPT ); Mon, 17 Apr 2023 21:42:29 -0400 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 D11707DB2 for ; Mon, 17 Apr 2023 18:41:29 -0700 (PDT) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-54be7584b28so368338127b3.16 for ; Mon, 17 Apr 2023 18:41:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782089; x=1684374089; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=Z4g1tB3udC3qOuw7tknQnFIwzeoIaxj84BFW0ZiQCFQ=; b=PWNxqCY04pgGT68/fhpmjWUAwCbx54KY6uSUjjYhQqzp9lLJvHVDSqt+SI6+HzFwO7 fyG+VrOF03Whi4jmxb9IPQLjIWF1tt5QBDpzqohLc0FDjVHNm1qBZ0OdxWQJ4SOM5GaY JmoIxTjQptfm3JGLYD4GlLL/ahIixyaK+FaZLM1NRpT7qrBURyp0+1U0UNiqr7DKn0GS SnqgwZxuPPks4eiFf/F1WIrn8+rzTH/w9PcFNAyzvAMjaFEMXE/iQ1Ngtw+5daeO5Js2 2KH3kevDzRqBqh44lbo+ARV65hd/S1NuMvXtvdyiJ+OVk9nbQYzXiQwH4zNY8CIi3PYZ APvA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782089; x=1684374089; 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=Z4g1tB3udC3qOuw7tknQnFIwzeoIaxj84BFW0ZiQCFQ=; b=PZPQ5Ki765FTgtRWDotkcraO1CZNjwKsIsp6ShjfBQKn1/dUfs60mgJD82IrPJqiF/ lDMofiv4nw7IW3oGIYCOLcGDJKmbg/aaKg4GZQwHK1jjDYg6uTP6TpqmeN8lGFe6rbTB XgnhZ0GeVTRuCZ5Wy+2ZByGvyivh7olCQ2w1l2JJQkPItTnWZfGl5qLIN4AZqJl/S8YN UOOi8QFYszTGP4aQF6zyUlkTUdCjvicbnwqhD9rPmMdCMwXgGsFzJ4i8AEH8e8AXxJdK yeKjmTN6Yq8bN08TclSZ1gGQegh8iZV4aOHEc+fwpnNIJxevvid+k45h/fGCPu2Io1+Z gbKQ== X-Gm-Message-State: AAQBX9cfWAt5VTmV68o1RUU965XfcNgqeJ1XwJBIMGPQXqCTubldAWN3 EECHtnrSYs11oguSd8oLwrjjvcCjYE0= X-Google-Smtp-Source: AKy350bpzdbUr7ugtKzWZK023/0mleS8ekb1JXmOGLJPSBH89ux6GBIOfdzqZrMzBVw/FzqHBnUFAl5jOMo= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a81:af53:0:b0:555:a687:e151 with SMTP id x19-20020a81af53000000b00555a687e151mr967497ywj.3.1681782089597; Mon, 17 Apr 2023 18:41:29 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:17 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-18-drosen@google.com> Subject: [RFC PATCH v3 17/37] fuse-bpf: Add support for read/write iter From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , kernel-team@android.com, Daniel Rosenberg , Paul Lawrence Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC Adds backing support for FUSE_READ and FUSE_WRITE 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 | 371 ++++++++++++++++++++++++++++++++++++++ fs/fuse/control.c | 2 +- fs/fuse/file.c | 8 + fs/fuse/fuse_i.h | 19 +- fs/fuse/inode.c | 13 ++ include/uapi/linux/fuse.h | 10 + 6 files changed, 421 insertions(+), 2 deletions(-) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index c6ef10aeec15..c7709a880e9c 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -11,6 +11,7 @@ #include #include #include +#include /* * expression statement to wrap the backing filter logic @@ -76,6 +77,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); @@ -664,6 +748,277 @@ 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_file_read_iter_args { + struct fuse_read_in in; + struct fuse_read_iter_out out; +}; + +static int fuse_file_read_iter_initialize_in(struct bpf_fuse_args *fa, struct fuse_file_read_iter_args *args, + struct kiocb *iocb, struct iov_iter *to) +{ + struct file *file = iocb->ki_filp; + struct fuse_file *ff = file->private_data; + + args->in = (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 bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .opcode = FUSE_READ, + .nodeid = ff->nodeid, + }, .in_numargs = 1, + .in_args[0].size = sizeof(args->in), + .in_args[0].value = &args->in, + /* + * 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 bpf_fuse_args *fa, struct fuse_file_read_iter_args *args, + struct kiocb *iocb, struct iov_iter *to) +{ + args->out = (struct fuse_read_iter_out) { + .ret = args->in.size, + }; + + fa->out_numargs = 1; + fa->out_args[0].size = sizeof(args->out); + fa->out_args[0].value = &args->out; + + return 0; +} + +static int fuse_file_read_iter_backing(struct bpf_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 bpf_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 bpf_fuse_backing(inode, struct fuse_file_read_iter_args, 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_file_write_iter_args { + struct fuse_write_in in; + struct fuse_write_iter_out out; +}; + +static int fuse_file_write_iter_initialize_in(struct bpf_fuse_args *fa, + struct fuse_file_write_iter_args *args, + struct kiocb *iocb, struct iov_iter *from) +{ + struct file *file = iocb->ki_filp; + struct fuse_file *ff = file->private_data; + + *args = (struct fuse_file_write_iter_args) { + .in.fh = ff->fh, + .in.offset = iocb->ki_pos, + .in.size = from->count, + }; + + /* TODO we can't assume 'from' is a kvec */ + *fa = (struct bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .opcode = FUSE_WRITE, + .nodeid = ff->nodeid, + }, + .in_numargs = 1, + .in_args[0].size = sizeof(args->in), + .in_args[0].value = &args->in, + }; + + return 0; +} + +static int fuse_file_write_iter_initialize_out(struct bpf_fuse_args *fa, + struct fuse_file_write_iter_args *args, + 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(args->out); + fa->out_args[0].value = &args->out; + + return 0; +} + +static int fuse_file_write_iter_backing(struct bpf_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 bpf_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 bpf_fuse_backing(inode, struct fuse_file_write_iter_args, 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; @@ -1360,3 +1715,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 1836d09d9ce3..5f19ef5bf124 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1679,6 +1679,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; @@ -1686,6 +1687,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 @@ -1697,6 +1701,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; @@ -1704,6 +1709,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 2cbe232c1048..4bc070b81ac2 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1135,7 +1135,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 @@ -1415,6 +1415,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); @@ -1467,6 +1469,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; @@ -1517,4 +1529,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 fe80984f099a..fd00910f1eb1 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -2095,11 +2095,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: @@ -2117,6 +2127,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(); } diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index 3ad725a3e968..dbfc8d501bcb 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -748,6 +748,11 @@ struct fuse_read_in { uint32_t padding; }; +// This is likely not what we want +struct fuse_read_iter_out { + uint64_t ret; +}; + #define FUSE_COMPAT_WRITE_IN_SIZE 24 struct fuse_write_in { @@ -765,6 +770,11 @@ struct fuse_write_out { uint32_t padding; }; +// This is likely not what we want +struct fuse_write_iter_out { + uint64_t ret; +}; + #define FUSE_COMPAT_STATFS_SIZE 48 struct fuse_statfs_out { From patchwork Tue Apr 18 01:40:18 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214959 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 4F5ACC77B7C for ; Tue, 18 Apr 2023 01:43:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231210AbjDRBnb (ORCPT ); Mon, 17 Apr 2023 21:43:31 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53244 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230517AbjDRBmd (ORCPT ); Mon, 17 Apr 2023 21:42:33 -0400 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 F3D0B5FC4 for ; Mon, 17 Apr 2023 18:41:41 -0700 (PDT) Received: by mail-yb1-xb49.google.com with SMTP id 84-20020a251457000000b00b8f59a09e1fso11380585ybu.5 for ; Mon, 17 Apr 2023 18:41:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782092; x=1684374092; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=KLfNH8wOaoEhBmLooOhZO4y32d1iMom08eY0cFO8TR0=; b=WjKoP43zShmBluCRjRG7SpJRr6ZhcFjGdtCDccumEdmZPIzgFTJvIKad4FYUIjjQQy IplrWk3NdRdH2gDDLJQ9Cx/UvfS1qSwkngDqd21ylbmG4ukfCRinSmNNAs5uppGzVJLG lpzuh+saSdzIC/lLpNoMzuaoBLoXD19zIu7i6cCa28+pGHuAQbBcT10P4y35gFo66XQa Tnn9wbyumgtkRMoHf71AAl1+0l3ZdUh4BWT0RNrOjfa3KXsL2/sdfWUPFVrKfqF59nvb JuM8Zm1GDKLnP4FRMd0bLhe8vn0smcB/2wdsoJsfUhL4Z5/rqi8pn3H217Z4nqyfNjCk TSiA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782092; x=1684374092; 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=KLfNH8wOaoEhBmLooOhZO4y32d1iMom08eY0cFO8TR0=; b=GgnpAg72SWm6vnvsUOd2uv8hwdeuOAfaMk7IB+OUXt34F9XYXR40+2R5wJls+7wr1x kN0Y10wNfmFeJb41A5gyMXyUpA/1JvVmVCS/8+r5gFCk1KMIv2UvI6iVIdw5DcSwY/Mb 3E0kmqhMzcNc7AhM/Jd+8oAK2S1heS2FBED31zKRIhm+xAtPJQkqRStj24rBPUl8CQBx 4pGMkcGWo4erUskgZ+MwBnpFLf8+B5SY9upTGZlfAn+BvBPM0zVWYr5xbJJQUMOKUPu7 Ix6MufGIHrsFZZbzBHf/almFFkJJ7WsW/9CSAqijmJf8UL26jhJC73QFInzWA0rb5HFh RZ6g== X-Gm-Message-State: AAQBX9dJs3iQXOoR2tMmzL/zaUfAVrNumOmNziClJXbJS9R2IQfdSG+0 v4Uy6Hb6EFnEAiEF9sWJjQJ6g6LlzDw= X-Google-Smtp-Source: AKy350ZFWi1/rz2iUYQDDITIMq4pvfy+q1dsB6RG635Ep9uEFEuOKaLHWxdFW4DJWTE+80pH5vnhyBiQBOM= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a05:690c:d91:b0:54f:e88d:79ba with SMTP id da17-20020a05690c0d9100b0054fe88d79bamr9714213ywb.5.1681782091925; Mon, 17 Apr 2023 18:41:31 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:18 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-19-drosen@google.com> Subject: [RFC PATCH v3 18/37] fuse-bpf: support readdir From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , 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 backing support for FUSE_READDIR Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 194 ++++++++++++++++++++++++++++++++++++++ fs/fuse/fuse_i.h | 6 ++ fs/fuse/readdir.c | 5 + include/uapi/linux/fuse.h | 6 ++ 4 files changed, 211 insertions(+) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index c7709a880e9c..2908c231a695 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -1669,6 +1669,200 @@ int fuse_bpf_unlink(int *out, struct inode *dir, struct dentry *entry) dir, entry); } +struct fuse_read_args { + struct fuse_read_in in; + struct fuse_read_out out; + struct fuse_buffer buffer; +}; + +static int fuse_readdir_initialize_in(struct bpf_fuse_args *fa, struct fuse_read_args *args, + 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 bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .nodeid = ff->nodeid, + .opcode = FUSE_READDIR, + }, + .in_numargs = 1, + .in_args[0] = (struct bpf_fuse_arg) { + .size = sizeof(args->in), + .value = &args->in, + }, + }; + + args->in = (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 bpf_fuse_args *fa, struct fuse_read_args *args, + 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->flags = FUSE_BPF_OUT_ARGVAR; + fa->out_numargs = 2; + fa->out_args[0] = (struct bpf_fuse_arg) { + .size = sizeof(args->out), + .value = &args->out, + }; + fa->out_args[1] = (struct bpf_fuse_arg) { + .is_buffer = true, + .buffer = &args->buffer, + }; + args->out = (struct fuse_read_out) { + .again = 0, + .offset = 0, + }; + args->buffer = (struct fuse_buffer) { + .data = page, + .size = PAGE_SIZE, + .alloc_size = PAGE_SIZE, + .max_size = PAGE_SIZE, + .flags = BPF_FUSE_VARIABLE_SIZE, + }; + + return 0; +} + +struct fusebpf_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 fusebpf_ctx *ec = container_of(ctx, struct fusebpf_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 bpf_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 fusebpf_ctx ec; + + ec = (struct fusebpf_ctx) { + .ctx.actor = filldir, + .ctx.pos = ctx->pos, + .addr = fa->out_args[1].buffer->data, + }; + + 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].buffer->size = ec.offset; + + fro->offset = ec.ctx.pos; + fro->again = false; + + return *out; +} + +static int fuse_readdir_finalize(struct bpf_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].buffer->data, fa->out_args[1].buffer->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].buffer->data); + 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 = bpf_fuse_backing(inode, struct fuse_read_args, 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 bpf_fuse_args *fa, struct fuse_access_in *in, struct inode *inode, int mask) { diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 4bc070b81ac2..fb3a77b79b0f 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1419,6 +1419,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 @@ -1489,6 +1490,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 dc603479b30e..cc6548f314f2 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 dbfc8d501bcb..e779064f5fad 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -748,6 +748,12 @@ struct fuse_read_in { uint32_t padding; }; +struct fuse_read_out { + uint64_t offset; + uint32_t again; + uint32_t padding; +}; + // This is likely not what we want struct fuse_read_iter_out { uint64_t ret; From patchwork Tue Apr 18 01:40:19 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214961 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 B41F9C77B72 for ; Tue, 18 Apr 2023 01:43:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231230AbjDRBnh (ORCPT ); Mon, 17 Apr 2023 21:43:37 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53818 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231145AbjDRBmo (ORCPT ); Mon, 17 Apr 2023 21:42:44 -0400 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 D3E5061AE for ; Mon, 17 Apr 2023 18:41:44 -0700 (PDT) Received: by mail-yb1-xb4a.google.com with SMTP id d14-20020a25e60e000000b00b8f65697946so10091398ybh.2 for ; Mon, 17 Apr 2023 18:41:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782094; x=1684374094; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=yHPyQOgLAeTv27Hmk1jV/Qw6lBeg9KsOnD3+nmdgTBc=; b=WfhU75GkbNBBGPNdlQQqWpIVlOAmIt1xhsdC8ETWkv8e89D2kVJXdfo2nDvpBH4oXa nqAcJ0rkvNDwDtDwu+BjaH1Q8oFoMz/kg0Or59UBboMN/KJfqzGCb2RqOb/U/0RSQ32t dDjHoFKhRwU61k68E2xItY5TqBQZUfdSkdlDFCBzv5zViEbPhG4YAFlvC6a4+lykVyz3 PN4aTi6QZSAJo3dKZSZtek7fRmsqdmoYf8DNy1EJQUBpOuz65vrZutPzrVi47/+A8syB akZ3w8TvKAFp/kH3G4HD2NJLL5M0F242/508++FqsS0CRy19r4HQJW7deZinJ5f6k7Yh LtIA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782094; x=1684374094; 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=yHPyQOgLAeTv27Hmk1jV/Qw6lBeg9KsOnD3+nmdgTBc=; b=GQ/POG+hyNoKjsjhWQ+CZlzhrOFWvslhA8VfoshX3WFP9Z9XRSOuM2Kvg8VrLEYZ41 wLu0p8kZaoirBhHCsoGEUwXPeGF9D1Be6oOkscvfGLxO01gG/0YNHXN2rhtU2Bv2gZ0Z YSSP5tVeENtDbMMiiujgUSJ1Uqe0LLreJP6I6+3rO5IfaXIaflMlF+vsSXLXp2GQUB+e s9QXxb8RHQEsh+MItGIRnGuTzI7PuhPsT0m1qJHsU4nXq0OZSNAX7Cp/oPFZBNi0ZGGf whXN+enD2UmW6YKZnRSzku/1gplbKgxkKRGGEHAdPFqkXltLgyK/lMkR8PMO9y3DpJ3u vYTA== X-Gm-Message-State: AAQBX9fWrpfxTnoX+oCQe+/H9yzgvNV8Fs00JfmN9lmpadgmTJ3W6uLR wCprTw0bcCUdWzY1y2yDSZtFfzOKs3Y= X-Google-Smtp-Source: AKy350ZGdfGHneASHc+2qR8hpHc/w9BF7/xnpO/COHxv9rPK2B1jF3L8CR7fh6IuzYQx0u6d+K6DOtAYt2s= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a25:8051:0:b0:b92:40f8:a46f with SMTP id a17-20020a258051000000b00b9240f8a46fmr4359628ybn.2.1681782094311; Mon, 17 Apr 2023 18:41:34 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:19 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-20-drosen@google.com> Subject: [RFC PATCH v3 19/37] fuse-bpf: Add support for sync operations From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , 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 backing support for FUSE_FLUSH, FUSE_FSYNC, and FUSE_FSYNCDIR. Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/dir.c | 3 + fs/fuse/file.c | 7 +++ fs/fuse/fuse_i.h | 18 ++++++ 4 files changed, 175 insertions(+) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index 2908c231a695..30492f7b2a05 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -659,6 +659,59 @@ 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 bpf_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 bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .nodeid = get_node_id(file->f_inode), + .opcode = FUSE_FLUSH, + }, + .in_numargs = 1, + .in_args[0].size = sizeof(*ffi), + .in_args[0].value = ffi, + .flags = FUSE_BPF_FORCE, + }; + + return 0; +} + +static int fuse_flush_initialize_out(struct bpf_fuse_args *fa, struct fuse_flush_in *ffi, + struct file *file, fl_owner_t id) +{ + return 0; +} + +static int fuse_flush_backing(struct bpf_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 bpf_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 bpf_fuse_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_args { struct fuse_lseek_in in; struct fuse_lseek_out out; @@ -748,6 +801,100 @@ 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 bpf_fuse_args *fa, struct fuse_fsync_in *in, + struct file *file, loff_t start, loff_t end, int datasync) +{ + struct fuse_file *fuse_file = file->private_data; + + *in = (struct fuse_fsync_in) { + .fh = fuse_file->fh, + .fsync_flags = datasync ? FUSE_FSYNC_FDATASYNC : 0, + }; + + *fa = (struct bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .nodeid = get_fuse_inode(file->f_inode)->nodeid, + .opcode = FUSE_FSYNC, + }, + .in_numargs = 1, + .in_args[0].size = sizeof(*in), + .in_args[0].value = in, + .flags = FUSE_BPF_FORCE, + }; + + return 0; +} + +static int fuse_fsync_initialize_out(struct bpf_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 bpf_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 bpf_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 bpf_fuse_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 bpf_fuse_args *fa, struct fuse_fsync_in *in, + struct file *file, loff_t start, loff_t end, int datasync) +{ + struct fuse_file *fuse_file = file->private_data; + + *in = (struct fuse_fsync_in) { + .fh = fuse_file->fh, + .fsync_flags = datasync ? FUSE_FSYNC_FDATASYNC : 0, + }; + + *fa = (struct bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .nodeid = get_fuse_inode(file->f_inode)->nodeid, + .opcode = FUSE_FSYNCDIR, + }, + .in_numargs = 1, + .in_args[0].size = sizeof(*in), + .in_args[0].value = in, + .flags = FUSE_BPF_FORCE, + }; + + return 0; +} + +static int fuse_dir_fsync_initialize_out(struct bpf_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 bpf_fuse_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 a763a45fa973..5ce65f696980 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1666,6 +1666,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 5f19ef5bf124..a4a0aeb28e4a 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -554,10 +554,14 @@ static int fuse_flush(struct file *file, fl_owner_t id) struct inode *inode = file_inode(file); struct fuse_mount *fm = get_fuse_mount(inode); struct fuse_file *ff = file->private_data; + int err; 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; @@ -615,6 +619,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 fb3a77b79b0f..e60207bf66de 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1414,7 +1414,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); @@ -1465,11 +1468,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 Apr 18 01:40:20 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214960 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 E28D6C77B7E for ; Tue, 18 Apr 2023 01:43:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230347AbjDRBne (ORCPT ); Mon, 17 Apr 2023 21:43:34 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54294 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230361AbjDRBmq (ORCPT ); Mon, 17 Apr 2023 21:42:46 -0400 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 0EF10618A for ; Mon, 17 Apr 2023 18:41:47 -0700 (PDT) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-54f69fb5cafso190551157b3.12 for ; Mon, 17 Apr 2023 18:41:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782097; x=1684374097; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=mM3VEDiu9mRhnxPoyHTlcLFFgOhw6pqe5rwY+welbQE=; b=73fduone9MSuyhI/Vj/EBtrUNK1HlKqIXi2R/1WFSMzTZeGfp5VAaZvM2T8Z1A2hT+ TW4GWEQGhkVXdJu5a2/KltZOzXD1AQjbMH7CIR0D5QETP4evRehG6EDqGgw/XSPXtGTD vVM4cpWKlgSrNskNHkKA3nkSoYyOvtjcA/osyf9wix2pqxY+xmbkY9RApyC6c7CrreuR ziFJAw8vrjwNraBMSSyT/8rY6SHmX6pnezYrpgPEU6LWtlkL/qSjF7IjcX7GLUKf/v2+ cH0jszXzfhYMnGCvrW87cOI//DWQdwJdFT43fsUzo5cCWupIGklT1pKXaLA4TRnEWYuZ o5oQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782097; x=1684374097; 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=mM3VEDiu9mRhnxPoyHTlcLFFgOhw6pqe5rwY+welbQE=; b=fD9hEaI5RNIxljMSFNiuXauwKnTIS1sb8QQfZb3oMG3ploQ/WKi4Ab6MynMQvVbAmq dgw+8LSQgSrVo+l2Fxkr617n95rP83/2EMS6SXg1kGNsr8opu7wzDssTtpZ/Er1mqQ1H fXbT6JGIFv2RCzV1V0pX/Vw5PadwQ5c/F2rz6XjTRKjqLBoxWP/NFOSvcUiF4vOyLWtd lHiogKefKL3TYALKUbKcWYc/r486ADnaD41XLBZF7WUT660kjjZ2aDZgoxEd76aoa6km /3UQU8DUJQHf73mRY9yYfzEOwDSOyWBhz5v0uN9iqj3c0+ZTgQNoajZTETWbeNv86Omq IoNw== X-Gm-Message-State: AAQBX9dfFRCsjSlB/xyoAz5UtCjaMKvH5Ox43fzzoVzJq2iLbKrQuQb9 KGadIIMPHkWEPbq48COE10NIrSrEdys= X-Google-Smtp-Source: AKy350Zd0zSOj3A4RVvFzehl/6ZADh+4u0SSYtyXG0ZS+awJjZupIGXSlNBiv6NTnINRlJ+t8jkY64w9PeM= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a25:d24d:0:b0:b90:1777:7194 with SMTP id j74-20020a25d24d000000b00b9017777194mr8137204ybg.6.1681782096895; Mon, 17 Apr 2023 18:41:36 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:20 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-21-drosen@google.com> Subject: [RFC PATCH v3 20/37] fuse-bpf: Add Rename support From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , 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 backing support for FUSE_RENAME and FUSE_RENAME2 Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 250 ++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/dir.c | 7 ++ fs/fuse/fuse_i.h | 18 ++++ 3 files changed, 275 insertions(+) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index 30492f7b2a05..d3a706b55905 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -1747,6 +1747,256 @@ 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_idmap = &nop_mnt_idmap, + .old_dir = d_inode(old_backing_dir_dentry), + .old_dentry = old_backing_dentry, + .new_mnt_idmap = &nop_mnt_idmap, + .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; +} + +struct fuse_rename2_args { + struct fuse_rename2_in in; + struct fuse_buffer old_name; + struct fuse_buffer new_name; +}; + +static int fuse_rename2_initialize_in(struct bpf_fuse_args *fa, struct fuse_rename2_args *args, + struct inode *olddir, struct dentry *oldent, + struct inode *newdir, struct dentry *newent, + unsigned int flags) +{ + *args = (struct fuse_rename2_args) { + .in = (struct fuse_rename2_in) { + .newdir = get_node_id(newdir), + .flags = flags, + }, + .old_name = (struct fuse_buffer) { + .data = (void *) oldent->d_name.name, + .size = oldent->d_name.len + 1, + .flags = BPF_FUSE_IMMUTABLE, + }, + .new_name = (struct fuse_buffer) { + .data = (void *) newent->d_name.name, + .size = newent->d_name.len + 1, + .flags = BPF_FUSE_IMMUTABLE, + }, + + }; + *fa = (struct bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .nodeid = get_node_id(olddir), + .opcode = FUSE_RENAME2, + }, + .in_numargs = 3, + .in_args[0] = (struct bpf_fuse_arg) { + .size = sizeof(args->in), + .value = &args->in, + }, + .in_args[1] = (struct bpf_fuse_arg) { + .is_buffer = true, + .buffer = &args->old_name, + }, + .in_args[2] = (struct bpf_fuse_arg) { + .is_buffer = true, + .buffer = &args->new_name, + }, + }; + + return 0; +} + +static int fuse_rename2_initialize_out(struct bpf_fuse_args *fa, struct fuse_rename2_args *args, + struct inode *olddir, struct dentry *oldent, + struct inode *newdir, struct dentry *newent, + unsigned int flags) +{ + return 0; +} + +static int fuse_rename2_backing(struct bpf_fuse_args *fa, int *out, + struct inode *olddir, struct dentry *oldent, + struct inode *newdir, struct dentry *newent, + unsigned int flags) +{ + const struct fuse_rename2_args *fri = fa->in_args[0].value; + + /* TODO: deal with changing dirs/ents */ + *out = fuse_rename_backing_common(olddir, oldent, newdir, newent, + fri->in.flags); + return *out; +} + +static int fuse_rename2_finalize(struct bpf_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 bpf_fuse_backing(olddir, struct fuse_rename2_args, out, + fuse_rename2_initialize_in, fuse_rename2_initialize_out, + fuse_rename2_backing, fuse_rename2_finalize, + olddir, oldent, newdir, newent, flags); +} + +struct fuse_rename_args { + struct fuse_rename_in in; + struct fuse_buffer old_name; + struct fuse_buffer new_name; +}; + +static int fuse_rename_initialize_in(struct bpf_fuse_args *fa, struct fuse_rename_args *args, + struct inode *olddir, struct dentry *oldent, + struct inode *newdir, struct dentry *newent) +{ + *args = (struct fuse_rename_args) { + .in = (struct fuse_rename_in) { + .newdir = get_node_id(newdir), + }, + .old_name = (struct fuse_buffer) { + .data = (void *) oldent->d_name.name, + .size = oldent->d_name.len + 1, + .flags = BPF_FUSE_IMMUTABLE, + }, + .new_name = (struct fuse_buffer) { + .data = (void *) newent->d_name.name, + .size = newent->d_name.len + 1, + .flags = BPF_FUSE_IMMUTABLE, + }, + + }; + *fa = (struct bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .nodeid = get_node_id(olddir), + .opcode = FUSE_RENAME, + }, + .in_numargs = 3, + .in_args[0] = (struct bpf_fuse_arg) { + .size = sizeof(args->in), + .value = &args->in, + }, + .in_args[1] = (struct bpf_fuse_arg) { + .is_buffer = true, + .buffer = &args->old_name, + }, + .in_args[2] = (struct bpf_fuse_arg) { + .is_buffer = true, + .buffer = &args->new_name, + }, + }; + + return 0; +} + +static int fuse_rename_initialize_out(struct bpf_fuse_args *fa, struct fuse_rename_args *args, + struct inode *olddir, struct dentry *oldent, + struct inode *newdir, struct dentry *newent) +{ + return 0; +} + +static int fuse_rename_backing(struct bpf_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 bpf_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 bpf_fuse_backing(olddir, struct fuse_rename_args, 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 bpf_fuse_args *fa, struct fuse_buffer *name, struct inode *dir, struct dentry *entry) { diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 5ce65f696980..086e3ecada19 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1184,6 +1184,10 @@ static int fuse_rename2(struct mnt_idmap *idmap, 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; @@ -1195,6 +1199,9 @@ static int fuse_rename2(struct mnt_idmap *idmap, 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 e60207bf66de..5c8bd2f76fb9 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1411,6 +1411,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); @@ -1453,6 +1458,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 Apr 18 01:40:21 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214962 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 90350C77B75 for ; Tue, 18 Apr 2023 01:43:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231250AbjDRBnx (ORCPT ); Mon, 17 Apr 2023 21:43:53 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53836 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230369AbjDRBmw (ORCPT ); Mon, 17 Apr 2023 21:42:52 -0400 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 B530861B5 for ; Mon, 17 Apr 2023 18:41:48 -0700 (PDT) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-54f97aac3d0so147132807b3.15 for ; Mon, 17 Apr 2023 18:41:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782099; x=1684374099; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=nG4okeL2v0Jdp53r3S0fEOBeF2fbrMx1qVzxXkIqqTE=; b=u0cBnWjjy4rLWn/KKVJ6YrMH0yDyoE7VqGPJ1ZiIQiauc+J5y96wH58/hT/Tgc/dtZ qAd8rv3qhW+TfRFJg9rTk5e1V98BfhsrmoHYLyb0zIlV624hfurC4g6h1SkmtTdbB341 9N9rKVQFrHEQHE+epE3uDxR+62tw6wmYtb5fVlZg/aQUSKFfRzHj2Vdiu4aT0COlQHJD 68+YCVPzX890hTZGkZW7BXMMybXNvFt12+o4RZx6+p/OOvSLdYn1UPSLG/g5tTN3SCq7 v6msMYUXxk8pTJQWAng43xaxeTq5LLYD7um28T6hLW6CQD3nYt1tEV7tT85OQc7zg2Ss jdJw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782099; x=1684374099; 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=nG4okeL2v0Jdp53r3S0fEOBeF2fbrMx1qVzxXkIqqTE=; b=NWwpteRqzmRF+2lOdkuCyQmo1HRhD8c+qyVZ71PuafXzdpYzZDXvtNk9vorFiYqbQh CJ3+p5s+DqswFFpEE9S8N4jBH6xJ86g72/sID+dgCDvHcJiexRcKPbWgHN2iBHn7X2QH bvWM7kM2JMIe+eBr30E8+9+U4gqUFg4qHdanHHMuc8cxP7gHCfn8UzgcEHQtZ3QmB/Fg xgH2mid10eL0hGEi+ipxj6yHk05mPnxQT9lu27dE5d2a6g0NTusLacZygioebECdZ8Jv XLtu906g3Nt/aNQT0FOdq6gCCwZf44xcKPzvxb+4DLw0T2VveM/J49AxWs9vNkkF1UKD z8rQ== X-Gm-Message-State: AAQBX9dGNxe5PNcomjIRmT44fbRFUCpC2RdZ58eAEdhbg7tKK2GQ0ZLd wgDl/6qxTNobGCZAfLl8n0garWtWEto= X-Google-Smtp-Source: AKy350YhytlOZ02ss3SEwZRBfhwnYIswD0E28QPdA5RI4z8jwAukpSy/1DOKgKx6QO6ewMAWxbSCDtbtTCc= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a81:ae12:0:b0:54c:2409:c306 with SMTP id m18-20020a81ae12000000b0054c2409c306mr10197096ywh.6.1681782098995; Mon, 17 Apr 2023 18:41:38 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:21 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-22-drosen@google.com> Subject: [RFC PATCH v3 21/37] fuse-bpf: Add attr support From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , 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 backing support for FUSE_GETATTR, FUSE_SETATTR, and FUSE_STATFS Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 288 ++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/dir.c | 68 ++--------- fs/fuse/fuse_i.h | 102 ++++++++++++++++ fs/fuse/inode.c | 17 +-- 4 files changed, 405 insertions(+), 70 deletions(-) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index d3a706b55905..6a6130a16d2b 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -2066,6 +2066,294 @@ int fuse_bpf_unlink(int *out, struct inode *dir, struct dentry *entry) dir, entry); } +struct fuse_getattr_args { + struct fuse_getattr_in in; + struct fuse_attr_out out; +}; + +static int fuse_getattr_initialize_in(struct bpf_fuse_args *fa, struct fuse_getattr_args *args, + const struct dentry *entry, struct kstat *stat, + u32 request_mask, unsigned int flags) +{ + args->in = (struct fuse_getattr_in) { + .getattr_flags = flags, + .fh = -1, /* TODO is this OK? */ + }; + + *fa = (struct bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .nodeid = get_node_id(entry->d_inode), + .opcode = FUSE_GETATTR, + }, + .in_numargs = 1, + .in_args[0] = (struct bpf_fuse_arg) { + .size = sizeof(args->in), + .value = &args->in, + }, + }; + + return 0; +} + +static int fuse_getattr_initialize_out(struct bpf_fuse_args *fa, struct fuse_getattr_args *args, + const struct dentry *entry, struct kstat *stat, + u32 request_mask, unsigned int flags) +{ + args->out = (struct fuse_attr_out) { 0 }; + + fa->out_numargs = 1; + fa->out_args[0] = (struct bpf_fuse_arg) { + .size = sizeof(args->out), + .value = &args->out, + }; + + return 0; +} + +static int fuse_getattr_backing(struct bpf_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 bpf_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 bpf_fuse_backing(inode, struct fuse_getattr_args, 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_args { + struct fuse_setattr_in in; + struct fuse_attr_out out; +}; + +static int fuse_setattr_initialize_in(struct bpf_fuse_args *fa, struct fuse_setattr_args *args, + struct dentry *dentry, struct iattr *attr, struct file *file) +{ + struct fuse_conn *fc = get_fuse_conn(dentry->d_inode); + + *args = (struct fuse_setattr_args) { 0 }; + iattr_to_fattr(fc, attr, &args->in, true); + + *fa = (struct bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .opcode = FUSE_SETATTR, + .nodeid = get_node_id(dentry->d_inode), + }, + .in_numargs = 1, + .in_args[0].size = sizeof(args->in), + .in_args[0].value = &args->in, + }; + + return 0; +} + +static int fuse_setattr_initialize_out(struct bpf_fuse_args *fa, struct fuse_setattr_args *args, + struct dentry *dentry, struct iattr *attr, struct file *file) +{ + fa->out_numargs = 1; + fa->out_args[0].size = sizeof(args->out); + fa->out_args[0].value = &args->out; + + return 0; +} + +static int fuse_setattr_backing(struct bpf_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(&nop_mnt_idmap, 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 bpf_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 bpf_fuse_backing(inode, struct fuse_setattr_args, 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 bpf_fuse_args *fa, struct fuse_statfs_out *out, + struct dentry *dentry, struct kstatfs *buf) +{ + *fa = (struct bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .nodeid = get_node_id(d_inode(dentry)), + .opcode = FUSE_STATFS, + }, + }; + + return 0; +} + +static int fuse_statfs_initialize_out(struct bpf_fuse_args *fa, struct fuse_statfs_out *out, + struct dentry *dentry, struct kstatfs *buf) +{ + *out = (struct fuse_statfs_out) { 0 }; + + fa->out_numargs = 1; + fa->out_args[0].size = sizeof(*out); + fa->out_args[0].value = out; + + return 0; +} + +static int fuse_statfs_backing(struct bpf_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 bpf_fuse_args *fa, int *out, + struct dentry *dentry, struct kstatfs *buf) +{ + struct fuse_statfs_out *fso = fa->out_args[0].value; + + if (!fa->info.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 bpf_fuse_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_args { struct fuse_read_in in; struct fuse_read_out out; diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 086e3ecada19..7d589241c9b0 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1236,7 +1236,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; @@ -1313,6 +1313,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) { @@ -1322,6 +1323,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) @@ -1345,7 +1349,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, @@ -1714,58 +1718,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 * @@ -1880,6 +1832,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; @@ -2059,7 +2014,8 @@ static int fuse_setattr(struct mnt_idmap *idmap, 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; @@ -2116,7 +2072,7 @@ static int fuse_getattr(struct mnt_idmap *idmap, 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 5c8bd2f76fb9..17899a1fe885 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1427,6 +1427,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); @@ -1526,6 +1530,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; @@ -1571,6 +1591,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 fd00910f1eb1..3dfb9cfb6e73 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -623,20 +623,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; @@ -650,6 +636,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 Apr 18 01:40:22 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214964 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 412A7C77B7E for ; Tue, 18 Apr 2023 01:44:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229669AbjDRBoF (ORCPT ); Mon, 17 Apr 2023 21:44:05 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54308 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231151AbjDRBmy (ORCPT ); Mon, 17 Apr 2023 21:42:54 -0400 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 49AC56E8E for ; Mon, 17 Apr 2023 18:41:51 -0700 (PDT) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-54f810e01f5so165555917b3.0 for ; Mon, 17 Apr 2023 18:41:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782101; x=1684374101; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=ikqE+zYz1BXZh8mMNk5W0C4vdov8fukuxDv9VBrvDl0=; b=lWajx8mafW1seUIDD7+qIrKfh1VFBzwDUsLiwI++++Oz4bdMluxYPYY5tEsxAWa6CH vxq2L9Ahhr00QlIXhuRa3H0R5r+rycy5muTibHI7wt1orB3xYTatC8P/ujh+GxeJv/Ea /qsU11Dq37CWkGOvJwpF5+roapL50puA2pWR52NQY0wwb8g7QRm+EFltGejT5i4Jt/Y1 OXZxsrsSrppo/t9R3VKyB1ZTuREyblAyqTJ0f7YYvHtuCgBmBUieoHEv00v6QvsgLSJm 90ZuhZjoOrQHXNOmhPGdV9IuQx/Xj2Nnt6uh+MJyIttYHlPKKkqeKwTrP9CBD6HG1qXp E0dw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782101; x=1684374101; 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=ikqE+zYz1BXZh8mMNk5W0C4vdov8fukuxDv9VBrvDl0=; b=BJXXGcVG/sCep603jMHc4hsyYf0wXfJ4+weUh2aDKE3OaZTvq6bm/ZgSmZgr7PJyND LTDQ70dLRIum10fz6NW7XDZVI1eDJiNueUMCP+3iZ421HrN5Hql0N8dCoTbfn54pwnUx T0GzQX19zMn6PqdDvMSxpxt04s1fP6imBrwfXEUpb+AsTPHu/9izoyv4ndfDVBeRm8Oz BiwzklNtAWz9KlBqcZnn/ut78iRTa2jgjJNkHGxun5EElxX9mhjTuSfkP+U5ZgLclbAu 91tcrd/BAXLjLpTCpoDW3YevpTLWRdDUizU/MXclDDuBGMGigjcoth2+4nmw+fbPA0XL UoCQ== X-Gm-Message-State: AAQBX9fS1BAGg2TwnugibboFYGH50HT954mbtzsn3B39kbu5VbqKVrNx UO4X0E9wHt1Mifi+BikNBIts/fHxX2M= X-Google-Smtp-Source: AKy350Y8W7u23ruYQztA6DVfVSc2+c8wyOOiKTHLOnbBM9IdQq81SwSucFeU6gGP5hv1qDXkZmp6krD4aBI= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a81:a807:0:b0:552:f777:88ce with SMTP id f7-20020a81a807000000b00552f77788cemr2101987ywh.3.1681782101478; Mon, 17 Apr 2023 18:41:41 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:22 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-23-drosen@google.com> Subject: [RFC PATCH v3 22/37] fuse-bpf: Add support for FUSE_COPY_FILE_RANGE From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , 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 backing support for FUSE_COPY_FILE_RANGE Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/file.c | 4 +++ fs/fuse/fuse_i.h | 10 ++++++ 3 files changed, 101 insertions(+) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index 6a6130a16d2b..928b24db2303 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -801,6 +801,93 @@ int fuse_bpf_lseek(loff_t *out, struct inode *inode, struct file *file, loff_t o file, offset, whence); } +struct fuse_copy_file_range_args { + struct fuse_copy_file_range_in in; + struct fuse_write_out out; +}; + +static int fuse_copy_file_range_initialize_in(struct bpf_fuse_args *fa, + struct fuse_copy_file_range_args *args, + 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; + + args->in = (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 bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .nodeid = get_node_id(file_in->f_inode), + .opcode = FUSE_COPY_FILE_RANGE, + }, + .in_numargs = 1, + .in_args[0].size = sizeof(args->in), + .in_args[0].value = &args->in, + }; + + return 0; +} + +static int fuse_copy_file_range_initialize_out(struct bpf_fuse_args *fa, + struct fuse_copy_file_range_args *args, + 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(args->out); + fa->out_args[0].value = &args->out; + + return 0; +} + +static int fuse_copy_file_range_backing(struct bpf_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 bpf_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 bpf_fuse_backing(inode, struct fuse_copy_file_range_args, 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 bpf_fuse_args *fa, struct fuse_fsync_in *in, struct file *file, loff_t start, loff_t end, int datasync) { diff --git a/fs/fuse/file.c b/fs/fuse/file.c index a4a0aeb28e4a..8179afe28c6f 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -3199,6 +3199,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 17899a1fe885..74540f308636 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1421,6 +1421,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); @@ -1500,6 +1503,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 Apr 18 01:40:23 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214992 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 76818C77B72 for ; Tue, 18 Apr 2023 01:44:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231278AbjDRBoL (ORCPT ); Mon, 17 Apr 2023 21:44:11 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53738 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230424AbjDRBm4 (ORCPT ); Mon, 17 Apr 2023 21:42:56 -0400 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 A507883CA for ; Mon, 17 Apr 2023 18:41:55 -0700 (PDT) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-54ee12aa4b5so247174607b3.4 for ; Mon, 17 Apr 2023 18:41:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782104; x=1684374104; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=U2ap14iOZpnbsKNo6G93wf0F6NVTN36P+wrV7MWQ5vc=; b=LNB3R+49gmQhyMAJnqmL6KYI/ZCR+tQ/0jba5N0/i2eEk6OmYIYbiuf0Z7V4bUldDj xmAcckNGdRp5IYqNUT8hqhpXD3vdnlrJNfoEzI9hjUpQNlq3IjGbbi4vQ1h9Xgp31A+R 5jbqBakdNiRAQFySaud23Qc2bb0XZCCsv91cuEDtJ0lsyZMTvQItKMyw0e1gbKSpBG7W 2yGprGFBTkg9LHUqY/Yx/LEu6jmWcorOClliwxzsxuu4MLRG7YazaQAJntomIhbXgBUg 9zw6XZJs07Ntzj/gIsAFey2DX1dvCDUPgQXl4rZCW29e5vOds9wU1NhA4cgwPQE9OmSr p62A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782104; x=1684374104; 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=U2ap14iOZpnbsKNo6G93wf0F6NVTN36P+wrV7MWQ5vc=; b=BH9DCmXhOsZ7j+TFxaaNcdmZ6Kz3uHQqqlATJyNrSLT50v2FbbGsn7XbsqGv9kVJQS kPX59E+qwy40XoInZMOtUMHdVHVZPjpJA0qNc+KrRq65zfVscfvB9IBXWkSQ0kiJM5GR LVcz5kp8mIZWcV/tAUId9fNVyWXJN7SUFuf5KeFbVz/OPguovNGVH/zJ68o9pv4bCP9H kNPLtAazEbQNWp4U2uXwmQ6QweARupIiXpEyWAfZTlA6ldsGlSe4vfje5uFtY50XFGW4 MBRve7TrcYlBfEkDpUApORN/m1TeM4Dsrkv5ZoakfT+fidj5ibtkPIr5vSD/1cWII0UR f7Bw== X-Gm-Message-State: AAQBX9e5sB1uTXw8e8gzAHJE5W1ClAOBO9plOpsnysGQyb57b/4Vfrm0 BctBht4ngMkA2MOTqstjcHEAK2xMP40= X-Google-Smtp-Source: AKy350aATwmWG/o9YxDsbVJURffOBFykqGN8a34WfB/p7jnkyKN8nP3tgJdz5JXEk/Bod96TuKRjP9ed7Bo= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a25:d196:0:b0:b8f:6faa:6480 with SMTP id i144-20020a25d196000000b00b8f6faa6480mr8469156ybg.7.1681782103829; Mon, 17 Apr 2023 18:41:43 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:23 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-24-drosen@google.com> Subject: [RFC PATCH v3 23/37] fuse-bpf: Add xattr support From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , 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 support for FUSE_GETXATTR, FUSE_LISTXATTR, FUSE_SETXATTR, and FUSE_REMOVEXATTR Signed-off-by: Daniel Rosenberg Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 349 ++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/fuse_i.h | 30 ++++ fs/fuse/xattr.c | 18 +++ 3 files changed, 397 insertions(+) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index 928b24db2303..eb3eb184c867 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -982,6 +982,355 @@ int fuse_bpf_dir_fsync(int *out, struct inode *inode, struct file *file, loff_t file, start, end, datasync); } +struct fuse_getxattr_args { + struct fuse_getxattr_in in; + struct fuse_buffer name; + struct fuse_buffer value; + struct fuse_getxattr_out out; +}; + +static int fuse_getxattr_initialize_in(struct bpf_fuse_args *fa, + struct fuse_getxattr_args *args, + struct dentry *dentry, const char *name, void *value, + size_t size) +{ + *args = (struct fuse_getxattr_args) { + .in.size = size, + .name = (struct fuse_buffer) { + .data = (void *) name, + .size = strlen(name) + 1, + .max_size = XATTR_NAME_MAX + 1, + .flags = BPF_FUSE_MUST_ALLOCATE | BPF_FUSE_VARIABLE_SIZE, + }, + }; + + *fa = (struct bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .nodeid = get_fuse_inode(dentry->d_inode)->nodeid, + .opcode = FUSE_GETXATTR, + }, + .in_numargs = 2, + .in_args[0] = (struct bpf_fuse_arg) { + .size = sizeof(args->in), + .value = &args->in, + }, + .in_args[1] = (struct bpf_fuse_arg) { + .is_buffer = true, + .buffer = &args->name, + }, + }; + + return 0; +} + +static int fuse_getxattr_initialize_out(struct bpf_fuse_args *fa, + struct fuse_getxattr_args *args, + struct dentry *dentry, const char *name, void *value, + size_t size) +{ + fa->flags = size ? FUSE_BPF_OUT_ARGVAR : 0; + fa->out_numargs = 1; + if (size) { + args->value = (struct fuse_buffer) { + .data = (void *) value, + .size = size, + .alloc_size = size, + .max_size = size, + .flags = BPF_FUSE_VARIABLE_SIZE, + }; + fa->out_args[0].is_buffer = true; + fa->out_args[0].buffer = &args->value; + } else { + fa->out_args[0].size = sizeof(args->out); + fa->out_args[0].value = &args->out; + } + return 0; +} + +static int fuse_getxattr_backing(struct bpf_fuse_args *fa, int *out, + struct dentry *dentry, const char *name, void *value, + size_t size) +{ + ssize_t ret; + + if (fa->in_args[1].buffer->flags & BPF_FUSE_MODIFIED) { + // Ensure bpf provided string is null terminated + char *new_name = fa->in_args[1].buffer->data; + new_name[fa->in_args[1].buffer->size - 1] = 0; + } + ret = vfs_getxattr(&nop_mnt_idmap, + get_fuse_dentry(dentry)->backing_path.dentry, + fa->in_args[1].buffer->data, value, size); + + if (fa->flags & FUSE_BPF_OUT_ARGVAR) + fa->out_args[0].buffer->size = ret; + else + ((struct fuse_getxattr_out *)fa->out_args[0].value)->size = ret; + + return 0; +} + +static int fuse_getxattr_finalize(struct bpf_fuse_args *fa, int *out, + struct dentry *dentry, const char *name, void *value, + size_t size) +{ + struct fuse_getxattr_out *fgo; + + if (fa->flags & FUSE_BPF_OUT_ARGVAR) { + *out = fa->out_args[0].buffer->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 bpf_fuse_backing(inode, struct fuse_getxattr_args, 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 bpf_fuse_args *fa, + struct fuse_getxattr_args *args, + struct dentry *dentry, char *list, size_t size) +{ + *args = (struct fuse_getxattr_args) { + .in.size = size, + }; + + *fa = (struct bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .nodeid = get_fuse_inode(dentry->d_inode)->nodeid, + .opcode = FUSE_LISTXATTR, + }, + .in_numargs = 1, + .in_args[0] = + (struct bpf_fuse_arg) { + .size = sizeof(args->in), + .value = &args->in, + }, + }; + + return 0; +} + +static int fuse_listxattr_initialize_out(struct bpf_fuse_args *fa, + struct fuse_getxattr_args *args, + struct dentry *dentry, char *list, size_t size) +{ + fa->out_numargs = 1; + + if (size) { + args->value = (struct fuse_buffer) { + .data = list, + .size = size, + .alloc_size = size, + .max_size = size, + .flags = BPF_FUSE_VARIABLE_SIZE, + }; + fa->flags = FUSE_BPF_OUT_ARGVAR; + fa->out_args[0].is_buffer = true; + fa->out_args[0].buffer = &args->value; + } else { + fa->out_args[0].size = sizeof(args->out); + fa->out_args[0].value = &args->out; + } + return 0; +} + +static int fuse_listxattr_backing(struct bpf_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->flags & FUSE_BPF_OUT_ARGVAR) + fa->out_args[0].buffer->size = *out; + else + ((struct fuse_getxattr_out *)fa->out_args[0].value)->size = *out; + + return 0; +} + +static int fuse_listxattr_finalize(struct bpf_fuse_args *fa, ssize_t *out, struct dentry *dentry, + char *list, size_t size) +{ + struct fuse_getxattr_out *fgo; + + if (fa->info.error_in) + return 0; + + if (fa->flags & FUSE_BPF_OUT_ARGVAR) { + *out = fa->out_args[0].buffer->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 bpf_fuse_backing(inode, struct fuse_getxattr_args, out, + fuse_listxattr_initialize_in, fuse_listxattr_initialize_out, + fuse_listxattr_backing, fuse_listxattr_finalize, + dentry, list, size); +} + +struct fuse_setxattr_args { + struct fuse_setxattr_in in; + struct fuse_buffer name; + struct fuse_buffer value; +}; + +static int fuse_setxattr_initialize_in(struct bpf_fuse_args *fa, + struct fuse_setxattr_args *args, + struct dentry *dentry, const char *name, + const void *value, size_t size, int flags) +{ + *args = (struct fuse_setxattr_args) { + .in = (struct fuse_setxattr_in) { + .size = size, + .flags = flags, + }, + .name = (struct fuse_buffer) { + .data = (void *) name, + .size = strlen(name) + 1, + .max_size = XATTR_NAME_MAX + 1, + .flags = BPF_FUSE_VARIABLE_SIZE | BPF_FUSE_MUST_ALLOCATE, + }, + .value =(struct fuse_buffer) { + .data = (void *) value, + .size = size, + .max_size = XATTR_SIZE_MAX, + .flags = BPF_FUSE_VARIABLE_SIZE | BPF_FUSE_MUST_ALLOCATE, + }, + }; + + *fa = (struct bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .nodeid = get_fuse_inode(dentry->d_inode)->nodeid, + .opcode = FUSE_SETXATTR, + }, + .in_numargs = 3, + .in_args[0] = (struct bpf_fuse_arg) { + .size = sizeof(args->in), + .value = &args->in, + }, + .in_args[1] = (struct bpf_fuse_arg) { + .is_buffer = true, + .buffer = &args->name, + }, + .in_args[2] = (struct bpf_fuse_arg) { + .is_buffer = true, + .buffer = &args->value, + }, + }; + + return 0; +} + +static int fuse_setxattr_initialize_out(struct bpf_fuse_args *fa, + struct fuse_setxattr_args *args, + struct dentry *dentry, const char *name, + const void *value, size_t size, int flags) +{ + return 0; +} + +static int fuse_setxattr_backing(struct bpf_fuse_args *fa, int *out, struct dentry *dentry, + const char *name, const void *value, size_t size, + int flags) +{ + // TODO Ensure we actually use filter values + *out = vfs_setxattr(&nop_mnt_idmap, + get_fuse_dentry(dentry)->backing_path.dentry, name, + value, size, flags); + return 0; +} + +static int fuse_setxattr_finalize(struct bpf_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 bpf_fuse_backing(inode, struct fuse_setxattr_args, 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 bpf_fuse_args *fa, + struct fuse_buffer *in, + struct dentry *dentry, const char *name) +{ + *in = (struct fuse_buffer) { + .data = (void *) name, + .size = strlen(name) + 1, + .max_size = XATTR_NAME_MAX + 1, + .flags = BPF_FUSE_VARIABLE_SIZE | BPF_FUSE_MUST_ALLOCATE, + }; + *fa = (struct bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .nodeid = get_fuse_inode(dentry->d_inode)->nodeid, + .opcode = FUSE_REMOVEXATTR, + }, + .in_numargs = 1, + .in_args[0] = (struct bpf_fuse_arg) { + .is_buffer = true, + .buffer = in, + }, + }; + + return 0; +} + +static int fuse_removexattr_initialize_out(struct bpf_fuse_args *fa, + struct fuse_buffer *in, + struct dentry *dentry, const char *name) +{ + return 0; +} + +static int fuse_removexattr_backing(struct bpf_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(&nop_mnt_idmap, backing_path->dentry, name); + return 0; +} + +static int fuse_removexattr_finalize(struct bpf_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 bpf_fuse_backing(inode, struct fuse_buffer, 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 74540f308636..243a8fe0c343 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1426,6 +1426,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); @@ -1520,6 +1527,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 49c01559580f..d00f7dc50038 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 Apr 18 01:40:24 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214993 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 C9EA7C77B72 for ; Tue, 18 Apr 2023 01:44:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231176AbjDRBo2 (ORCPT ); Mon, 17 Apr 2023 21:44:28 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54462 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229977AbjDRBnP (ORCPT ); Mon, 17 Apr 2023 21:43:15 -0400 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 DCF2B6E96 for ; Mon, 17 Apr 2023 18:42:04 -0700 (PDT) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-54feaa94819so79486987b3.2 for ; Mon, 17 Apr 2023 18:42:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782106; x=1684374106; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=nvRLddSkeIm9PecIuzQsl89859K3ZUpPQfxG17id1Mk=; b=XlxFgRg0kxYxSjyZXrqofVDZz+iSvTOODqUtf12czQ/mSsTbgNfeu3peW779Uw8Dc2 18GKMXLuUG+C+s8RgVUM+pHcTAB4mu2P0Oa2/5s3x2TJHxPzoaNL0gqI9Ve2X2bcARlj 8OtlZ4AlEhFGz0C72gRftnGxUtBvNhSxFRzbv5iJaoXCIKt7BX4WeruaHXiCykenQ9ZP 0MXj57A6OYaCu2AkWRsuzJcjqTUNLUf1dqEO2GjZ6bkHl298GXkexM1oElOZAKVXcdsA COWy7z5p3ou2wEN10bVz4/gExra0QrVpaW+GfbTKy+ZCyMZynWOs0Tp9Dtm3XdypbrDI YBXw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782106; x=1684374106; 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=nvRLddSkeIm9PecIuzQsl89859K3ZUpPQfxG17id1Mk=; b=hsTAQDPjs8UBowdC5n/vgeWbCf70zA61DozoQv+B0b1m+rosDF3FSoYbh4BfmdlG1F CFgFpuUEADGu8rr2ZPV9ASP2dhnXnhhhcb+x3ibDvWrmmPyp+poj3bB61vxkqU9dgmB/ geiAY5zpotb4ReASinnY3k1oUEiHzjMwzQ9eoUynGsbYJ2tBdyzWzdSHyXpJBgbG8/NL a0nXNUUNe0vZIiNn+WnayeF5WHYsRccnZj8tcXD8zvStLwT3BJJDPZtQvrh5yJjS8/C+ fnUTEIop4gOI7x1J8HeR+6T0AJb0AWISe7wzIRfeCQJY9Y71LTUvfnHHCh6BvI2t2yA6 p3eg== X-Gm-Message-State: AAQBX9fex636obd8ebMFN6nkXY1ufO74ncMynHDoF0bsn4+k4iJ7F7hR gDyayiUs/MKF79/Gzr+oPcsoN/jjORM= X-Google-Smtp-Source: AKy350bnnjuHdh4NImroO7h36oSrvoNCrPKP80I7mzBIz856gdltQzBiy3nX4BcegzARjsg+1e/Y1F6L+7o= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a25:cc05:0:b0:b8b:eea7:525a with SMTP id l5-20020a25cc05000000b00b8beea7525amr8483126ybf.7.1681782106223; Mon, 17 Apr 2023 18:41:46 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:24 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-25-drosen@google.com> Subject: [RFC PATCH v3 24/37] fuse-bpf: Add symlink/link support From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , 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 backing support for FUSE_LINK, FUSE_READLINK, and FUSE_SYMLINK Signed-off-by: Daniel Rosenberg --- fs/fuse/backing.c | 327 ++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/dir.c | 11 ++ fs/fuse/fuse_i.h | 20 +++ 3 files changed, 358 insertions(+) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index eb3eb184c867..e807ae4f6f53 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -2502,6 +2502,125 @@ int fuse_bpf_unlink(int *out, struct inode *dir, struct dentry *entry) dir, entry); } +struct fuse_link_args { + struct fuse_link_in in; + struct fuse_buffer name; +}; + +static int fuse_link_initialize_in(struct bpf_fuse_args *fa, struct fuse_link_args *args, + struct dentry *entry, struct inode *dir, + struct dentry *newent) +{ + struct inode *src_inode = entry->d_inode; + + *args = (struct fuse_link_args) { + .in = (struct fuse_link_in) { + .oldnodeid = get_node_id(src_inode), + }, + .name = (struct fuse_buffer) { + .data = (void *) newent->d_name.name, + .size = newent->d_name.len + 1, + .max_size = NAME_MAX + 1, + .flags = BPF_FUSE_VARIABLE_SIZE | BPF_FUSE_MUST_ALLOCATE, + }, + }; + + *fa = (struct bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .opcode = FUSE_LINK, + }, + .in_numargs = 2, + .in_args[0] = (struct bpf_fuse_arg) { + .size = sizeof(args->in), + .value = &args->in, + }, + .in_args[1] = (struct bpf_fuse_arg) { + .is_buffer = true, + .buffer = &args->name, + }, + }; + + return 0; +} + +static int fuse_link_initialize_out(struct bpf_fuse_args *fa, struct fuse_link_args *args, + struct dentry *entry, struct inode *dir, + struct dentry *newent) +{ + return 0; +} + +static int fuse_link_backing(struct bpf_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, &nop_mnt_idmap, + 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 bpf_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 bpf_fuse_backing(inode, struct fuse_link_args, out, + fuse_link_initialize_in, fuse_link_initialize_out, + fuse_link_backing, fuse_link_finalize, + entry, newdir, newent); +} + struct fuse_getattr_args { struct fuse_getattr_in in; struct fuse_attr_out out; @@ -2790,6 +2909,214 @@ int fuse_bpf_statfs(int *out, struct inode *inode, struct dentry *dentry, struct dentry, buf); } +struct fuse_get_link_args { + struct fuse_buffer name; + struct fuse_buffer path; +}; + +static int fuse_get_link_initialize_in(struct bpf_fuse_args *fa, struct fuse_get_link_args *args, + 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 + */ + *args = (struct fuse_get_link_args) { + .name = (struct fuse_buffer) { + .data = (void *) dentry->d_name.name, + .size = dentry->d_name.len + 1, + .max_size = NAME_MAX + 1, + .flags = BPF_FUSE_VARIABLE_SIZE | BPF_FUSE_MUST_ALLOCATE, + }, + }; + + *fa = (struct bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .opcode = FUSE_READLINK, + .nodeid = get_node_id(inode), + }, + .in_numargs = 1, + .in_args[0] = (struct bpf_fuse_arg) { + .is_buffer = true, + .buffer = &args->name, + }, + }; + + return 0; +} + +static int fuse_get_link_initialize_out(struct bpf_fuse_args *fa, struct fuse_get_link_args *args, + struct inode *inode, struct dentry *dentry, + struct delayed_call *callback) +{ + // TODO +#if 0 + args->path = (struct fuse_buffer) { + .data = NULL, + .size = 0, + .max_size = PATH_MAX, + .flags = BPF_FUSE_VARIABLE_SIZE | BPF_FUSE_MUST_ALLOCATE, + }; + fa->out_numargs = 1; + fa->out_args[0].is_buffer = true; + fa->out_args[0].buffer = &args->path; +#endif + + return 0; +} + +static int fuse_get_link_backing(struct bpf_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 bpf_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 bpf_fuse_backing(inode, struct fuse_get_link_args, out, + fuse_get_link_initialize_in, fuse_get_link_initialize_out, + fuse_get_link_backing, fuse_get_link_finalize, + inode, dentry, callback); +} + +struct fuse_symlink_args { + struct fuse_buffer name; + struct fuse_buffer path; +}; + +static int fuse_symlink_initialize_in(struct bpf_fuse_args *fa, struct fuse_symlink_args *args, + struct inode *dir, struct dentry *entry, const char *link, int len) +{ + *args = (struct fuse_symlink_args) { + .name = (struct fuse_buffer) { + .data = (void *) entry->d_name.name, + .size = entry->d_name.len + 1, + .flags = BPF_FUSE_IMMUTABLE, + }, + .path = (struct fuse_buffer) { + .data = (void *) link, + .size = len, + .max_size = PATH_MAX, + .flags = BPF_FUSE_VARIABLE_SIZE | BPF_FUSE_MUST_ALLOCATE, + }, + }; + *fa = (struct bpf_fuse_args) { + .info = (struct bpf_fuse_meta_info) { + .nodeid = get_node_id(dir), + .opcode = FUSE_SYMLINK, + }, + .in_numargs = 2, + .in_args[0] = (struct bpf_fuse_arg) { + .is_buffer = true, + .buffer = &args->name, + }, + .in_args[1] = (struct bpf_fuse_arg) { + .is_buffer = true, + .buffer = &args->path, + }, + }; + + return 0; +} + +static int fuse_symlink_initialize_out(struct bpf_fuse_args *fa, struct fuse_symlink_args *args, + struct inode *dir, struct dentry *entry, const char *link, int len) +{ + return 0; +} + +static int fuse_symlink_backing(struct bpf_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(&nop_mnt_idmap, 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 bpf_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 bpf_fuse_backing(dir, struct fuse_symlink_args, out, + fuse_symlink_initialize_in, fuse_symlink_initialize_out, + fuse_symlink_backing, fuse_symlink_finalize, + dir, entry, link, len); +} + struct fuse_read_args { struct fuse_read_in in; struct fuse_read_out out; diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 7d589241c9b0..d1c3b2bfb0b1 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1013,6 +1013,10 @@ static int fuse_symlink(struct mnt_idmap *idmap, 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; @@ -1219,6 +1223,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; @@ -1618,12 +1625,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 243a8fe0c343..121d31a04e79 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1417,6 +1417,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); @@ -1441,6 +1442,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); @@ -1490,6 +1494,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; @@ -1586,6 +1595,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 Apr 18 01:40:25 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214995 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 DF8C8C77B7C for ; Tue, 18 Apr 2023 01:44:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230135AbjDRBo3 (ORCPT ); Mon, 17 Apr 2023 21:44:29 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53880 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230432AbjDRBnT (ORCPT ); Mon, 17 Apr 2023 21:43:19 -0400 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 581046591 for ; Mon, 17 Apr 2023 18:42:06 -0700 (PDT) Received: by mail-yb1-xb4a.google.com with SMTP id c64-20020a254e43000000b00b92530ded91so3263382ybb.17 for ; Mon, 17 Apr 2023 18:42:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782108; x=1684374108; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=f+tC+6XFyVz5iTVVtehlShhqqyPSU3xUJhrgatVmT44=; b=rIbd7s/2ek5G8EhMwiYEWTKGjSzZ7qOMthCTcIGG3nURoO/goLZ/THknkdr/Day049 NOH2ThvnPaKS985+9T2Fj65xFkSb8mN7EIMqZpgOoWSQGsC7+K9Lo5IZgHyPfy0r15ie 76fB2pTkT8+QMtmwqL4sijLUqof+ZN+hn8E6tAbc46UFjpy1TcWRQsd5ACEpzyMLSmW0 74VzZ/vHRFvKlgEgwp6sWLYGEao1V0rHzw1TwBzEvw3oDwOtehIqQJ89/IY1OKVdN93F kJFpSyE1Czbvvz7vfD2Xz00Ts/BoJbP3VVBKgAUxrEePjcGseWyzi6UVxppwiAp8z3XC uLNw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782108; x=1684374108; 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=f+tC+6XFyVz5iTVVtehlShhqqyPSU3xUJhrgatVmT44=; b=F3bNChGWDBVU58W0JIAoLTX5KrUk+QGukjP/zl8sZGxemvDt2y4HyB4J0OE+DgFSDA kAqMBP9GABv0h1uTyQf+E6/5J8OJrpenA1BkpVhOQ6uYBZBWVOeSzAWlxV2nCf2tS4Eb J2QPA719tYsFzDCvlgdBoYDdHBf3/rLMpcs6vHzFNAs4GeLq2TI0k2actIqduCkbBscL UT9I0v8x85zzXQPH5BYvuy66B3Dk+FpWSV2ypfoy+rrFFz9OpRev5UVsDYKEnauxEcsl umiaC/gpWg2CCf0XK5YK2G5w1ID7mo1xogsf3ocZX88RIuiMDrdAwjZlXgp6GuCpBpfx 1Blw== X-Gm-Message-State: AAQBX9evttn+ZF+PdXIQ3b6wlf4b8fTP/OdqFrcxRjt6aqFAVAay2uHy pSN/+xrdHD51PNfnwS23AIPM81awn/0= X-Google-Smtp-Source: AKy350az5srFMYJyfvvtfh6sqad66/N+PonFGFWZFCufX/5Wu37wcKRBKPvzh2skEJqf7c2CQLiJDByUTVw= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a81:ad0e:0:b0:545:6106:5334 with SMTP id l14-20020a81ad0e000000b0054561065334mr10694952ywh.8.1681782108243; Mon, 17 Apr 2023 18:41:48 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:25 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-26-drosen@google.com> Subject: [RFC PATCH v3 25/37] fuse-bpf: allow mounting with no userspace daemon From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , 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 121d31a04e79..2bd45c8658e8 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -566,6 +566,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; @@ -847,6 +848,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 3dfb9cfb6e73..31f34962bc9b 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -756,6 +756,7 @@ enum { OPT_MAX_READ, OPT_BLKSIZE, OPT_ROOT_DIR, + OPT_NO_DAEMON, OPT_ERR }; @@ -771,6 +772,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), {} }; @@ -860,6 +862,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; } @@ -1419,7 +1426,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); @@ -1694,6 +1701,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); @@ -1740,7 +1748,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; @@ -1748,10 +1756,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) @@ -1801,6 +1811,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); From patchwork Tue Apr 18 01:40:26 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214963 X-Patchwork-Delegate: bpf@iogearbox.net 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 B3BDFC77B72 for ; Tue, 18 Apr 2023 01:43:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231251AbjDRBnz (ORCPT ); Mon, 17 Apr 2023 21:43:55 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54300 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230384AbjDRBmw (ORCPT ); Mon, 17 Apr 2023 21:42:52 -0400 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 7A6B765B9 for ; Mon, 17 Apr 2023 18:41:50 -0700 (PDT) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-54be7584b28so368343317b3.16 for ; Mon, 17 Apr 2023 18:41:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782110; x=1684374110; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=zWwdeYSbe2MZzb6+0YuKWkWzO0OIunr3DIdjLXb+QPs=; b=bvVRAayaKb3f4XHecCOw1fq1zrxbTJlfu+X0j4JJ+ODZZ8s343yV2zF5V5cJPVfBrx vO1QktBuUoSHL08PHmb741zrbYTtLR44mlVkerlNbRbCJGYvY5rfLmHur4kpmRZXckp/ 5Xt5y4R3xULWEw01wuUy+z/jQtalviK0udMiGXZVR4o34gUJMO46tjSd9TtEsLa3oC0C Qs9ggxL4witMvtnXFhB17F+6dIIHcCSX5M76pppw2FB4zNzSuRHvs30bGFNla01g3E11 K2LsOQQ7l/LXtTgEgSoMdbEAFkvDtok6PTyc3QsDYVhsfSDu4Pfk86mtQUxZrOSNJ97d ihKA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782110; x=1684374110; 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=zWwdeYSbe2MZzb6+0YuKWkWzO0OIunr3DIdjLXb+QPs=; b=DOVvOGD2EojdeeOepggwaLad1ItqjxXVZk6Ztamyb9ONdUZmZa5tq4JYAgTB0IC7az vT5txxgazaEpnS92g83e0BKUc8LLzHzXKw5pgQc83Lr4vUBt6UPh9+U5Y4+6lwEoEnIH 3d9fUOqkvnqCU8PaxbF9fzTJY1/RZ4C5Bm9/JO+/VCMbdf+XtlYCYKmCtTOiig3subkI jDiuoZ9YJN1QdPxKeZAKcsHM6LBUlY66O7EP+nfawIZnXiaTkQLC9hUc6hhm1uFRjZmY 5l1rWhLRMx1fMBpQFConvjFd3ItlHP63RqTI6i6sKjADQIxrQGOx63iv/yPY+Vjn3l0i jYdA== X-Gm-Message-State: AAQBX9eBqA+VaAHhZbkJes5rD/0Xd3GgP6PhwFmv0mCniikGkCaeq5Uq zq3XR1r82ucCw8kcS9xFpQ0KpzkmzJo= X-Google-Smtp-Source: AKy350aIiy0yEVjIhRUwhKZEZY7qdBZhQoYfVQEj5j3ErV9/GTclD1JkHLmadrVOBoXwbliIvZF/mWfRLJ4= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a0d:ec02:0:b0:54c:2723:560d with SMTP id q2-20020a0dec02000000b0054c2723560dmr10855123ywn.3.1681782110238; Mon, 17 Apr 2023 18:41:50 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:26 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-27-drosen@google.com> Subject: [RFC PATCH v3 26/37] bpf: Increase struct_op limits From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , kernel-team@android.com, Daniel Rosenberg Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net X-Patchwork-State: RFC Fuse bpf goes a bit past the '64' limit here, although in reality, this limit seems to be more like 37. After 37, we start overrunning the safety checks while setting up the trampoline. This simply doubles some of these values. This will have the same issue, as we'll run out of space way before hitting the 128 limit, but for now that unblocks fuse-bpf. Signed-off-by: Daniel Rosenberg --- include/linux/bpf.h | 2 +- kernel/bpf/bpf_struct_ops.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 18b592fde896..c006f823e634 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -1537,7 +1537,7 @@ struct bpf_link_primer { struct bpf_struct_ops_value; struct btf_member; -#define BPF_STRUCT_OPS_MAX_NR_MEMBERS 64 +#define BPF_STRUCT_OPS_MAX_NR_MEMBERS 128 struct bpf_struct_ops { const struct bpf_verifier_ops *verifier_ops; int (*init)(struct btf *btf); diff --git a/kernel/bpf/bpf_struct_ops.c b/kernel/bpf/bpf_struct_ops.c index d3f0a4825fa6..deb9eecaf1e4 100644 --- a/kernel/bpf/bpf_struct_ops.c +++ b/kernel/bpf/bpf_struct_ops.c @@ -417,7 +417,7 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key, udata = &uvalue->data; kdata = &kvalue->data; image = st_map->image; - image_end = st_map->image + PAGE_SIZE; + image_end = st_map->image + 2 * PAGE_SIZE; for_each_member(i, t, member) { const struct btf_type *mtype, *ptype; @@ -688,7 +688,7 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr) st_map->links = bpf_map_area_alloc(btf_type_vlen(t) * sizeof(struct bpf_links *), NUMA_NO_NODE); - st_map->image = bpf_jit_alloc_exec(PAGE_SIZE); + st_map->image = bpf_jit_alloc_exec(2 * PAGE_SIZE); if (!st_map->uvalue || !st_map->links || !st_map->image) { __bpf_struct_ops_map_free(map); return ERR_PTR(-ENOMEM); From patchwork Tue Apr 18 01:40:27 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214996 X-Patchwork-Delegate: bpf@iogearbox.net 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 D6B53C77B72 for ; Tue, 18 Apr 2023 01:44:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230064AbjDRBoc (ORCPT ); Mon, 17 Apr 2023 21:44:32 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54170 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231205AbjDRBnb (ORCPT ); Mon, 17 Apr 2023 21:43:31 -0400 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 1EABD4218 for ; Mon, 17 Apr 2023 18:42:13 -0700 (PDT) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-54be7584b28so368343747b3.16 for ; Mon, 17 Apr 2023 18:42:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782112; x=1684374112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=tnG7XRDx2oVnxRfx5yqwQBjNsF82L1FllZqCFSzhSvk=; b=SkI3OL7Xwcb2D8buNw1xsFgokzhAAQBXqENLcRBKEhMxZBceAdT76zRqhUKaAlZRib aI6PlfHc5n4ddtN3+6mjTW4Cs8BBdyXGjyExSVPyeQf5XRY0Liaw+CyvPQArNpVYYnib TGm738pcBWjnR5XstajeRfCSTCqIp9dGcgPyzLhYYauS/Ba9DT/7Oo4iwDdfQ5MeXhvv m9omEapvVlOwz2XNsPXLPYl6qDc4wN5t+WJMQ3+JHgRWz6iur0twsD7zOIFKKeIe51D7 dw3h5jHufm+Ui9oRM1TAW/H897y1hff2PlchumkhwBxLImVetPWMILw+PxvkA+8WTkDa ztcA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782112; x=1684374112; 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=tnG7XRDx2oVnxRfx5yqwQBjNsF82L1FllZqCFSzhSvk=; b=lChPUIv3ygbGOxG9zoWFbWuz/P8BTE5UfUBUqOoUe3y+VhPwdfs+YPPIU8C8uejLV0 tl5JPgJ+9vhByGzC8aouHLSC0jUQagd6gAdAEtYt+ixgVp7p+2QJYrfNKyUaR1ODW+fj OJDMwlyiNtjBGiOqGV2GORRk0RniTpMH5zzPquLjc8Q+82uKN7mSXpsuToLHQZKn45KA mPKl2I1fbUNGyijOhHwWCKFVBLCirexlbBAt0r0iIUYKdc6HZYqdsHz6dTOXmYIMjtDP tb9VdRNsLT5n9uH8ig4hLDeGLPzjvA5BhfjA3plzD1r2YQwfQF/0zzCRmyEO9KUws1tq NCgg== X-Gm-Message-State: AAQBX9fFSAQvFhVVdxdSlN4AzYj4ujJBQxZz8zILgH5FAvFGysIhv5cg lRrISJpNcWngaP5TTrrcko5HCslvoYQ= X-Google-Smtp-Source: AKy350b7LZaOW7Db/+iu/rILXEm8KL2d9x1YzXYuCbML8M1OSVxY74Uh31fhIpMfF3C/uFjNLKcy7inXoIs= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a81:e503:0:b0:54f:40fe:10cc with SMTP id s3-20020a81e503000000b0054f40fe10ccmr10636927ywl.9.1681782112257; Mon, 17 Apr 2023 18:41:52 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:27 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-28-drosen@google.com> Subject: [RFC PATCH v3 27/37] fuse-bpf: Add fuse-bpf constants From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , kernel-team@android.com, Daniel Rosenberg Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net X-Patchwork-State: RFC This adds constants that fuse_op programs will rely on for communicating what action fuse should take next. Signed-off-by: Daniel Rosenberg --- include/uapi/linux/bpf.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 4b20a7269bee..6521c40875c7 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -7155,4 +7155,16 @@ struct bpf_iter_num { __u64 __opaque[1]; } __attribute__((aligned(8))); +/* Return Codes for Fuse BPF struct_op programs */ +#define BPF_FUSE_CONTINUE 0 +#define BPF_FUSE_USER 1 +#define BPF_FUSE_USER_PREFILTER 2 +#define BPF_FUSE_POSTFILTER 3 +#define BPF_FUSE_USER_POSTFILTER 4 + +/* Op Code Filter values for BPF Programs */ +#define FUSE_OPCODE_FILTER 0x0ffff +#define FUSE_PREFILTER 0x10000 +#define FUSE_POSTFILTER 0x20000 + #endif /* _UAPI__LINUX_BPF_H__ */ From patchwork Tue Apr 18 01:40:28 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214998 X-Patchwork-Delegate: bpf@iogearbox.net 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 86E39C77B75 for ; Tue, 18 Apr 2023 01:45:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231148AbjDRBpK (ORCPT ); Mon, 17 Apr 2023 21:45:10 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54306 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230393AbjDRBoE (ORCPT ); Mon, 17 Apr 2023 21:44:04 -0400 Received: from mail-pg1-x54a.google.com (mail-pg1-x54a.google.com [IPv6:2607:f8b0:4864:20::54a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C0CEC8A72 for ; Mon, 17 Apr 2023 18:42:21 -0700 (PDT) Received: by mail-pg1-x54a.google.com with SMTP id 41be03b00d2f7-517a6547b0aso1595624a12.1 for ; Mon, 17 Apr 2023 18:42:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782114; x=1684374114; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=uItCp9dmlSq9K5w/dO5KDhSfWqlCiTpddbyFnUdvY1o=; b=6ZG7MrPplM5M81seOPN0hvXOXCiSEwqx1nO2/dhdDCrhZSmG9COpDr1eX2fxHouiFO e/p0ot7SOR2DqTtejD+ao/RKa21LSQkoUotrF6i4BLWIuTlGH9VzaoyiD599p5QSXw2F bQTC68wutfGQQ0eI74oDjeJVg0AEAToVsf2UFB6NyFGzhg8MljgXOsKeuihOuHCSVDsw XreW21ONO4vhlYTSSmXeQYqGb7nwbDqxO4UByZJQczd+uVUQorJ83f6LfpPcZj+7/PMk 2rvlYT4gUyoofHRhNBzeNxQMYk2VT6VUhLo0ZnNohLDKM637Qfr2oAIbXfmiz0imCiDA IFsw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782114; x=1684374114; 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=uItCp9dmlSq9K5w/dO5KDhSfWqlCiTpddbyFnUdvY1o=; b=KILAvu9edkQfrp4CL1EZZpvO13EBOGuxfczwI6vtX56fYCoQFVovNC3O46yIl91Cox 1dOwDWOeSJ9YFnc7agCC6ugMNtf//sg3BBkQKfXmFYWz1fHw+u6fqXa+U6uoCNiibJGu uf8oGuiDC3CkWZrxIatCKwppohAcmT+szorgKF3AexiLzZno1WoFuPqnJNs2OYEMGJSd 2DuIP4jAJpvkH8kn9xSdAtswFUn6JKKlU3frt89Yloxg++opH6nGafCYIzv3iKqoaxYZ OOzQ4WvPNFDR/WL/G9Jdas5ZNJWL143217Rl/lTnl8xpdNROL0SdNFVpjemOoh4Td+Tp ERnQ== X-Gm-Message-State: AAQBX9dooH8X2zXA8GHHJ822LY6i0ggswMRJiHMj598hDBmPjjAFGGEk T+DQqUmJMU2JehH/3If2BmWhRBqUSJk= X-Google-Smtp-Source: AKy350ZppNJyCCMUoSVMCNcn1UBo1hOQckQ73RYyKMhi9AzOeJREQF2N4JedMcWfQRQtWg8BteVAlnRCSrQ= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a17:903:294c:b0:1a6:898a:41fd with SMTP id li12-20020a170903294c00b001a6898a41fdmr201053plb.6.1681782114402; Mon, 17 Apr 2023 18:41:54 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:28 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-29-drosen@google.com> Subject: [RFC PATCH v3 28/37] WIP: bpf: Add fuse_ops struct_op programs From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , kernel-team@android.com, Daniel Rosenberg Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net X-Patchwork-State: RFC This introduces a new struct_op type: fuse_ops. This program set provides pre and post filters to run around fuse-bpf calls that act directly on the lower filesystem. The inputs are either fixed structures, or struct fuse_buffer's. These programs are not permitted to make any changes to these fuse_buffers unless they create a dynptr wrapper using the supplied kfunc helpers. Fuse_buffers maintain additional state information that FUSE uses to manage memory and determine if additional set up or checks are needed. Signed-off-by: Daniel Rosenberg --- include/linux/bpf_fuse.h | 189 +++++++++++++++++++++++ kernel/bpf/Makefile | 4 + kernel/bpf/bpf_fuse.c | 241 ++++++++++++++++++++++++++++++ kernel/bpf/bpf_struct_ops_types.h | 4 + kernel/bpf/btf.c | 1 + kernel/bpf/verifier.c | 9 ++ 6 files changed, 448 insertions(+) create mode 100644 kernel/bpf/bpf_fuse.c diff --git a/include/linux/bpf_fuse.h b/include/linux/bpf_fuse.h index ce8b1b347496..780a7889aea2 100644 --- a/include/linux/bpf_fuse.h +++ b/include/linux/bpf_fuse.h @@ -30,6 +30,8 @@ struct fuse_buffer { #define BPF_FUSE_MODIFIED (1 << 3) // The helper function allowed writes to the buffer #define BPF_FUSE_ALLOCATED (1 << 4) // The helper function allocated the buffer +extern void *bpf_fuse_get_writeable(struct fuse_buffer *arg, u64 size, bool copy); + /* * BPF Fuse Args * @@ -81,4 +83,191 @@ static inline unsigned bpf_fuse_arg_size(const struct bpf_fuse_arg *arg) return arg->is_buffer ? arg->buffer->size : arg->size; } +struct fuse_ops { + uint32_t (*open_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_open_in *in); + uint32_t (*open_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_open_in *in, + struct fuse_open_out *out); + + uint32_t (*opendir_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_open_in *in); + uint32_t (*opendir_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_open_in *in, + struct fuse_open_out *out); + + uint32_t (*create_open_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_create_in *in, struct fuse_buffer *name); + uint32_t (*create_open_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_create_in *in, const struct fuse_buffer *name, + struct fuse_entry_out *entry_out, struct fuse_open_out *out); + + uint32_t (*release_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_release_in *in); + uint32_t (*release_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_release_in *in); + + uint32_t (*releasedir_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_release_in *in); + uint32_t (*releasedir_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_release_in *in); + + uint32_t (*flush_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_flush_in *in); + uint32_t (*flush_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_flush_in *in); + + uint32_t (*lseek_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_lseek_in *in); + uint32_t (*lseek_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_lseek_in *in, + struct fuse_lseek_out *out); + + uint32_t (*copy_file_range_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_copy_file_range_in *in); + uint32_t (*copy_file_range_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_copy_file_range_in *in, + struct fuse_write_out *out); + + uint32_t (*fsync_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_fsync_in *in); + uint32_t (*fsync_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_fsync_in *in); + + uint32_t (*dir_fsync_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_fsync_in *in); + uint32_t (*dir_fsync_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_fsync_in *in); + + uint32_t (*getxattr_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_getxattr_in *in, struct fuse_buffer *name); + // if in->size > 0, use value. If in->size == 0, use out. + uint32_t (*getxattr_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_getxattr_in *in, const struct fuse_buffer *name, + struct fuse_buffer *value, struct fuse_getxattr_out *out); + + uint32_t (*listxattr_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_getxattr_in *in); + // if in->size > 0, use value. If in->size == 0, use out. + uint32_t (*listxattr_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_getxattr_in *in, + struct fuse_buffer *value, struct fuse_getxattr_out *out); + + uint32_t (*setxattr_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_setxattr_in *in, struct fuse_buffer *name, + struct fuse_buffer *value); + uint32_t (*setxattr_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_setxattr_in *in, const struct fuse_buffer *name, + const struct fuse_buffer *value); + + uint32_t (*removexattr_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_buffer *name); + uint32_t (*removexattr_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_buffer *name); + + /* Read and Write iter will likely undergo some sort of change/addition to handle changing + * the data buffer passed in/out. */ + uint32_t (*read_iter_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_read_in *in); + uint32_t (*read_iter_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_read_in *in, + struct fuse_read_iter_out *out); + + uint32_t (*write_iter_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_write_in *in); + uint32_t (*write_iter_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_write_in *in, + struct fuse_write_iter_out *out); + + uint32_t (*file_fallocate_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_fallocate_in *in); + uint32_t (*file_fallocate_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_fallocate_in *in); + + uint32_t (*lookup_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_buffer *name); + uint32_t (*lookup_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_buffer *name, + struct fuse_entry_out *out, struct fuse_buffer *entries); + + uint32_t (*mknod_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_mknod_in *in, struct fuse_buffer *name); + uint32_t (*mknod_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_mknod_in *in, const struct fuse_buffer *name); + + uint32_t (*mkdir_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_mkdir_in *in, struct fuse_buffer *name); + uint32_t (*mkdir_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_mkdir_in *in, const struct fuse_buffer *name); + + uint32_t (*rmdir_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_buffer *name); + uint32_t (*rmdir_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_buffer *name); + + uint32_t (*rename2_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_rename2_in *in, struct fuse_buffer *old_name, + struct fuse_buffer *new_name); + uint32_t (*rename2_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_rename2_in *in, const struct fuse_buffer *old_name, + const struct fuse_buffer *new_name); + + uint32_t (*rename_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_rename_in *in, struct fuse_buffer *old_name, + struct fuse_buffer *new_name); + uint32_t (*rename_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_rename_in *in, const struct fuse_buffer *old_name, + const struct fuse_buffer *new_name); + + uint32_t (*unlink_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_buffer *name); + uint32_t (*unlink_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_buffer *name); + + uint32_t (*link_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_link_in *in, struct fuse_buffer *name); + uint32_t (*link_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_link_in *in, const struct fuse_buffer *name); + + uint32_t (*getattr_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_getattr_in *in); + uint32_t (*getattr_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_getattr_in *in, + struct fuse_attr_out *out); + + uint32_t (*setattr_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_setattr_in *in); + uint32_t (*setattr_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_setattr_in *in, + struct fuse_attr_out *out); + + uint32_t (*statfs_prefilter)(const struct bpf_fuse_meta_info *meta); + uint32_t (*statfs_postfilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_statfs_out *out); + + //TODO: This does not allow doing anything with path + uint32_t (*get_link_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_buffer *name); + uint32_t (*get_link_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_buffer *name); + + uint32_t (*symlink_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_buffer *name, struct fuse_buffer *path); + uint32_t (*symlink_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_buffer *name, const struct fuse_buffer *path); + + uint32_t (*readdir_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_read_in *in); + uint32_t (*readdir_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_read_in *in, + struct fuse_read_out *out, struct fuse_buffer *buffer); + + uint32_t (*access_prefilter)(const struct bpf_fuse_meta_info *meta, + struct fuse_access_in *in); + uint32_t (*access_postfilter)(const struct bpf_fuse_meta_info *meta, + const struct fuse_access_in *in); + + char name[BPF_FUSE_NAME_MAX]; +}; + #endif /* _BPF_FUSE_H */ diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile index 1d3892168d32..26a2e741ef61 100644 --- a/kernel/bpf/Makefile +++ b/kernel/bpf/Makefile @@ -45,3 +45,7 @@ obj-$(CONFIG_BPF_PRELOAD) += preload/ obj-$(CONFIG_BPF_SYSCALL) += relo_core.o $(obj)/relo_core.o: $(srctree)/tools/lib/bpf/relo_core.c FORCE $(call if_changed_rule,cc_o_c) + +ifeq ($(CONFIG_FUSE_BPF),y) +obj-$(CONFIG_BPF_SYSCALL) += bpf_fuse.o +endif diff --git a/kernel/bpf/bpf_fuse.c b/kernel/bpf/bpf_fuse.c new file mode 100644 index 000000000000..35125c1f8eef --- /dev/null +++ b/kernel/bpf/bpf_fuse.c @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2021 Google LLC + +#include +#include +#include +#include +#include + +void *bpf_fuse_get_writeable(struct fuse_buffer *arg, u64 size, bool copy) +{ + void *writeable_val; + + if (arg->flags & BPF_FUSE_IMMUTABLE) + return 0; + + if (size <= arg->size && + (!(arg->flags & BPF_FUSE_MUST_ALLOCATE) || + (arg->flags & BPF_FUSE_ALLOCATED))) { + if (arg->flags & BPF_FUSE_VARIABLE_SIZE) + arg->size = size; + arg->flags |= BPF_FUSE_MODIFIED; + return arg->data; + } + /* Variable sized arrays must stay below max size. If the buffer must be fixed size, + * don't change the allocated size. Verifier will enforce requested size for accesses + */ + if (arg->flags & BPF_FUSE_VARIABLE_SIZE) { + if (size > arg->max_size) + return 0; + } else { + if (size > arg->size) + return 0; + size = arg->size; + } + + if (size != arg->size && size > arg->max_size) + return 0; + + /* If our buffer is big enough, just adjust size */ + if (size <= arg->alloc_size) { + if (!copy) + arg->size = size; + arg->flags |= BPF_FUSE_MODIFIED; + return arg->data; + } + + writeable_val = kzalloc(size, GFP_KERNEL); + if (!writeable_val) + return 0; + + arg->alloc_size = size; + /* If we're copying the buffer, assume the same amount is used. If that isn't the case, + * caller must change size. Otherwise, assume entirety of new buffer is used. + */ + if (copy) + memcpy(writeable_val, arg->data, (arg->size > size) ? size : arg->size); + else + arg->size = size; + + if (arg->flags & BPF_FUSE_ALLOCATED) + kfree(arg->data); + arg->data = writeable_val; + + arg->flags |= BPF_FUSE_ALLOCATED | BPF_FUSE_MODIFIED; + + return arg->data; +} +EXPORT_SYMBOL(bpf_fuse_get_writeable); + +__diag_push(); +__diag_ignore_all("-Wmissing-prototypes", + "Global kfuncs as their definitions will be in BTF"); +void bpf_fuse_get_rw_dynptr(struct fuse_buffer *buffer, struct bpf_dynptr_kern *dynptr__uninit, u64 size, bool copy) +{ + buffer->data = bpf_fuse_get_writeable(buffer, size, copy); + bpf_dynptr_init(dynptr__uninit, buffer->data, BPF_DYNPTR_TYPE_LOCAL, 0, buffer->size); +} + +void bpf_fuse_get_ro_dynptr(const struct fuse_buffer *buffer, struct bpf_dynptr_kern *dynptr__uninit) +{ + bpf_dynptr_init(dynptr__uninit, buffer->data, BPF_DYNPTR_TYPE_LOCAL, 0, buffer->size); + bpf_dynptr_set_rdonly(dynptr__uninit); +} + +uint32_t bpf_fuse_return_len(struct fuse_buffer *buffer) +{ + return buffer->size; +} +__diag_pop(); +BTF_SET8_START(fuse_kfunc_set) +BTF_ID_FLAGS(func, bpf_fuse_get_rw_dynptr) +BTF_ID_FLAGS(func, bpf_fuse_get_ro_dynptr) +BTF_ID_FLAGS(func, bpf_fuse_return_len) +BTF_SET8_END(fuse_kfunc_set) + +static const struct btf_kfunc_id_set bpf_fuse_kfunc_set = { + .owner = THIS_MODULE, + .set = &fuse_kfunc_set, +}; + +static int __init bpf_fuse_kfuncs_init(void) +{ + return register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, + &bpf_fuse_kfunc_set); +} + +late_initcall(bpf_fuse_kfuncs_init); + +static const struct bpf_func_proto *bpf_fuse_get_func_proto(enum bpf_func_id func_id, + const struct bpf_prog *prog) +{ + switch (func_id) { + default: + return bpf_base_func_proto(func_id); + } +} + +static bool bpf_fuse_is_valid_access(int off, int size, + enum bpf_access_type type, + const struct bpf_prog *prog, + struct bpf_insn_access_aux *info) +{ + return bpf_tracing_btf_ctx_access(off, size, type, prog, info); +} + +const struct btf_type *fuse_buffer_struct_type; + +static int bpf_fuse_btf_struct_access(struct bpf_verifier_log *log, + const struct bpf_reg_state *reg, + int off, int size) +{ + const struct btf_type *t; + + t = btf_type_by_id(reg->btf, reg->btf_id); + if (t == fuse_buffer_struct_type) { + bpf_log(log, + "direct access to fuse_buffer is disallowed\n"); + return -EACCES; + } + + return 0; +} + +static const struct bpf_verifier_ops bpf_fuse_verifier_ops = { + .get_func_proto = bpf_fuse_get_func_proto, + .is_valid_access = bpf_fuse_is_valid_access, + .btf_struct_access = bpf_fuse_btf_struct_access, +}; + +static int bpf_fuse_check_member(const struct btf_type *t, + const struct btf_member *member, + const struct bpf_prog *prog) +{ + //if (is_unsupported(__btf_member_bit_offset(t, member) / 8)) + // return -ENOTSUPP; + return 0; +} + +static int bpf_fuse_init_member(const struct btf_type *t, + const struct btf_member *member, + void *kdata, const void *udata) +{ + const struct fuse_ops *uf_ops; + struct fuse_ops *f_ops; + u32 moff; + + uf_ops = (const struct fuse_ops *)udata; + f_ops = (struct fuse_ops *)kdata; + + moff = __btf_member_bit_offset(t, member) / 8; + switch (moff) { + case offsetof(struct fuse_ops, name): + if (bpf_obj_name_cpy(f_ops->name, uf_ops->name, + sizeof(f_ops->name)) <= 0) + return -EINVAL; + //if (tcp_ca_find(utcp_ca->name)) + // return -EEXIST; + return 1; + } + + return 0; +} + +static int bpf_fuse_init(struct btf *btf) +{ + s32 type_id; + + type_id = btf_find_by_name_kind(btf, "fuse_buffer", BTF_KIND_STRUCT); + if (type_id < 0) + return -EINVAL; + fuse_buffer_struct_type = btf_type_by_id(btf, type_id); + + return 0; +} + +static struct bpf_fuse_ops_attach *fuse_reg = NULL; + +static int bpf_fuse_reg(void *kdata) +{ + if (fuse_reg) + return fuse_reg->fuse_register_bpf(kdata); + pr_warn("Cannot register fuse_ops, FUSE not found"); + return -EOPNOTSUPP; +} + +static void bpf_fuse_unreg(void *kdata) +{ + if(fuse_reg) + return fuse_reg->fuse_unregister_bpf(kdata); +} + +int register_fuse_bpf(struct bpf_fuse_ops_attach *reg_ops) +{ + fuse_reg = reg_ops; + return 0; +} +EXPORT_SYMBOL_GPL(register_fuse_bpf); + +void unregister_fuse_bpf(struct bpf_fuse_ops_attach *reg_ops) +{ + if (reg_ops == fuse_reg) + fuse_reg = NULL; + else + pr_warn("Refusing to unregister unregistered FUSE"); +} +EXPORT_SYMBOL_GPL(unregister_fuse_bpf); + +/* "extern" is to avoid sparse warning. It is only used in bpf_struct_ops.c. */ +extern struct bpf_struct_ops bpf_fuse_ops; + +struct bpf_struct_ops bpf_fuse_ops = { + .verifier_ops = &bpf_fuse_verifier_ops, + .reg = bpf_fuse_reg, + .unreg = bpf_fuse_unreg, + .check_member = bpf_fuse_check_member, + .init_member = bpf_fuse_init_member, + .init = bpf_fuse_init, + .name = "fuse_ops", +}; + diff --git a/kernel/bpf/bpf_struct_ops_types.h b/kernel/bpf/bpf_struct_ops_types.h index 5678a9ddf817..fabb2c1a9482 100644 --- a/kernel/bpf/bpf_struct_ops_types.h +++ b/kernel/bpf/bpf_struct_ops_types.h @@ -5,6 +5,10 @@ #ifdef CONFIG_NET BPF_STRUCT_OPS_TYPE(bpf_dummy_ops) #endif +#ifdef CONFIG_FUSE_BPF +#include +BPF_STRUCT_OPS_TYPE(fuse_ops) +#endif #ifdef CONFIG_INET #include BPF_STRUCT_OPS_TYPE(tcp_congestion_ops) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 027f9f8a3551..c34fd9e70039 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "../tools/lib/bpf/relo_core.h" diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index fd959824469d..b3bda15283c0 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -9597,6 +9597,8 @@ enum special_kfunc_type { KF_bpf_dynptr_from_xdp, KF_bpf_dynptr_slice, KF_bpf_dynptr_slice_rdwr, + KF_bpf_fuse_get_rw_dynptr, + KF_bpf_fuse_get_ro_dynptr, }; BTF_SET_START(special_kfunc_set) @@ -9616,6 +9618,8 @@ BTF_ID(func, bpf_dynptr_from_skb) BTF_ID(func, bpf_dynptr_from_xdp) BTF_ID(func, bpf_dynptr_slice) BTF_ID(func, bpf_dynptr_slice_rdwr) +BTF_ID(func, bpf_fuse_get_rw_dynptr) +BTF_ID(func, bpf_fuse_get_ro_dynptr) BTF_SET_END(special_kfunc_set) BTF_ID_LIST(special_kfunc_list) @@ -9637,6 +9641,8 @@ BTF_ID(func, bpf_dynptr_from_skb) BTF_ID(func, bpf_dynptr_from_xdp) BTF_ID(func, bpf_dynptr_slice) BTF_ID(func, bpf_dynptr_slice_rdwr) +BTF_ID(func, bpf_fuse_get_rw_dynptr) +BTF_ID(func, bpf_fuse_get_ro_dynptr) static bool is_kfunc_bpf_rcu_read_lock(struct bpf_kfunc_call_arg_meta *meta) { @@ -10349,6 +10355,9 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ dynptr_arg_type |= DYNPTR_TYPE_SKB; else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_xdp]) dynptr_arg_type |= DYNPTR_TYPE_XDP; + else if (meta->func_id == special_kfunc_list[KF_bpf_fuse_get_rw_dynptr] || + meta->func_id == special_kfunc_list[KF_bpf_fuse_get_ro_dynptr]) + dynptr_arg_type |= DYNPTR_TYPE_LOCAL; ret = process_dynptr_func(env, regno, insn_idx, dynptr_arg_type); if (ret < 0) From patchwork Tue Apr 18 01:40:29 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214999 X-Patchwork-Delegate: bpf@iogearbox.net 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 6CCE5C77B7F for ; Tue, 18 Apr 2023 01:45:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230493AbjDRBpN (ORCPT ); Mon, 17 Apr 2023 21:45:13 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54372 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231288AbjDRBoO (ORCPT ); Mon, 17 Apr 2023 21:44:14 -0400 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 4EB6293FA for ; Mon, 17 Apr 2023 18:42:25 -0700 (PDT) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-54ee1fd7876so256058947b3.23 for ; Mon, 17 Apr 2023 18:42:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782116; x=1684374116; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=DALAO2ExmfoURx8M3mnwBONzpAf0Sbtvw4FW/zSKTOw=; b=b1pQ3Wp8pZEzXCbTH1QL/Ey40sMcU1WsteMeb2jyEsZgnjoUBXs5syd1fmCtVJ54BM ucvMArZe1DkIAPgCv6jTYYdZaXqPr1r6ky4LmwZHPamxAuGCy1nVGcp7zftCUflOLmmi aJeYevODjYE4KqIk27YivRkHUUBNXZTg0n5uJXu+HFhCaXhwv304SKI0sISs3mHSDnGE 5o+CzIg5h/iHNjnXmuv+lRIg+H2gVBT1ZeY2rFDaAJ2oL3N4SRNTTIZZDhGXtWOFHsrW uSS3X2aDmNNPhbbfdU+hPMiEwfIzOyfTT3qhJgQj1/9EC9ZxyNIJ7+fpnVzr+Azzqjmw gbbw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782116; x=1684374116; 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=DALAO2ExmfoURx8M3mnwBONzpAf0Sbtvw4FW/zSKTOw=; b=YdGZp4tSP+Ud0POkyq/wAJB9lY/roMm0kmBZe9Mz3qus1k2/z3BiyUUHUq65VV9vsQ KSNd7I3KoR5WTexhuM2hQSHHkBRUzBoo6FMhygkEWUQ+yNaFv7eBcM4vy94neYePgMdq Dt0lcVEDT1YBu6IQKA0BLGf/8eydlSp8TvIx6q8qhKHNZGH4iBpkSAo2IXz9vvcBbBN6 7HWr0VftlrGbM4m1VRaAhyjnyGjHt/dNIkZ+/xjkrapIlCRjp4aZfUP+hlbaKmSk5T25 LEcjOK/ky+YMnSF9Cdpk6H0yOjIeUg+9dIZxipBbX+TPgk64pKFt0AxNbLueYxgwgI6L 32/w== X-Gm-Message-State: AAQBX9eAblG2z+qHp4BlHw2SGCsjaAwEMcdhG17srXJIl/VLdRypCbZo ngpFnF1OPRCkPyfOxkn+GsRXWtfoSLc= X-Google-Smtp-Source: AKy350YnTlka0bcTU0RKtykV/F5v449o4LL77KqLH6Q3VuPHp90P6xAOoKpCMm9SycClz+UembUKg7Mmil0= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a25:8051:0:b0:b92:40f8:a46f with SMTP id a17-20020a258051000000b00b9240f8a46fmr4360158ybn.2.1681782116629; Mon, 17 Apr 2023 18:41:56 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:29 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-30-drosen@google.com> Subject: [RFC PATCH v3 29/37] fuse-bpf: Export Functions From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , kernel-team@android.com, Daniel Rosenberg Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net X-Patchwork-State: RFC These functions needed to be exported to build fuse as a module Signed-off-by: Daniel Rosenberg --- kernel/bpf/bpf_struct_ops.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/bpf/bpf_struct_ops.c b/kernel/bpf/bpf_struct_ops.c index deb9eecaf1e4..0bf727996a08 100644 --- a/kernel/bpf/bpf_struct_ops.c +++ b/kernel/bpf/bpf_struct_ops.c @@ -745,6 +745,7 @@ bool bpf_struct_ops_get(const void *kdata) map = __bpf_map_inc_not_zero(&st_map->map, false); return !IS_ERR(map); } +EXPORT_SYMBOL_GPL(bpf_struct_ops_get); void bpf_struct_ops_put(const void *kdata) { @@ -756,6 +757,7 @@ void bpf_struct_ops_put(const void *kdata) bpf_map_put(&st_map->map); } +EXPORT_SYMBOL_GPL(bpf_struct_ops_put); static bool bpf_struct_ops_valid_to_reg(struct bpf_map *map) { From patchwork Tue Apr 18 01:40:30 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13215000 X-Patchwork-Delegate: bpf@iogearbox.net 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 BA606C77B76 for ; Tue, 18 Apr 2023 01:45:32 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231314AbjDRBpb (ORCPT ); Mon, 17 Apr 2023 21:45:31 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53816 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231311AbjDRBob (ORCPT ); Mon, 17 Apr 2023 21:44:31 -0400 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 C762A93C3 for ; Mon, 17 Apr 2023 18:42:28 -0700 (PDT) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-54fde069e4aso98098687b3.3 for ; Mon, 17 Apr 2023 18:42:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782118; x=1684374118; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=aAO1Q9dHIBUjzlD7EaIYD+isx8yohgW2kA3a//3wT4c=; b=Kll014u8X4/3frsvKv6leX7edC+R7R1Svq3qo68yXW+C9ILWLgAfAjeNGMvs85FLGh zRrNJAuImgvQ5aLqSDs6M3APLSG0xdaiP4iK1agE9vnbv/bb9c9BBSAvBPElsvcznwhS 18Td8YTl8Nbb5K44I/dYYJ0RKx30ve0GpDthDWt69IIdqj5AW7DXB5TyEF4h/R+gSB59 20TGhlcI9NUnrlAgE2aCP3qYcsMrg8xF371ZknTy0YTdphlpmwpe1R4AMCUMeHYSZNSm B6IbrnWBvKY3/GcoP2N2xJuw2VM6t3g9rGLiGtVmfAxmLksP2oeb1gliWysDzZiaXu/O zeeg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782118; x=1684374118; 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=aAO1Q9dHIBUjzlD7EaIYD+isx8yohgW2kA3a//3wT4c=; b=kcW0ie3SEdxILE3PKsZPeS966Kc+VcPzmVs1P04SZzlNxu3TV0UC5n6UR5VflJu//L puwIaaQA52AIbzju+aqVyH+sNmzbAieiRCx6xWgdSvu7yVb+QVMF5Rgj77nftNIWuJfV /eHyPVzfuMgaI2vwnr+DM42eCV7HhEs857lmycFcjGhwKlJ4rHM1FP7Q0Fie/SLjinYD WVyd/WQnZIJ6HtD2szInBSGUj56gbe441N832AMvGfWgELbkvxP/acr3b54iMtozKuke RDp8ZW36j/pHYHN8Gw7PPjOnCP8wFYZhN+rQu8K7Yot11l8bTA5nOBgz6lCG/j1QG0Sn c2rg== X-Gm-Message-State: AAQBX9f/w0kMuZ0Fpl86N2TetvGDuVXu3hOaY9IOU54T602DBDf8nncO zjrwzRJG0JTDpL7vuKZ3p8GUhGMz4BY= X-Google-Smtp-Source: AKy350b2qWWm6u3O3O0nJnqw2v6qkgnCPTFrenW321HSICqNzQeyV4bIcR+CmpW2E20ABopGYna2UCgS8Vs= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a0d:ec02:0:b0:54c:2723:560d with SMTP id q2-20020a0dec02000000b0054c2723560dmr10855330ywn.3.1681782118566; Mon, 17 Apr 2023 18:41:58 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:30 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-31-drosen@google.com> Subject: [RFC PATCH v3 30/37] fuse: Provide registration functions for fuse-bpf From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , kernel-team@android.com, Daniel Rosenberg Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net X-Patchwork-State: RFC Fuse may be built as a module, but verifier components are not. This provides a means for fuse-bpf to handle struct op programs once the module is loaded. Signed-off-by: Daniel Rosenberg --- fs/fuse/Makefile | 2 +- fs/fuse/backing.c | 2 + fs/fuse/bpf_register.c | 209 +++++++++++++++++++++++++++++++++++++++ fs/fuse/fuse_i.h | 26 +++++ include/linux/bpf_fuse.h | 8 ++ 5 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 fs/fuse/bpf_register.c diff --git a/fs/fuse/Makefile b/fs/fuse/Makefile index a0853c439db2..903253db7285 100644 --- a/fs/fuse/Makefile +++ b/fs/fuse/Makefile @@ -9,6 +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 +fuse-$(CONFIG_FUSE_BPF) += backing.o bpf_register.o virtiofs-y := virtio_fs.o diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index e807ae4f6f53..898ef9e05e9d 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -3360,6 +3360,7 @@ int fuse_bpf_access(int *out, struct inode *inode, int mask) int __init fuse_bpf_init(void) { + init_fuse_bpf(); fuse_bpf_aio_request_cachep = kmem_cache_create("fuse_bpf_aio_req", sizeof(struct fuse_bpf_aio_req), 0, SLAB_HWCACHE_ALIGN, NULL); @@ -3371,5 +3372,6 @@ int __init fuse_bpf_init(void) void __exit fuse_bpf_cleanup(void) { + uninit_fuse_bpf(); kmem_cache_destroy(fuse_bpf_aio_request_cachep); } diff --git a/fs/fuse/bpf_register.c b/fs/fuse/bpf_register.c new file mode 100644 index 000000000000..dfe15dcf3477 --- /dev/null +++ b/fs/fuse/bpf_register.c @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * FUSE-BPF: Filesystem in Userspace with BPF + * Copyright (c) 2021 Google LLC + */ + +#include +#include +#include +#include +#include + +#include "fuse_i.h" + +struct fuse_ops tmp_f_op_empty = { 0 }; +struct fuse_ops *tmp_f_op = &tmp_f_op_empty; + +struct hashtable_entry { + struct hlist_node hlist; + struct hlist_node dlist; /* for deletion cleanup */ + struct qstr key; + struct fuse_ops *ops; +}; + +static DEFINE_HASHTABLE(name_to_ops, 8); + +static unsigned int full_name_case_hash(const void *salt, const unsigned char *name, unsigned int len) +{ + unsigned long hash = init_name_hash(salt); + + while (len--) + hash = partial_name_hash(tolower(*name++), hash); + return end_name_hash(hash); +} + +static inline void qstr_init(struct qstr *q, const char *name) +{ + q->name = name; + q->len = strlen(q->name); + q->hash = full_name_case_hash(0, q->name, q->len); +} + +static inline int qstr_copy(const struct qstr *src, struct qstr *dest) +{ + dest->name = kstrdup(src->name, GFP_KERNEL); + dest->hash_len = src->hash_len; + return !!dest->name; +} + +static inline int qstr_eq(const struct qstr *s1, const struct qstr *s2) +{ + int res, r1, r2, r3; + + r1 = s1->len == s2->len; + r2 = s1->hash == s2->hash; + r3 = memcmp(s1->name, s2->name, s1->len); + res = (s1->len == s2->len && s1->hash == s2->hash && !memcmp(s1->name, s2->name, s1->len)); + return res; +} + +static struct fuse_ops *__find_fuse_ops(const struct qstr *key) +{ + struct hashtable_entry *hash_cur; + unsigned int hash = key->hash; + struct fuse_ops *ret_ops; + + rcu_read_lock(); + hash_for_each_possible_rcu(name_to_ops, hash_cur, hlist, hash) { + if (qstr_eq(key, &hash_cur->key)) { + ret_ops = hash_cur->ops; + ret_ops = get_fuse_ops(ret_ops); + rcu_read_unlock(); + return ret_ops; + } + } + rcu_read_unlock(); + return NULL; +} + +struct fuse_ops *get_fuse_ops(struct fuse_ops *ops) +{ + if (bpf_try_module_get(ops, BPF_MODULE_OWNER)) + return ops; + else + return NULL; +} + +void put_fuse_ops(struct fuse_ops *ops) +{ + if (ops) + bpf_module_put(ops, BPF_MODULE_OWNER); +} + +struct fuse_ops *find_fuse_ops(const char *key) +{ + struct qstr q; + + qstr_init(&q, key); + return __find_fuse_ops(&q); +} + +static struct hashtable_entry *alloc_hashtable_entry(const struct qstr *key, + struct fuse_ops *value) +{ + struct hashtable_entry *ret = kzalloc(sizeof(*ret), GFP_KERNEL); + if (!ret) + return NULL; + INIT_HLIST_NODE(&ret->dlist); + INIT_HLIST_NODE(&ret->hlist); + + if (!qstr_copy(key, &ret->key)) { + kfree(ret); + return NULL; + } + + ret->ops = value; + return ret; +} + +static int __register_fuse_op(struct fuse_ops *value) +{ + struct hashtable_entry *hash_cur; + struct hashtable_entry *new_entry; + struct qstr key; + unsigned int hash; + + qstr_init(&key, value->name); + hash = key.hash; + hash_for_each_possible_rcu(name_to_ops, hash_cur, hlist, hash) { + if (qstr_eq(&key, &hash_cur->key)) { + return -EEXIST; + } + } + new_entry = alloc_hashtable_entry(&key, value); + if (!new_entry) + return -ENOMEM; + hash_add_rcu(name_to_ops, &new_entry->hlist, hash); + return 0; +} + +static int register_fuse_op(struct fuse_ops *value) +{ + int err; + + if (bpf_try_module_get(value, BPF_MODULE_OWNER)) + err = __register_fuse_op(value); + else + return -EBUSY; + + return err; +} + +static void unregister_fuse_op(struct fuse_ops *value) +{ + struct hashtable_entry *hash_cur; + struct qstr key; + unsigned int hash; + struct hlist_node *h_t; + HLIST_HEAD(free_list); + + qstr_init(&key, value->name); + hash = key.hash; + + hash_for_each_possible_rcu(name_to_ops, hash_cur, hlist, hash) { + if (qstr_eq(&key, &hash_cur->key)) { + hash_del_rcu(&hash_cur->hlist); + hlist_add_head(&hash_cur->dlist, &free_list); + } + } + synchronize_rcu(); + bpf_module_put(value, BPF_MODULE_OWNER); + hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) + kfree(hash_cur); +} + +static void fuse_op_list_destroy(void) +{ + struct hashtable_entry *hash_cur; + struct hlist_node *h_t; + HLIST_HEAD(free_list); + int i; + + //mutex_lock(&sdcardfs_super_list_lock); + hash_for_each_rcu(name_to_ops, i, hash_cur, hlist) { + hash_del_rcu(&hash_cur->hlist); + hlist_add_head(&hash_cur->dlist, &free_list); + } + synchronize_rcu(); + hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) + kfree(hash_cur); + //mutex_unlock(&sdcardfs_super_list_lock); + pr_info("fuse: destroyed fuse_op list\n"); +} + +static struct bpf_fuse_ops_attach bpf_fuse_ops_connect = { + .fuse_register_bpf = ®ister_fuse_op, + .fuse_unregister_bpf = &unregister_fuse_op, +}; + +int init_fuse_bpf(void) +{ + return register_fuse_bpf(&bpf_fuse_ops_connect); +} + +void uninit_fuse_bpf(void) +{ + unregister_fuse_bpf(&bpf_fuse_ops_connect); + fuse_op_list_destroy(); +} diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 2bd45c8658e8..84c591d02e43 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1390,6 +1390,32 @@ void fuse_file_release(struct inode *inode, struct fuse_file *ff, unsigned int open_flags, fl_owner_t id, bool isdir); /* backing.c */ +#ifdef CONFIG_FUSE_BPF +struct fuse_ops *find_fuse_ops(const char *key); +struct fuse_ops *get_fuse_ops(struct fuse_ops *ops); +void put_fuse_ops(struct fuse_ops *ops); +int init_fuse_bpf(void); +void uninit_fuse_bpf(void); +#else +int init_fuse_bpf(void) +{ + return -EOPNOTSUPP; +} +void uninit_fuse_bpf(void) +{ +} +struct fuse_ops *find_fuse_ops(const char *key) +{ + return NULL; +} +struct fuse_ops *get_fuse_ops(struct fuse_ops *ops) +{ + return NULL; +} +void put_fuse_ops(struct fuse_ops *ops) +{ +} +#endif enum fuse_bpf_set { FUSE_BPF_UNCHANGED = 0, diff --git a/include/linux/bpf_fuse.h b/include/linux/bpf_fuse.h index 780a7889aea2..2183a7a45c92 100644 --- a/include/linux/bpf_fuse.h +++ b/include/linux/bpf_fuse.h @@ -270,4 +270,12 @@ struct fuse_ops { char name[BPF_FUSE_NAME_MAX]; }; +struct bpf_fuse_ops_attach { + int (*fuse_register_bpf)(struct fuse_ops *f_ops); + void (*fuse_unregister_bpf)(struct fuse_ops *f_ops); +}; + +int register_fuse_bpf(struct bpf_fuse_ops_attach *reg_ops); +void unregister_fuse_bpf(struct bpf_fuse_ops_attach *reg_ops); + #endif /* _BPF_FUSE_H */ From patchwork Tue Apr 18 01:40:31 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13215001 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 7CF60C77B72 for ; Tue, 18 Apr 2023 01:45:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230026AbjDRBpd (ORCPT ); Mon, 17 Apr 2023 21:45:33 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53708 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231335AbjDRBot (ORCPT ); Mon, 17 Apr 2023 21:44:49 -0400 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 1BCB259D1 for ; Mon, 17 Apr 2023 18:42:31 -0700 (PDT) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-552e3fa8f2fso32404387b3.19 for ; Mon, 17 Apr 2023 18:42:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782120; x=1684374120; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=YCFXbIE/7zM5T1Zxz4s1sjI+Fr3ZActGUdsgPEik6eM=; b=FB1wts4EjjmXAYzwfnd2fXz7dClLikX5H2f+j5h+pJVNsx6Q6NlCRmHSItjyPUrnLv YKyra7iCWVEBF0LSP7pSpNJnt31Essq6CEOYUvx3a7DeB1H1zp6/AzOIMV4nIMmPQ1qh qMCnxSgQQEmKpoo+wUGmGbWJWaPB9mdD+g4OxND3tsdF6PhQCvPKTZ1L9qfeLvvDeRwC diebbFiKsuAfFKHUnGox1H1Vm8nPX77oEHMs7kh9H6yCekzNrbtTKcZMsq9SW5ZfMvTo tES6furCQMp++TtGvH27jawxyKvTX19Ki63HlB/d0cozAwalDTSnp6pYD+fi6iqjgBt8 e32w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782120; x=1684374120; 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=YCFXbIE/7zM5T1Zxz4s1sjI+Fr3ZActGUdsgPEik6eM=; b=baBOffDR7aQz59rvunOlcBG3f+hYp2sWdFsYXXiIas1U55GQOSRSFujPKS0I50u7LM SspQ+F3BQhr77jMw9hgic0hHuJ0dmV9JGeZuk0b2WznV4VF0rCitVkX8Q54ddhgR4mbV G/9aDEKDWxsk3+JW+sI2LdYCnvnldKtInOdo75Avao7rwRjBy5HcppfmaTmzK9j7zBSs smUtSkcc+hyRhGaVAo5iKaIIhDdMQ0jB1n57lstQGe3k/bZy9Yom7kRpmUT8BMzUlmJ5 XO3PHOP0TQyKp2w/+KmZhcnOQ3eIdeGoEx1m3YnRMJzjgzGaMFUI8k4Xxn5wSJGVM0MC lnXg== X-Gm-Message-State: AAQBX9d0G17RsgTa00bywU3HcoLHN1on8HpAYuamREfmcstMvCUZ0SNk T2zrAgKkFr96hhgCfaoeSao1e1GeENk= X-Google-Smtp-Source: AKy350bP8nkD4q8ZefsJwT0Q2ZhJNsKNBRwFjIJbXhB6OG/HhtE2gYEray13+6teD80zVxL1NhMk7TiPsY0= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a81:af0c:0:b0:54c:2889:7105 with SMTP id n12-20020a81af0c000000b0054c28897105mr10920487ywh.0.1681782120540; Mon, 17 Apr 2023 18:42:00 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:31 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-32-drosen@google.com> Subject: [RFC PATCH v3 31/37] fuse-bpf: Set fuse_ops at mount or lookup time From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , kernel-team@android.com, Daniel Rosenberg Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC This adds the ability to associate a fuse_op struct_op program with inodes in fuse. This can be done at mount time at the root level, or by inode at lookup time. Signed-off-by: Daniel Rosenberg --- fs/fuse/backing.c | 91 +++++++++++++++++++++++++++++++++++++++++++---- fs/fuse/dir.c | 16 +++++++-- fs/fuse/fuse_i.h | 12 +++++++ fs/fuse/inode.c | 28 ++++++++++++++- 4 files changed, 138 insertions(+), 9 deletions(-) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index 898ef9e05e9d..d5ba1e334e69 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -6,6 +6,7 @@ #include "fuse_i.h" +#include #include #include #include @@ -168,12 +169,13 @@ static void fuse_get_backing_path(struct file *file, struct path *path) static bool has_file(int type) { - return type == FUSE_ENTRY_BACKING; + return (type == FUSE_ENTRY_BACKING); } /* - * The optional fuse bpf entry lists the backing file for a particular - * lookup. These are inherited by default. + * The optional fuse bpf entry lists the bpf and backing files for a particular + * lookup. These are inherited by default. A Bpf requires a backing file to be + * meaningful. * * In the future, we may support multiple bpfs, and multiple backing files for * the bpf to choose between. @@ -182,14 +184,14 @@ static bool has_file(int type) * 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 fuse_ops *ops; struct file *file; bool has_backing = false; + bool has_bpf_ops = false; int num_entries; int err = -EINVAL; int i; @@ -227,6 +229,11 @@ int parse_fuse_bpf_entry(struct fuse_bpf_entry *fbe, int num) goto out_err; fbe->backing_action = FUSE_BPF_REMOVE; break; + case FUSE_ENTRY_REMOVE_BPF: + if (fbe->bpf_action || i == 2) + goto out_err; + fbe->bpf_action = FUSE_BPF_REMOVE; + break; case FUSE_ENTRY_BACKING: if (fbe->backing_action) goto out_err; @@ -234,8 +241,17 @@ int parse_fuse_bpf_entry(struct fuse_bpf_entry *fbe, int num) fbe->backing_action = FUSE_BPF_SET; has_backing = true; break; + case FUSE_ENTRY_BPF: + if (fbe->bpf_action || i == 2) + goto out_err; + ops = find_fuse_ops(fbeo->name); + if (!ops) + goto out_err; + has_bpf_ops = true; + fbe->bpf_action = FUSE_BPF_SET; + fbe->ops = ops; + break; default: - err = -EINVAL; goto out_err; } if (has_file(fbeo->entry_type)) { @@ -252,6 +268,10 @@ int parse_fuse_bpf_entry(struct fuse_bpf_entry *fbe, int num) fput(file); if (has_backing) path_put_init(&fbe->backing_path); + if (has_bpf_ops) { + put_fuse_ops(fbe->ops); + fbe->ops = NULL; + } return err; } @@ -527,6 +547,15 @@ static int fuse_create_open_backing(struct bpf_fuse_args *fa, int *out, goto out; } + if (get_fuse_inode(inode)->bpf_ops) + put_fuse_ops(get_fuse_inode(inode)->bpf_ops); + get_fuse_inode(inode)->bpf_ops = dir_fuse_inode->bpf_ops; + if (get_fuse_inode(inode)->bpf_ops) + if (!get_fuse_ops(get_fuse_inode(inode)->bpf_ops)) { + *out = -EINVAL; + goto out; + } + newent = d_splice_alias(inode, entry); if (IS_ERR(newent)) { *out = PTR_ERR(newent); @@ -1842,6 +1871,52 @@ int fuse_handle_backing(struct fuse_bpf_entry *fbe, struct path *backing_path) return 0; } +int fuse_handle_bpf_ops(struct fuse_bpf_entry *fbe, struct inode *parent, + struct fuse_ops **ops) +{ + struct fuse_ops *new_ops; + + /* Parent isn't presented, but we want to keep + * Don't touch bpf program at all in this case + */ + if (fbe->bpf_action == FUSE_BPF_UNCHANGED && !parent) + return 0; + + switch (fbe->bpf_action) { + case FUSE_BPF_UNCHANGED: { + struct fuse_inode *pi = get_fuse_inode(parent); + + new_ops = pi->bpf_ops; + if (new_ops && !get_fuse_ops(new_ops)) + return -EINVAL; + break; + } + + case FUSE_BPF_REMOVE: + new_ops = NULL; + break; + + case FUSE_BPF_SET: + new_ops = fbe->ops; + + if (!new_ops) + return -EINVAL; + break; + + default: + return -EINVAL; + } + + /* Cannot change existing program */ + if (*ops) { + put_fuse_ops(new_ops); + return new_ops == *ops ? 0 : -EINVAL; + } + + *ops = new_ops; + return 0; +} + static int fuse_lookup_finalize(struct bpf_fuse_args *fa, struct dentry **out, struct inode *dir, struct dentry *entry, unsigned int flags) { @@ -1879,6 +1954,10 @@ static int fuse_lookup_finalize(struct bpf_fuse_args *fa, struct dentry **out, if (IS_ERR(inode)) return PTR_ERR(inode); + error = fuse_handle_bpf_ops(fbe, dir, &get_fuse_inode(inode)->bpf_ops); + if (error) + return error; + get_fuse_inode(inode)->nodeid = feo->nodeid; *out = d_splice_alias(inode, entry); diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index d1c3b2bfb0b1..b7bc8260a537 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -185,6 +186,7 @@ static bool backing_data_changed(struct fuse_inode *fi, struct dentry *entry, { struct path new_backing_path; struct inode *new_backing_inode; + struct fuse_ops *ops = NULL; int err; bool ret = true; @@ -199,9 +201,15 @@ static bool backing_data_changed(struct fuse_inode *fi, struct dentry *entry, if (err) goto put_inode; - ret = (fi->backing_inode != new_backing_inode || - !path_equal(&get_fuse_dentry(entry)->backing_path, &new_backing_path)); + err = fuse_handle_bpf_ops(bpf_arg, entry->d_parent->d_inode, &ops); + if (err) + goto put_bpf; + ret = (ops != fi->bpf_ops || fi->backing_inode != new_backing_inode || + !path_equal(&get_fuse_dentry(entry)->backing_path, &new_backing_path)); +put_bpf: + if (ops) + put_fuse_ops(ops); put_inode: path_put(&new_backing_path); return ret; @@ -466,6 +474,10 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, *inode = fuse_iget_backing(sb, outarg->nodeid, backing_inode); if (!*inode) goto out_queue_forget; + + err = fuse_handle_bpf_ops(&bpf_arg, NULL, &get_fuse_inode(*inode)->bpf_ops); + if (err) + goto out; } else #endif { diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 84c591d02e43..15962ab3b381 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -33,6 +33,7 @@ #include #include #include +#include #include /** Default max number of pages that can be used in a single read request */ @@ -109,6 +110,12 @@ struct fuse_inode { * If this is set, nodeid is 0. */ struct inode *backing_inode; + + /** + * fuse_ops, provides handlers to run on all operations to determine + * whether to pass through or handle in place + */ + struct fuse_ops *bpf_ops; #endif /** Unique ID, which identifies the inode between userspace @@ -571,6 +578,7 @@ struct fuse_fs_context { unsigned int max_read; unsigned int blksize; const char *subtype; + struct fuse_ops *root_ops; struct file *root_dir; /* DAX device, may be NULL */ @@ -1428,6 +1436,8 @@ struct fuse_bpf_entry { enum fuse_bpf_set backing_action; struct path backing_path; + enum fuse_bpf_set bpf_action; + struct fuse_ops *ops; bool is_used; }; @@ -1651,6 +1661,8 @@ static inline int fuse_bpf_access(int *out, struct inode *inode, int mask) 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_handle_bpf_ops(struct fuse_bpf_entry *fbe, struct inode *parent, + struct fuse_ops **ops); int fuse_revalidate_backing(struct dentry *entry, unsigned int flags); diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 31f34962bc9b..7fd79efbdac1 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -80,6 +80,7 @@ static struct inode *fuse_alloc_inode(struct super_block *sb) fi->inval_mask = 0; #ifdef CONFIG_FUSE_BPF fi->backing_inode = NULL; + fi->bpf_ops = NULL; #endif fi->nodeid = 0; fi->nlookup = 0; @@ -125,6 +126,9 @@ static void fuse_evict_inode(struct inode *inode) #ifdef CONFIG_FUSE_BPF iput(fi->backing_inode); + if (fi->bpf_ops) + put_fuse_ops(fi->bpf_ops); + fi->bpf_ops = NULL; #endif truncate_inode_pages_final(&inode->i_data); @@ -755,6 +759,7 @@ enum { OPT_ALLOW_OTHER, OPT_MAX_READ, OPT_BLKSIZE, + OPT_ROOT_BPF, OPT_ROOT_DIR, OPT_NO_DAEMON, OPT_ERR @@ -771,6 +776,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_string ("root_bpf", OPT_ROOT_BPF), fsparam_u32 ("root_dir", OPT_ROOT_DIR), fsparam_flag ("no_daemon", OPT_NO_DAEMON), {} @@ -856,6 +862,18 @@ static int fuse_parse_param(struct fs_context *fsc, struct fs_parameter *param) ctx->blksize = result.uint_32; break; + case OPT_ROOT_BPF: + if (strnlen(param->string, BPF_FUSE_NAME_MAX + 1) > BPF_FUSE_NAME_MAX) { + return invalfc(fsc, "root_bpf name too long. Max length is %d", BPF_FUSE_NAME_MAX); + } + + ctx->root_ops = find_fuse_ops(param->string); + if (IS_ERR_OR_NULL(ctx->root_ops)) { + ctx->root_ops = NULL; + return invalfc(fsc, "Unable to find bpf program"); + } + break; + case OPT_ROOT_DIR: ctx->root_dir = fget(result.uint_32); if (!ctx->root_dir) @@ -881,6 +899,8 @@ static void fuse_free_fsc(struct fs_context *fsc) if (ctx) { if (ctx->root_dir) fput(ctx->root_dir); + if (ctx->root_ops) + put_fuse_ops(ctx->root_ops); kfree(ctx->subtype); kfree(ctx); } @@ -1010,6 +1030,7 @@ EXPORT_SYMBOL_GPL(fuse_conn_get); static struct inode *fuse_get_root_inode(struct super_block *sb, unsigned int mode, + struct fuse_ops *root_bpf_ops, struct file *backing_fd) { struct fuse_attr attr; @@ -1024,6 +1045,10 @@ static struct inode *fuse_get_root_inode(struct super_block *sb, return NULL; #ifdef CONFIG_FUSE_BPF + get_fuse_inode(inode)->bpf_ops = root_bpf_ops; + if (root_bpf_ops) + get_fuse_ops(root_bpf_ops); + if (backing_fd) { get_fuse_inode(inode)->backing_inode = backing_fd->f_inode; ihold(backing_fd->f_inode); @@ -1704,7 +1729,8 @@ int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx) fc->no_daemon = ctx->no_daemon; err = -ENOMEM; - root = fuse_get_root_inode(sb, ctx->rootmode, ctx->root_dir); + root = fuse_get_root_inode(sb, ctx->rootmode, ctx->root_ops, + ctx->root_dir); sb->s_d_op = &fuse_root_dentry_operations; root_dentry = d_make_root(root); if (!root_dentry) From patchwork Tue Apr 18 01:40:32 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13215003 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 2725DC77B75 for ; Tue, 18 Apr 2023 01:45:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231146AbjDRBpq (ORCPT ); Mon, 17 Apr 2023 21:45:46 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53850 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231338AbjDRBpK (ORCPT ); Mon, 17 Apr 2023 21:45:10 -0400 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 40CDA977E for ; Mon, 17 Apr 2023 18:42:37 -0700 (PDT) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-552f169d85eso23926967b3.13 for ; Mon, 17 Apr 2023 18:42:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782123; x=1684374123; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=rV9cf7X8542M+vZlF6IZmm0T2c7Hcn7L+aL9IBbyW5E=; b=HzUxzGdXoVkcjZV6vHWK2fyeDSJjSY1BBS0UdnnsTptpTqqiHorVFtUY2wrJbm+wO6 c6YZbS6lKh3efNHCQ50jJX0wC8VyZjZRkK23zqrpmzD4ei1yb0QYNd5rcDWau5SzeBJQ rDlcg6fdSokuniQsaZDb6N8xlmOwZBb8POwJL9DzFUs4EG1RzaiKuvjbe3Lo7t9bQE9x 7KtfUpluZQaqe3VCF32gmQurOzqALEDbob0EvuC9YrxPDt/0W9mtlVP65GhxaB+gMeEQ r1ro2/gZJ93/hQh+dmunABaKF8jUu1yGu0CShwO4GzI+5HpmwSJfNjIOArphd+PrRqgI uiuA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782123; x=1684374123; 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=rV9cf7X8542M+vZlF6IZmm0T2c7Hcn7L+aL9IBbyW5E=; b=QvOB2cItnhJaJFi8xfNpOE9WZUl/dRS8a28HumS2lYaPmkrDGKuNTC0rcfqM8d9v5/ rNAfUh/+arvRKXr+sEEFJs69j0yor/VUVV13ibnaW/F8ZFr6oRCwe0mRd9DT6amzIJ8H g7+VfNNVp/8ocKuqNHFAxeIncL6DNPXCDgTcYjdkpayTA2V6d++gmUJo/qq2EKo54Ijq 3xBtkDNXvzDaBmm+BFns0R89hkll5vMeRjwSLt5ji3EPJmrBtyVvAZ2+ko6+/sLQjw2k hRxLyjz45JFfXRdZTTyzUgP0liciqmGF/ZgeO1DU7P77+hXrJ6l/Zu3kWWSKA+0Nwsgj +zaw== X-Gm-Message-State: AAQBX9eJTjLBBnniLjYFUm3A4hn+dIzqRl1blXHmsq17vWNNrLuWSAmJ 3QUvhHrFeY0NzAed++cOvW1Yxj5Tzzc= X-Google-Smtp-Source: AKy350brwRJR3viQIbnQAfQLMqCkkFC/uJkI1XPJ61jychKX9KIwFh23vN/gKLI87pp9DsQRargy36UV8QQ= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a25:da89:0:b0:b8f:4f1d:be06 with SMTP id n131-20020a25da89000000b00b8f4f1dbe06mr10985720ybf.11.1681782122938; Mon, 17 Apr 2023 18:42:02 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:32 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-33-drosen@google.com> Subject: [RFC PATCH v3 32/37] fuse-bpf: Call bpf for pre/post filters From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , kernel-team@android.com, Daniel Rosenberg Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC This allows altering input or output parameters to fuse calls that will be handled directly by the backing filesystems. BPF programs can signal whether the entire operation should instead go through regular fuse, or if a postfilter call is needed. Signed-off-by: Daniel Rosenberg --- fs/fuse/backing.c | 606 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 606 insertions(+) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index d5ba1e334e69..9217e9f83d98 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -14,6 +14,27 @@ #include #include +static inline void bpf_fuse_set_in_immutable(struct bpf_fuse_args *fa) +{ + int i; + + for (i = 0; i < FUSE_MAX_ARGS_IN; i++) + if (fa->in_args[i].is_buffer) + fa->in_args[i].buffer->flags |= BPF_FUSE_IMMUTABLE; +} + +static inline void bpf_fuse_free_alloced(struct bpf_fuse_args *fa) +{ + int i; + + for (i = 0; i < FUSE_MAX_ARGS_IN; i++) + if (fa->in_args[i].is_buffer && (fa->in_args[i].buffer->flags & BPF_FUSE_ALLOCATED)) + kfree(fa->in_args[i].buffer->data); + for (i = 0; i < FUSE_MAX_ARGS_OUT; i++) + if (fa->out_args[i].is_buffer && (fa->out_args[i].buffer->flags & BPF_FUSE_ALLOCATED)) + kfree(fa->out_args[i].buffer->data); +} + /* * expression statement to wrap the backing filter logic * struct inode *inode: inode with bpf and backing inode @@ -23,6 +44,10 @@ * up fa and io based on args * void initialize_out(struct bpf_fuse_args *fa, io *in_out, args...): function that sets * up fa and io based on args + * int call_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, io *in_out): Calls + * the struct_op prefilter function for the given fuse op + * int call_prostfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, io *in_out): Calls + * the struct_op postfilter function for the given fuse op * 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 @@ -30,13 +55,16 @@ */ #define bpf_fuse_backing(inode, io, out, \ initialize_in, initialize_out, \ + call_prefilter, call_postfilter, \ backing, finalize, args...) \ ({ \ struct fuse_inode *fuse_inode = get_fuse_inode(inode); \ + struct fuse_ops *fuse_ops = fuse_inode->bpf_ops; \ struct bpf_fuse_args fa = { 0 }; \ bool initialized = false; \ bool handled = false; \ ssize_t res; \ + int bpf_next; \ io feo = { 0 }; \ int error = 0; \ \ @@ -49,16 +77,46 @@ if (error) \ break; \ \ + fa.info.opcode |= FUSE_PREFILTER; \ + if (fuse_ops) \ + bpf_next = call_prefilter(fuse_ops, \ + &fa.info, &feo); \ + else \ + bpf_next = BPF_FUSE_CONTINUE; \ + if (bpf_next < 0) { \ + error = bpf_next; \ + break; \ + } \ + \ + bpf_fuse_set_in_immutable(&fa); \ + \ error = initialize_out(&fa, &feo, args); \ if (error) \ break; \ \ initialized = true; \ + if (bpf_next == BPF_FUSE_USER) { \ + handled = false; \ + break; \ + } \ + \ + fa.info.opcode &= ~FUSE_PREFILTER; \ \ error = backing(&fa, out, args); \ if (error < 0) \ fa.info.error_in = error; \ \ + if (bpf_next == BPF_FUSE_CONTINUE) \ + break; \ + \ + fa.info.opcode |= FUSE_POSTFILTER; \ + if (bpf_next == BPF_FUSE_POSTFILTER) \ + bpf_next = call_postfilter(fuse_ops, &fa.info, &feo);\ + if (bpf_next < 0) { \ + error = bpf_next; \ + break; \ + } \ + \ } while (false); \ \ if (initialized && handled) { \ @@ -66,6 +124,7 @@ if (res) \ error = res; \ } \ + bpf_fuse_free_alloced(&fa); \ \ *out = error ? _Generic((*out), \ default : \ @@ -351,6 +410,34 @@ static int fuse_open_initialize_out(struct bpf_fuse_args *fa, struct fuse_open_a return 0; } +static int fuse_open_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_open_args *open) +{ + if (meta->opcode == (FUSE_OPEN | FUSE_PREFILTER)) { + if (ops->open_prefilter) + return ops->open_prefilter(meta, &open->in); + } + if (meta->opcode == (FUSE_OPENDIR | FUSE_PREFILTER)) { + if (ops->opendir_prefilter) + return ops->opendir_prefilter(meta, &open->in); + } + return BPF_FUSE_CONTINUE; +} + +static int fuse_open_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_open_args *open) +{ + if (meta->opcode == (FUSE_OPEN | FUSE_POSTFILTER)) { + if (ops->open_postfilter) + return ops->open_postfilter(meta, &open->in, &open->out); + } + if (meta->opcode == (FUSE_OPENDIR | FUSE_POSTFILTER)) { + if (ops->opendir_postfilter) + return ops->opendir_postfilter(meta, &open->in, &open->out); + } + return BPF_FUSE_CONTINUE; +} + static int fuse_open_backing(struct bpf_fuse_args *fa, int *out, struct inode *inode, struct file *file, bool isdir) { @@ -419,6 +506,7 @@ int fuse_bpf_open(int *out, struct inode *inode, struct file *file, bool isdir) { return bpf_fuse_backing(inode, struct fuse_open_args, out, fuse_open_initialize_in, fuse_open_initialize_out, + fuse_open_prefilter, fuse_open_postfilter, fuse_open_backing, fuse_open_finalize, inode, file, isdir); } @@ -484,6 +572,22 @@ static int fuse_create_open_initialize_out(struct bpf_fuse_args *fa, struct fuse return 0; } +static int fuse_create_open_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_create_open_args *args) +{ + if (ops->create_open_prefilter) + return ops->create_open_prefilter(meta, &args->in, &args->name); + return BPF_FUSE_CONTINUE; +} + +static int fuse_create_open_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_create_open_args *args) +{ + if (ops->create_open_postfilter) + return ops->create_open_postfilter(meta, &args->in, &args->name, &args->entry_out, &args->open_out); + return BPF_FUSE_CONTINUE; +} + static int fuse_open_file_backing(struct inode *inode, struct file *file) { struct fuse_mount *fm = get_fuse_mount(inode); @@ -586,12 +690,16 @@ static int fuse_create_open_finalize(struct bpf_fuse_args *fa, int *out, 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 bpf_fuse_backing(dir, struct fuse_create_open_args, out, fuse_create_open_initialize_in, fuse_create_open_initialize_out, + fuse_create_open_prefilter, + fuse_create_open_postfilter, fuse_create_open_backing, fuse_create_open_finalize, dir, entry, file, flags, mode); @@ -652,6 +760,38 @@ static int fuse_releasedir_initialize_in(struct bpf_fuse_args *fa, return 0; } +static int fuse_release_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_release_in *args) +{ + if (ops->release_prefilter) + return ops->release_prefilter(meta, args); + return BPF_FUSE_CONTINUE; +} + +static int fuse_release_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_release_in *args) +{ + if (ops->release_postfilter) + return ops->release_postfilter(meta, args); + return BPF_FUSE_CONTINUE; +} + +static int fuse_releasedir_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_release_in *args) +{ + if (ops->releasedir_prefilter) + return ops->releasedir_prefilter(meta, args); + return BPF_FUSE_CONTINUE; +} + +static int fuse_releasedir_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_release_in *args) +{ + if (ops->releasedir_postfilter) + return ops->releasedir_postfilter(meta, args); + return BPF_FUSE_CONTINUE; +} + static int fuse_releasedir_initialize_out(struct bpf_fuse_args *fa, struct fuse_release_in *fri, struct inode *inode, struct file *file) @@ -677,6 +817,7 @@ int fuse_bpf_release(int *out, struct inode *inode, struct file *file) { return bpf_fuse_backing(inode, struct fuse_release_in, out, fuse_release_initialize_in, fuse_release_initialize_out, + fuse_release_prefilter, fuse_release_postfilter, fuse_release_backing, fuse_release_finalize, inode, file); } @@ -685,6 +826,7 @@ int fuse_bpf_releasedir(int *out, struct inode *inode, struct file *file) { return bpf_fuse_backing(inode, struct fuse_release_in, out, fuse_releasedir_initialize_in, fuse_releasedir_initialize_out, + fuse_releasedir_prefilter, fuse_releasedir_postfilter, fuse_release_backing, fuse_release_finalize, inode, file); } @@ -717,6 +859,22 @@ static int fuse_flush_initialize_out(struct bpf_fuse_args *fa, struct fuse_flush return 0; } +static int fuse_flush_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_flush_in *args) +{ + if (ops->flush_prefilter) + return ops->flush_prefilter(meta, args); + return BPF_FUSE_CONTINUE; +} + +static int fuse_flush_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_flush_in *args) +{ + if (ops->flush_postfilter) + return ops->flush_postfilter(meta, args); + return BPF_FUSE_CONTINUE; +} + static int fuse_flush_backing(struct bpf_fuse_args *fa, int *out, struct file *file, fl_owner_t id) { struct fuse_file *fuse_file = file->private_data; @@ -737,6 +895,7 @@ int fuse_bpf_flush(int *out, struct inode *inode, struct file *file, fl_owner_t { return bpf_fuse_backing(inode, struct fuse_flush_in, out, fuse_flush_initialize_in, fuse_flush_initialize_out, + fuse_flush_prefilter, fuse_flush_postfilter, fuse_flush_backing, fuse_flush_finalize, file, id); } @@ -780,6 +939,22 @@ static int fuse_lseek_initialize_out(struct bpf_fuse_args *fa, struct fuse_lseek return 0; } +static int fuse_lseek_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_lseek_args *args) +{ + if (ops->lseek_prefilter) + return ops->lseek_prefilter(meta, &args->in); + return BPF_FUSE_CONTINUE; +} + +static int fuse_lseek_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_lseek_args *args) +{ + if (ops->lseek_postfilter) + return ops->lseek_postfilter(meta, &args->in, &args->out); + return BPF_FUSE_CONTINUE; +} + static int fuse_lseek_backing(struct bpf_fuse_args *fa, loff_t *out, struct file *file, loff_t offset, int whence) { @@ -826,6 +1001,7 @@ int fuse_bpf_lseek(loff_t *out, struct inode *inode, struct file *file, loff_t o { return bpf_fuse_backing(inode, struct fuse_lseek_args, out, fuse_lseek_initialize_in, fuse_lseek_initialize_out, + fuse_lseek_prefilter, fuse_lseek_postfilter, fuse_lseek_backing, fuse_lseek_finalize, file, offset, whence); } @@ -878,6 +1054,22 @@ static int fuse_copy_file_range_initialize_out(struct bpf_fuse_args *fa, return 0; } +static int fuse_copy_file_range_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_copy_file_range_args *args) +{ + if (ops->copy_file_range_prefilter) + return ops->copy_file_range_prefilter(meta, &args->in); + return BPF_FUSE_CONTINUE; +} + +static int fuse_copy_file_range_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_copy_file_range_args *args) +{ + if (ops->copy_file_range_postfilter) + return ops->copy_file_range_postfilter(meta, &args->in, &args->out); + return BPF_FUSE_CONTINUE; +} + static int fuse_copy_file_range_backing(struct bpf_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) @@ -912,6 +1104,8 @@ int fuse_bpf_copy_file_range(ssize_t *out, struct inode *inode, struct file *fil return bpf_fuse_backing(inode, struct fuse_copy_file_range_args, out, fuse_copy_file_range_initialize_in, fuse_copy_file_range_initialize_out, + fuse_copy_file_range_prefilter, + fuse_copy_file_range_postfilter, fuse_copy_file_range_backing, fuse_copy_file_range_finalize, file_in, pos_in, file_out, pos_out, len, flags); @@ -947,6 +1141,22 @@ static int fuse_fsync_initialize_out(struct bpf_fuse_args *fa, struct fuse_fsync return 0; } +static int fuse_fsync_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_fsync_in *in) +{ + if (ops->fsync_prefilter) + return ops->fsync_prefilter(meta, in); + return BPF_FUSE_CONTINUE; +} + +static int fuse_fsync_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_fsync_in *in) +{ + if (ops->fsync_postfilter) + return ops->fsync_postfilter(meta, in); + return BPF_FUSE_CONTINUE; +} + static int fuse_fsync_backing(struct bpf_fuse_args *fa, int *out, struct file *file, loff_t start, loff_t end, int datasync) { @@ -969,6 +1179,7 @@ int fuse_bpf_fsync(int *out, struct inode *inode, struct file *file, loff_t star { return bpf_fuse_backing(inode, struct fuse_fsync_in, out, fuse_fsync_initialize_in, fuse_fsync_initialize_out, + fuse_fsync_prefilter, fuse_fsync_postfilter, fuse_fsync_backing, fuse_fsync_finalize, file, start, end, datasync); } @@ -1003,10 +1214,27 @@ static int fuse_dir_fsync_initialize_out(struct bpf_fuse_args *fa, struct fuse_f return 0; } +static int fuse_dir_fsync_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_fsync_in *in) +{ + if (ops->dir_fsync_prefilter) + return ops->fsync_prefilter(meta, in); + return BPF_FUSE_CONTINUE; +} + +static int fuse_dir_fsync_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_fsync_in *in) +{ + if (ops->dir_fsync_postfilter) + return ops->dir_fsync_postfilter(meta, in); + return BPF_FUSE_CONTINUE; +} + int fuse_bpf_dir_fsync(int *out, struct inode *inode, struct file *file, loff_t start, loff_t end, int datasync) { return bpf_fuse_backing(inode, struct fuse_fsync_in, out, fuse_dir_fsync_initialize_in, fuse_dir_fsync_initialize_out, + fuse_dir_fsync_prefilter, fuse_dir_fsync_postfilter, fuse_fsync_backing, fuse_fsync_finalize, file, start, end, datasync); } @@ -1076,6 +1304,22 @@ static int fuse_getxattr_initialize_out(struct bpf_fuse_args *fa, return 0; } +static int fuse_getxattr_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_getxattr_args *args) +{ + if (ops->getxattr_prefilter) + return ops->getxattr_prefilter(meta, &args->in, &args->name); + return BPF_FUSE_CONTINUE; +} + +static int fuse_getxattr_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_getxattr_args *args) +{ + if (ops->getxattr_postfilter) + return ops->getxattr_postfilter(meta, &args->in, &args->name, &args->value, &args->out); + return BPF_FUSE_CONTINUE; +} + static int fuse_getxattr_backing(struct bpf_fuse_args *fa, int *out, struct dentry *dentry, const char *name, void *value, size_t size) @@ -1121,6 +1365,7 @@ int fuse_bpf_getxattr(int *out, struct inode *inode, struct dentry *dentry, cons { return bpf_fuse_backing(inode, struct fuse_getxattr_args, out, fuse_getxattr_initialize_in, fuse_getxattr_initialize_out, + fuse_getxattr_prefilter, fuse_getxattr_postfilter, fuse_getxattr_backing, fuse_getxattr_finalize, dentry, name, value, size); } @@ -1173,6 +1418,22 @@ static int fuse_listxattr_initialize_out(struct bpf_fuse_args *fa, return 0; } +static int fuse_listxattr_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_getxattr_args *args) +{ + if (ops->listxattr_prefilter) + return ops->listxattr_prefilter(meta, &args->in); + return BPF_FUSE_CONTINUE; +} + +static int fuse_listxattr_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_getxattr_args *args) +{ + if (ops->listxattr_postfilter) + return ops->listxattr_postfilter(meta, &args->in, &args->value, &args->out); + return BPF_FUSE_CONTINUE; +} + static int fuse_listxattr_backing(struct bpf_fuse_args *fa, ssize_t *out, struct dentry *dentry, char *list, size_t size) { @@ -1212,6 +1473,7 @@ int fuse_bpf_listxattr(ssize_t *out, struct inode *inode, struct dentry *dentry, { return bpf_fuse_backing(inode, struct fuse_getxattr_args, out, fuse_listxattr_initialize_in, fuse_listxattr_initialize_out, + fuse_listxattr_prefilter, fuse_listxattr_postfilter, fuse_listxattr_backing, fuse_listxattr_finalize, dentry, list, size); } @@ -1277,6 +1539,22 @@ static int fuse_setxattr_initialize_out(struct bpf_fuse_args *fa, return 0; } +static int fuse_setxattr_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_setxattr_args *args) +{ + if (ops->setxattr_prefilter) + return ops->setxattr_prefilter(meta, &args->in, &args->name, &args->value); + return BPF_FUSE_CONTINUE; +} + +static int fuse_setxattr_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_setxattr_args *args) +{ + if (ops->setxattr_postfilter) + return ops->setxattr_postfilter(meta, &args->in, &args->name, &args->value); + return BPF_FUSE_CONTINUE; +} + static int fuse_setxattr_backing(struct bpf_fuse_args *fa, int *out, struct dentry *dentry, const char *name, const void *value, size_t size, int flags) @@ -1300,6 +1578,7 @@ int fuse_bpf_setxattr(int *out, struct inode *inode, struct dentry *dentry, { return bpf_fuse_backing(inode, struct fuse_setxattr_args, out, fuse_setxattr_initialize_in, fuse_setxattr_initialize_out, + fuse_setxattr_prefilter, fuse_setxattr_postfilter, fuse_setxattr_backing, fuse_setxattr_finalize, dentry, name, value, size, flags); } @@ -1336,6 +1615,22 @@ static int fuse_removexattr_initialize_out(struct bpf_fuse_args *fa, return 0; } +static int fuse_removexattr_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_buffer *in) +{ + if (ops->removexattr_prefilter) + return ops->removexattr_prefilter(meta, in); + return BPF_FUSE_CONTINUE; +} + +static int fuse_removexattr_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_buffer *in) +{ + if (ops->removexattr_postfilter) + return ops->removexattr_postfilter(meta, in); + return BPF_FUSE_CONTINUE; +} + static int fuse_removexattr_backing(struct bpf_fuse_args *fa, int *out, struct dentry *dentry, const char *name) { @@ -1356,6 +1651,7 @@ int fuse_bpf_removexattr(int *out, struct inode *inode, struct dentry *dentry, c { return bpf_fuse_backing(inode, struct fuse_buffer, out, fuse_removexattr_initialize_in, fuse_removexattr_initialize_out, + fuse_removexattr_prefilter, fuse_removexattr_postfilter, fuse_removexattr_backing, fuse_removexattr_finalize, dentry, name); } @@ -1446,6 +1742,22 @@ static int fuse_file_read_iter_initialize_out(struct bpf_fuse_args *fa, struct f return 0; } +static int fuse_file_read_iter_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_file_read_iter_args *args) +{ + if (ops->read_iter_prefilter) + return ops->read_iter_prefilter(meta, &args->in); + return BPF_FUSE_CONTINUE; +} + +static int fuse_file_read_iter_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_file_read_iter_args *args) +{ + if (ops->read_iter_postfilter) + return ops->read_iter_postfilter(meta, &args->in, &args->out); + return BPF_FUSE_CONTINUE; +} + static int fuse_file_read_iter_backing(struct bpf_fuse_args *fa, ssize_t *out, struct kiocb *iocb, struct iov_iter *to) { @@ -1513,6 +1825,8 @@ int fuse_bpf_file_read_iter(ssize_t *out, struct inode *inode, struct kiocb *ioc return bpf_fuse_backing(inode, struct fuse_file_read_iter_args, out, fuse_file_read_iter_initialize_in, fuse_file_read_iter_initialize_out, + fuse_file_read_iter_prefilter, + fuse_file_read_iter_postfilter, fuse_file_read_iter_backing, fuse_file_read_iter_finalize, iocb, to); @@ -1562,6 +1876,22 @@ static int fuse_file_write_iter_initialize_out(struct bpf_fuse_args *fa, return 0; } +static int fuse_write_iter_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_file_write_iter_args *args) +{ + if (ops->write_iter_prefilter) + return ops->write_iter_prefilter(meta, &args->in); + return BPF_FUSE_CONTINUE; +} + +static int fuse_write_iter_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_file_write_iter_args *args) +{ + if (ops->write_iter_postfilter) + return ops->write_iter_postfilter(meta, &args->in, &args->out); + return BPF_FUSE_CONTINUE; +} + static int fuse_file_write_iter_backing(struct bpf_fuse_args *fa, ssize_t *out, struct kiocb *iocb, struct iov_iter *from) { @@ -1626,6 +1956,8 @@ int fuse_bpf_file_write_iter(ssize_t *out, struct inode *inode, struct kiocb *io return bpf_fuse_backing(inode, struct fuse_file_write_iter_args, out, fuse_file_write_iter_initialize_in, fuse_file_write_iter_initialize_out, + fuse_write_iter_prefilter, + fuse_write_iter_postfilter, fuse_file_write_iter_backing, fuse_file_write_iter_finalize, iocb, from); @@ -1701,6 +2033,22 @@ static int fuse_file_fallocate_initialize_out(struct bpf_fuse_args *fa, return 0; } +static int fuse_file_fallocate_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_fallocate_in *in) +{ + if (ops->file_fallocate_prefilter) + return ops->file_fallocate_prefilter(meta, in); + return BPF_FUSE_CONTINUE; +} + +static int fuse_file_fallocate_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_fallocate_in *in) +{ + if (ops->file_fallocate_postfilter) + return ops->file_fallocate_postfilter(meta, in); + return BPF_FUSE_CONTINUE; +} + static int fuse_file_fallocate_backing(struct bpf_fuse_args *fa, int *out, struct file *file, int mode, loff_t offset, loff_t length) { @@ -1723,6 +2071,8 @@ int fuse_bpf_file_fallocate(int *out, struct inode *inode, struct file *file, in return bpf_fuse_backing(inode, struct fuse_fallocate_in, out, fuse_file_fallocate_initialize_in, fuse_file_fallocate_initialize_out, + fuse_file_fallocate_prefilter, + fuse_file_fallocate_postfilter, fuse_file_fallocate_backing, fuse_file_fallocate_finalize, file, mode, offset, length); @@ -1790,6 +2140,22 @@ static int fuse_lookup_initialize_out(struct bpf_fuse_args *fa, struct fuse_look return 0; } +static int fuse_lookup_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_lookup_args *args) +{ + if (ops->lookup_prefilter) + return ops->lookup_prefilter(meta, &args->name); + return BPF_FUSE_CONTINUE; +} + +static int fuse_lookup_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_lookup_args *args) +{ + if (ops->lookup_postfilter) + return ops->lookup_postfilter(meta, &args->name, &args->out, &args->bpf_entries); + return BPF_FUSE_CONTINUE; +} + static int fuse_lookup_backing(struct bpf_fuse_args *fa, struct dentry **out, struct inode *dir, struct dentry *entry, unsigned int flags) { @@ -1968,6 +2334,7 @@ int fuse_bpf_lookup(struct dentry **out, struct inode *dir, struct dentry *entry { return bpf_fuse_backing(dir, struct fuse_lookup_args, out, fuse_lookup_initialize_in, fuse_lookup_initialize_out, + fuse_lookup_prefilter, fuse_lookup_postfilter, fuse_lookup_backing, fuse_lookup_finalize, dir, entry, flags); } @@ -2034,6 +2401,22 @@ static int fuse_mknod_initialize_out(struct bpf_fuse_args *fa, struct fuse_mknod return 0; } +static int fuse_mknod_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_mknod_args *args) +{ + if (ops->mknod_prefilter) + return ops->mknod_prefilter(meta, &args->in, &args->name); + return BPF_FUSE_CONTINUE; +} + +static int fuse_mknod_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_mknod_args *args) +{ + if (ops->mknod_postfilter) + return ops->mknod_postfilter(meta, &args->in, &args->name); + return BPF_FUSE_CONTINUE; +} + static int fuse_mknod_backing(struct bpf_fuse_args *fa, int *out, struct inode *dir, struct dentry *entry, umode_t mode, dev_t rdev) { @@ -2086,6 +2469,7 @@ int fuse_bpf_mknod(int *out, struct inode *dir, struct dentry *entry, umode_t mo { return bpf_fuse_backing(dir, struct fuse_mknod_args, out, fuse_mknod_initialize_in, fuse_mknod_initialize_out, + fuse_mknod_prefilter, fuse_mknod_postfilter, fuse_mknod_backing, fuse_mknod_finalize, dir, entry, mode, rdev); } @@ -2187,10 +2571,27 @@ static int fuse_mkdir_finalize(struct bpf_fuse_args *fa, int *out, return 0; } +static int fuse_mkdir_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_mkdir_args *args) +{ + if (ops->mkdir_prefilter) + return ops->mkdir_prefilter(meta, &args->in, &args->name); + return BPF_FUSE_CONTINUE; +} + +static int fuse_mkdir_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_mkdir_args *args) +{ + if (ops->mkdir_prefilter) + return ops->mkdir_postfilter(meta, &args->in, &args->name); + return BPF_FUSE_CONTINUE; +} + int fuse_bpf_mkdir(int *out, struct inode *dir, struct dentry *entry, umode_t mode) { return bpf_fuse_backing(dir, struct fuse_mkdir_args, out, fuse_mkdir_initialize_in, fuse_mkdir_initialize_out, + fuse_mkdir_prefilter, fuse_mkdir_postfilter, fuse_mkdir_backing, fuse_mkdir_finalize, dir, entry, mode); } @@ -2224,6 +2625,22 @@ static int fuse_rmdir_initialize_out(struct bpf_fuse_args *fa, struct fuse_buffe return 0; } +static int fuse_rmdir_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_buffer *name) +{ + if (ops->rmdir_prefilter) + return ops->rmdir_prefilter(meta, name); + return BPF_FUSE_CONTINUE; +} + +static int fuse_rmdir_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_buffer *name) +{ + if (ops->rmdir_postfilter) + return ops->rmdir_postfilter(meta, name); + return BPF_FUSE_CONTINUE; +} + static int fuse_rmdir_backing(struct bpf_fuse_args *fa, int *out, struct inode *dir, struct dentry *entry) { @@ -2258,6 +2675,7 @@ int fuse_bpf_rmdir(int *out, struct inode *dir, struct dentry *entry) { return bpf_fuse_backing(dir, struct fuse_buffer, out, fuse_rmdir_initialize_in, fuse_rmdir_initialize_out, + fuse_rmdir_prefilter, fuse_rmdir_postfilter, fuse_rmdir_backing, fuse_rmdir_finalize, dir, entry); } @@ -2400,6 +2818,22 @@ static int fuse_rename2_initialize_out(struct bpf_fuse_args *fa, struct fuse_ren return 0; } +static int fuse_rename2_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_rename2_args *args) +{ + if (ops->rename2_prefilter) + return ops->rename2_prefilter(meta, &args->in, &args->old_name, &args->new_name); + return BPF_FUSE_CONTINUE; +} + +static int fuse_rename2_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_rename2_args *args) +{ + if (ops->rename2_postfilter) + return ops->rename2_postfilter(meta, &args->in, &args->old_name, &args->new_name); + return BPF_FUSE_CONTINUE; +} + static int fuse_rename2_backing(struct bpf_fuse_args *fa, int *out, struct inode *olddir, struct dentry *oldent, struct inode *newdir, struct dentry *newent, @@ -2427,6 +2861,7 @@ int fuse_bpf_rename2(int *out, struct inode *olddir, struct dentry *oldent, { return bpf_fuse_backing(olddir, struct fuse_rename2_args, out, fuse_rename2_initialize_in, fuse_rename2_initialize_out, + fuse_rename2_prefilter, fuse_rename2_postfilter, fuse_rename2_backing, fuse_rename2_finalize, olddir, oldent, newdir, newent, flags); } @@ -2487,6 +2922,22 @@ static int fuse_rename_initialize_out(struct bpf_fuse_args *fa, struct fuse_rena return 0; } +static int fuse_rename_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_rename_args *args) +{ + if (ops->rename_prefilter) + return ops->rename_prefilter(meta, &args->in, &args->old_name, &args->new_name); + return BPF_FUSE_CONTINUE; +} + +static int fuse_rename_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_rename_args *args) +{ + if (ops->rename_postfilter) + return ops->rename_postfilter(meta, &args->in, &args->old_name, &args->new_name); + return BPF_FUSE_CONTINUE; +} + static int fuse_rename_backing(struct bpf_fuse_args *fa, int *out, struct inode *olddir, struct dentry *oldent, struct inode *newdir, struct dentry *newent) @@ -2508,6 +2959,7 @@ int fuse_bpf_rename(int *out, struct inode *olddir, struct dentry *oldent, { return bpf_fuse_backing(olddir, struct fuse_rename_args, out, fuse_rename_initialize_in, fuse_rename_initialize_out, + fuse_rename_prefilter, fuse_rename_postfilter, fuse_rename_backing, fuse_rename_finalize, olddir, oldent, newdir, newent); } @@ -2541,6 +2993,22 @@ static int fuse_unlink_initialize_out(struct bpf_fuse_args *fa, struct fuse_buff return 0; } +static int fuse_unlink_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_buffer *name) +{ + if (ops->unlink_prefilter) + return ops->unlink_prefilter(meta, name); + return BPF_FUSE_CONTINUE; +} + +static int fuse_unlink_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_buffer *name) +{ + if (ops->unlink_postfilter) + return ops->unlink_postfilter(meta, name); + return BPF_FUSE_CONTINUE; +} + static int fuse_unlink_backing(struct bpf_fuse_args *fa, int *out, struct inode *dir, struct dentry *entry) { struct path backing_path; @@ -2577,6 +3045,7 @@ int fuse_bpf_unlink(int *out, struct inode *dir, struct dentry *entry) { return bpf_fuse_backing(dir, struct fuse_buffer, out, fuse_unlink_initialize_in, fuse_unlink_initialize_out, + fuse_unlink_prefilter, fuse_unlink_postfilter, fuse_unlink_backing, fuse_unlink_finalize, dir, entry); } @@ -2629,6 +3098,23 @@ static int fuse_link_initialize_out(struct bpf_fuse_args *fa, struct fuse_link_a return 0; } +static int fuse_link_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_link_args *args) +{ + if (ops->link_prefilter) + return ops->link_prefilter(meta, &args->in, &args->name); + return BPF_FUSE_CONTINUE; +} + +static int fuse_link_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_link_args *args) +{ + if (ops->link_postfilter) + return ops->link_postfilter(meta, &args->in, &args->name); + return BPF_FUSE_CONTINUE; +} + + static int fuse_link_backing(struct bpf_fuse_args *fa, int *out, struct dentry *entry, struct inode *dir, struct dentry *newent) { @@ -2696,6 +3182,7 @@ int fuse_bpf_link(int *out, struct inode *inode, struct dentry *entry, { return bpf_fuse_backing(inode, struct fuse_link_args, out, fuse_link_initialize_in, fuse_link_initialize_out, + fuse_link_prefilter, fuse_link_postfilter, fuse_link_backing, fuse_link_finalize, entry, newdir, newent); } @@ -2744,6 +3231,22 @@ static int fuse_getattr_initialize_out(struct bpf_fuse_args *fa, struct fuse_get return 0; } +static int fuse_getattr_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_getattr_args *args) +{ + if (ops->getattr_prefilter) + return ops->getattr_prefilter(meta, &args->in); + return BPF_FUSE_CONTINUE; +} + +static int fuse_getattr_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_getattr_args *args) +{ + if (ops->getattr_postfilter) + return ops->getattr_postfilter(meta, &args->in, &args->out); + return BPF_FUSE_CONTINUE; +} + static int fuse_getattr_backing(struct bpf_fuse_args *fa, int *out, const struct dentry *entry, struct kstat *stat, u32 request_mask, unsigned int flags) @@ -2804,6 +3307,7 @@ int fuse_bpf_getattr(int *out, struct inode *inode, const struct dentry *entry, { return bpf_fuse_backing(inode, struct fuse_getattr_args, out, fuse_getattr_initialize_in, fuse_getattr_initialize_out, + fuse_getattr_prefilter, fuse_getattr_postfilter, fuse_getattr_backing, fuse_getattr_finalize, entry, stat, request_mask, flags); } @@ -2883,6 +3387,22 @@ static int fuse_setattr_initialize_out(struct bpf_fuse_args *fa, struct fuse_set return 0; } +static int fuse_setattr_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_setattr_args *args) +{ + if (ops->setattr_prefilter) + return ops->setattr_prefilter(meta, &args->in); + return BPF_FUSE_CONTINUE; +} + +static int fuse_setattr_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_setattr_args *args) +{ + if (ops->setattr_postfilter) + return ops->setattr_postfilter(meta, &args->in, &args->out); + return BPF_FUSE_CONTINUE; +} + static int fuse_setattr_backing(struct bpf_fuse_args *fa, int *out, struct dentry *dentry, struct iattr *attr, struct file *file) { @@ -2920,6 +3440,7 @@ int fuse_bpf_setattr(int *out, struct inode *inode, struct dentry *dentry, struc { return bpf_fuse_backing(inode, struct fuse_setattr_args, out, fuse_setattr_initialize_in, fuse_setattr_initialize_out, + fuse_setattr_prefilter, fuse_setattr_postfilter, fuse_setattr_backing, fuse_setattr_finalize, dentry, attr, file); } @@ -2949,6 +3470,22 @@ static int fuse_statfs_initialize_out(struct bpf_fuse_args *fa, struct fuse_stat return 0; } +static int fuse_statfs_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_statfs_out *out) +{ + if (ops->statfs_prefilter) + return ops->statfs_prefilter(meta); + return BPF_FUSE_CONTINUE; +} + +static int fuse_statfs_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_statfs_out *out) +{ + if (ops->statfs_postfilter) + return ops->statfs_postfilter(meta, out); + return BPF_FUSE_CONTINUE; +} + static int fuse_statfs_backing(struct bpf_fuse_args *fa, int *out, struct dentry *dentry, struct kstatfs *buf) { @@ -2984,6 +3521,7 @@ int fuse_bpf_statfs(int *out, struct inode *inode, struct dentry *dentry, struct { return bpf_fuse_backing(dentry->d_inode, struct fuse_statfs_out, out, fuse_statfs_initialize_in, fuse_statfs_initialize_out, + fuse_statfs_prefilter, fuse_statfs_postfilter, fuse_statfs_backing, fuse_statfs_finalize, dentry, buf); } @@ -3053,6 +3591,22 @@ static int fuse_get_link_initialize_out(struct bpf_fuse_args *fa, struct fuse_ge return 0; } +static int fuse_get_link_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_get_link_args *args) +{ + if (ops->get_link_prefilter) + return ops->get_link_prefilter(meta, &args->name); + return BPF_FUSE_CONTINUE; +} + +static int fuse_get_link_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_get_link_args *args) +{ + if (ops->get_link_postfilter) + return ops->get_link_postfilter(meta, &args->name); + return BPF_FUSE_CONTINUE; +} + static int fuse_get_link_backing(struct bpf_fuse_args *fa, const char **out, struct inode *inode, struct dentry *dentry, struct delayed_call *callback) @@ -3092,6 +3646,7 @@ int fuse_bpf_get_link(const char **out, struct inode *inode, struct dentry *dent { return bpf_fuse_backing(inode, struct fuse_get_link_args, out, fuse_get_link_initialize_in, fuse_get_link_initialize_out, + fuse_get_link_prefilter, fuse_get_link_postfilter, fuse_get_link_backing, fuse_get_link_finalize, inode, dentry, callback); } @@ -3142,6 +3697,22 @@ static int fuse_symlink_initialize_out(struct bpf_fuse_args *fa, struct fuse_sym return 0; } +static int fuse_symlink_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_symlink_args *args) +{ + if (ops->symlink_prefilter) + return ops->symlink_prefilter(meta, &args->name, &args->path); + return BPF_FUSE_CONTINUE; +} + +static int fuse_symlink_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_symlink_args *args) +{ + if (ops->symlink_postfilter) + return ops->symlink_postfilter(meta, &args->name, &args->path); + return BPF_FUSE_CONTINUE; +} + static int fuse_symlink_backing(struct bpf_fuse_args *fa, int *out, struct inode *dir, struct dentry *entry, const char *link, int len) { @@ -3192,6 +3763,7 @@ int fuse_bpf_symlink(int *out, struct inode *dir, struct dentry *entry, const c { return bpf_fuse_backing(dir, struct fuse_symlink_args, out, fuse_symlink_initialize_in, fuse_symlink_initialize_out, + fuse_symlink_prefilter, fuse_symlink_postfilter, fuse_symlink_backing, fuse_symlink_finalize, dir, entry, link, len); } @@ -3265,6 +3837,22 @@ static int fuse_readdir_initialize_out(struct bpf_fuse_args *fa, struct fuse_rea return 0; } +static int fuse_readdir_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_read_args *args) +{ + if (ops->readdir_prefilter) + return ops->readdir_prefilter(meta, &args->in); + return BPF_FUSE_CONTINUE; +} + +static int fuse_readdir_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_read_args *args) +{ + if (ops->readdir_postfilter) + return ops->readdir_postfilter(meta, &args->in, &args->out, &args->buffer); + return BPF_FUSE_CONTINUE; +} + struct fusebpf_ctx { struct dir_context ctx; u8 *addr; @@ -3380,6 +3968,7 @@ int fuse_bpf_readdir(int *out, struct inode *inode, struct file *file, struct di again: ret = bpf_fuse_backing(inode, struct fuse_read_args, out, fuse_readdir_initialize_in, fuse_readdir_initialize_out, + fuse_readdir_prefilter, fuse_readdir_postfilter, fuse_readdir_backing, fuse_readdir_finalize, file, ctx, &force_again, &allow_force, is_continued); if (force_again && *out >= 0) { @@ -3416,6 +4005,22 @@ static int fuse_access_initialize_out(struct bpf_fuse_args *fa, struct fuse_acce return 0; } +static int fuse_access_prefilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_access_in *in) +{ + if (ops->access_prefilter) + return ops->access_prefilter(meta, in); + return BPF_FUSE_CONTINUE; +} + +static int fuse_access_postfilter(struct fuse_ops *ops, struct bpf_fuse_meta_info *meta, + struct fuse_access_in *in) +{ + if (ops->access_postfilter) + return ops->access_postfilter(meta, in); + return BPF_FUSE_CONTINUE; +} + static int fuse_access_backing(struct bpf_fuse_args *fa, int *out, struct inode *inode, int mask) { struct fuse_inode *fi = get_fuse_inode(inode); @@ -3434,6 +4039,7 @@ int fuse_bpf_access(int *out, struct inode *inode, int mask) { return bpf_fuse_backing(inode, struct fuse_access_in, out, fuse_access_initialize_in, fuse_access_initialize_out, + fuse_access_prefilter, fuse_access_postfilter, fuse_access_backing, fuse_access_finalize, inode, mask); } From patchwork Tue Apr 18 01:40:33 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13215002 X-Patchwork-Delegate: bpf@iogearbox.net 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 957F2C7EE20 for ; Tue, 18 Apr 2023 01:45:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231287AbjDRBps (ORCPT ); Mon, 17 Apr 2023 21:45:48 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54376 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231282AbjDRBpN (ORCPT ); Mon, 17 Apr 2023 21:45:13 -0400 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 C64DF9EE2 for ; Mon, 17 Apr 2023 18:42:40 -0700 (PDT) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-552e3233b26so32079957b3.9 for ; Mon, 17 Apr 2023 18:42:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782125; x=1684374125; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=K9AGDYeuciPVrN915Qcuh9H5YY4BpsMk+FO3fkMyYec=; b=VrPrsw2bmIPnUEb2XN3ynu2DfaIBg5kjX6yHgZ+daQdf4ahQzkoZoDDJ/j3zK9mhn0 ihY05K+k0Wdzb+pobv2yPCs/mtGZLkjsBIBCNie6c+RAIF2PN2A4TYaV+awyzNs/97JO 4QvTBNjWrWQld0yZckQ8YWsYK/0ESvJMFy0QKQuc1LZA3usE9Eik7oeDIO2SD9GAEjlV b6Byxc+kS4ICk2MND+KAZIzsj03hdQU2XOmxLL8wMOJdjLkHLRiM72bO2UEsaoU33HAw BETE2SUeDZaNZOTmoWT9KJSdOYkkwz4mCp2S23gxi4Ts1cpkx4bIb6ox0s83mhudTU9Z RX2w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782125; x=1684374125; 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=K9AGDYeuciPVrN915Qcuh9H5YY4BpsMk+FO3fkMyYec=; b=ehOzSjBymzLbBdyJAkpVwKPgZWwhLPXPAZRg8ab4lJFwhAcLjJg/g6TLQIp61K8iMC gus8O/0a57A86slMqvCuQt4F3gczY6ht5N5c/Okm0WTkJBXantQ5P8ljd3iTzKm9CE6Y QbD19WmD/pHZAbArtu793FbrT5xGdAjhdXq2+qPnmJ9ZUCFnUBK+ko/Gg27gjTWagYdK all0yHMfm2BdjYCI/OADkjBok8W92fwSfsW87eZku45Ex1E0ntfFFtEqoaK9sXu2+7T3 uZP756vArOqIDFZbZk+wd6rLWFJ8PprrlvQJ232WcuEV7/7Owa9P2N3rhZKr1UzuiL1U QhUA== X-Gm-Message-State: AAQBX9dSkw4VC+U64WZp9i4ZAzWggdvLReBVVU8UcxkwDLyTQeOer9L/ Jrapsm4KaTEKUsFRXCCl3LhnnrBUkWI= X-Google-Smtp-Source: AKy350ZLirn3I/BrcVzO0YqPwNPD1HbdzOUcnYLbOs6JaJ1QgTQX8WAFlA2B666sWoEpeNpU9WPT5G7v98g= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a05:690c:d91:b0:54f:e88d:79ba with SMTP id da17-20020a05690c0d9100b0054fe88d79bamr9715005ywb.5.1681782125267; Mon, 17 Apr 2023 18:42:05 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:33 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-34-drosen@google.com> Subject: [RFC PATCH v3 33/37] fuse-bpf: Add userspace pre/post filters From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , kernel-team@android.com, Daniel Rosenberg Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net X-Patchwork-State: RFC This allows fuse-bpf to call out to userspace to handle pre and post filters. Any of the inputs may be changed by the prefilter, so we must handle up to 3 outputs. For the postfilter, our inputs include the output arguments, so we must handle up to 5 inputs. Additionally, we add an extension for passing the return code of the backing call to the postfilter, adding one additional possible output bringing the total to 4. As long as you don't request both pre-filter and post-filter in userspace, we will end up doing fewer round trips to userspace. Signed-off-by: Daniel Rosenberg --- fs/fuse/backing.c | 179 ++++++++++++++++++++++++++++++++++++++ fs/fuse/dev.c | 2 + fs/fuse/dir.c | 6 +- fs/fuse/fuse_i.h | 33 ++++++- include/linux/bpf_fuse.h | 1 + include/uapi/linux/fuse.h | 1 + 6 files changed, 217 insertions(+), 5 deletions(-) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index 9217e9f83d98..1de302fc91b6 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -14,6 +14,163 @@ #include #include +static void set_in_args(struct fuse_in_arg *dst, struct bpf_fuse_arg *src) +{ + if (src->is_buffer) { + struct fuse_buffer *buffer = src->buffer; + + *dst = (struct fuse_in_arg) { + .size = buffer->size, + .value = buffer->data, + }; + } else { + *dst = (struct fuse_in_arg) { + .size = src->size, + .value = src->value, + }; + } +} + +static void set_out_args(struct fuse_arg *dst, struct bpf_fuse_arg *src) +{ + if (src->is_buffer) { + struct fuse_buffer *buffer = src->buffer; + + // Userspace out args presents as much space as needed + *dst = (struct fuse_arg) { + .size = buffer->max_size, + .value = buffer->data, + }; + } else { + *dst = (struct fuse_arg) { + .size = src->size, + .value = src->value, + }; + } +} + +static int get_err_in(uint32_t error, struct fuse_in_arg *ext) +{ + struct fuse_ext_header *xh; + uint32_t *err_in; + uint32_t err_in_size = fuse_ext_size(sizeof(*err_in)); + + xh = extend_arg(ext, err_in_size); + if (!xh) + return -ENOMEM; + xh->size = err_in_size; + xh->type = FUSE_ERROR_IN; + + err_in = (uint32_t *)&xh[1]; + *err_in = error; + return 0; +} + +static int get_filter_ext(struct fuse_args *args) +{ + struct fuse_in_arg ext = { .size = 0, .value = NULL }; + int err = 0; + + if (args->is_filter) + err = get_err_in(args->error_in, &ext); + if (!err && ext.size) { + WARN_ON(args->in_numargs >= ARRAY_SIZE(args->in_args)); + args->is_ext = true; + args->ext_idx = args->in_numargs++; + args->in_args[args->ext_idx] = ext; + } else { + kfree(ext.value); + } + return err; +} + +static ssize_t fuse_bpf_simple_request(struct fuse_mount *fm, struct bpf_fuse_args *fa, + unsigned short in_numargs, unsigned short out_numargs, + struct bpf_fuse_arg *out_arg_array, bool add_out_to_in) +{ + int i; + ssize_t res; + + struct fuse_args args = { + .nodeid = fa->info.nodeid, + .opcode = fa->info.opcode, + .error_in = fa->info.error_in, + .in_numargs = in_numargs, + .out_numargs = out_numargs, + .force = !!(fa->flags & FUSE_BPF_FORCE), + .out_argvar = !!(fa->flags & FUSE_BPF_OUT_ARGVAR), + .is_lookup = !!(fa->flags & FUSE_BPF_IS_LOOKUP), + .is_filter = true, + }; + + /* All out args must be writeable */ + for (i = 0; i < out_numargs; ++i) { + struct fuse_buffer *buffer; + + if (!out_arg_array[i].is_buffer) + continue; + buffer = out_arg_array[i].buffer; + if (!bpf_fuse_get_writeable(buffer, buffer->max_size, true)) + return -ENOMEM; + } + + /* Set in args */ + for (i = 0; i < fa->in_numargs; ++i) + set_in_args(&args.in_args[i], &fa->in_args[i]); + if (add_out_to_in) { + for (i = 0; i < fa->out_numargs; ++i) { + set_in_args(&args.in_args[fa->in_numargs + i], &fa->out_args[i]); + } + } + + /* Set out args */ + for (i = 0; i < out_numargs; ++i) + set_out_args(&args.out_args[i], &out_arg_array[i]); + + if (out_arg_array[out_numargs - 1].is_buffer) { + struct fuse_buffer *buff = out_arg_array[out_numargs - 1].buffer; + + if (buff->flags & BPF_FUSE_VARIABLE_SIZE) + args.out_argvar = true; + } + if (add_out_to_in) { + res = get_filter_ext(&args); + if (res) + return res; + } + res = fuse_simple_request(fm, &args); + + /* update used areas of buffers */ + for (i = 0; i < out_numargs; ++i) + if (out_arg_array[i].is_buffer && + (out_arg_array[i].buffer->flags & BPF_FUSE_VARIABLE_SIZE)) + out_arg_array[i].buffer->size = args.out_args[i].size; + fa->ret = args.ret; + + free_ext_value(&args); + + return res; +} + +static ssize_t fuse_prefilter_simple_request(struct fuse_mount *fm, struct bpf_fuse_args *fa) +{ + uint32_t out_args = fa->in_numargs; + + // mkdir and company are not permitted to change the name. This should be done at lookup + // Thus, these can't be set by the userspace prefilter + if (fa->in_args[fa->in_numargs - 1].is_buffer && + (fa->in_args[fa->in_numargs - 1].buffer->flags & BPF_FUSE_IMMUTABLE)) + out_args--; + return fuse_bpf_simple_request(fm, fa, fa->in_numargs, out_args, + fa->in_args, false); +} + +static ssize_t fuse_postfilter_simple_request(struct fuse_mount *fm, struct bpf_fuse_args *fa) +{ + return fuse_bpf_simple_request(fm, fa, fa->in_numargs + fa->out_numargs, fa->out_numargs, + fa->out_args, true); +} + static inline void bpf_fuse_set_in_immutable(struct bpf_fuse_args *fa) { int i; @@ -60,9 +217,11 @@ static inline void bpf_fuse_free_alloced(struct bpf_fuse_args *fa) ({ \ struct fuse_inode *fuse_inode = get_fuse_inode(inode); \ struct fuse_ops *fuse_ops = fuse_inode->bpf_ops; \ + struct fuse_mount *fm = get_fuse_mount(inode); \ struct bpf_fuse_args fa = { 0 }; \ bool initialized = false; \ bool handled = false; \ + bool locked; \ ssize_t res; \ int bpf_next; \ io feo = { 0 }; \ @@ -88,6 +247,16 @@ static inline void bpf_fuse_free_alloced(struct bpf_fuse_args *fa) break; \ } \ \ + if (bpf_next == BPF_FUSE_USER_PREFILTER) { \ + locked = fuse_lock_inode(inode); \ + res = fuse_prefilter_simple_request(fm, &fa); \ + fuse_unlock_inode(inode, locked); \ + if (res < 0) { \ + error = res; \ + break; \ + } \ + bpf_next = fa.ret; \ + } \ bpf_fuse_set_in_immutable(&fa); \ \ error = initialize_out(&fa, &feo, args); \ @@ -117,6 +286,16 @@ static inline void bpf_fuse_free_alloced(struct bpf_fuse_args *fa) break; \ } \ \ + if (!(bpf_next == BPF_FUSE_USER_POSTFILTER)) \ + break; \ + \ + locked = fuse_lock_inode(inode); \ + res = fuse_postfilter_simple_request(fm, &fa); \ + fuse_unlock_inode(inode, locked); \ + if (res < 0) { \ + error = res; \ + break; \ + } \ } while (false); \ \ if (initialized && handled) { \ diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index ad7d9d1e6da5..139f40b70228 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -521,6 +521,8 @@ ssize_t fuse_simple_request(struct fuse_mount *fm, struct fuse_args *args) BUG_ON(args->out_numargs == 0); ret = args->out_args[args->out_numargs - 1].size; } + if (args->is_filter && args->is_ext) + args->ret = req->out.h.error; fuse_put_request(req); return ret; diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index b7bc8260a537..bea5f1698127 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -620,7 +620,7 @@ static int get_security_context(struct dentry *entry, umode_t mode, return err; } -static void *extend_arg(struct fuse_in_arg *buf, u32 bytes) +void *extend_arg(struct fuse_in_arg *buf, u32 bytes) { void *p; u32 newlen = buf->size + bytes; @@ -640,7 +640,7 @@ static void *extend_arg(struct fuse_in_arg *buf, u32 bytes) return p + newlen - bytes; } -static u32 fuse_ext_size(size_t size) +u32 fuse_ext_size(size_t size) { return FUSE_REC_ALIGN(sizeof(struct fuse_ext_header) + size); } @@ -700,7 +700,7 @@ static int get_create_ext(struct fuse_args *args, return err; } -static void free_ext_value(struct fuse_args *args) +void free_ext_value(struct fuse_args *args) { if (args->is_ext) kfree(args->in_args[args->ext_idx].value); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 15962ab3b381..0504c136632d 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -304,6 +304,17 @@ struct fuse_page_desc { unsigned int offset; }; +/* To deal with bpf pre and post filters in userspace calls, we must support + * passing the inputs and outputs as inputs, and we must have enough space in + * outputs to handle all of the inputs. Plus one more for extensions. + */ +#define FUSE_EXTENDED_MAX_ARGS_IN (FUSE_MAX_ARGS_IN + FUSE_MAX_ARGS_OUT + 1) +#if FUSE_MAX_ARGS_IN > FUSE_MAX_ARGS_OUT +#define FUSE_EXTENDED_MAX_ARGS_OUT FUSE_MAX_ARGS_IN +#else +#define FUSE_EXTENDED_MAX_ARGS_OUT FUSE_MAX_ARGS_OUT +#endif + struct fuse_args { uint64_t nodeid; uint32_t opcode; @@ -322,10 +333,12 @@ struct fuse_args { bool page_replace:1; bool may_block:1; bool is_ext:1; + bool is_filter:1; bool is_lookup:1; bool via_ioctl:1; - struct fuse_in_arg in_args[3]; - struct fuse_arg out_args[2]; + uint32_t ret; + struct fuse_in_arg in_args[FUSE_EXTENDED_MAX_ARGS_IN]; + struct fuse_arg out_args[FUSE_EXTENDED_MAX_ARGS_OUT]; void (*end)(struct fuse_mount *fm, struct fuse_args *args, int error); }; @@ -1165,6 +1178,22 @@ void fuse_request_end(struct fuse_req *req); void fuse_abort_conn(struct fuse_conn *fc); void fuse_wait_aborted(struct fuse_conn *fc); +/** + * Allocated/Reallocate extended header information + * Returns pointer to start of most recent allocation + */ +void *extend_arg(struct fuse_in_arg *buf, u32 bytes); + +/** + * Returns adjusted size field for extensions + */ +u32 fuse_ext_size(size_t size); + +/** + * Free allocated extended header information + */ +void free_ext_value(struct fuse_args *args); + /** * Invalidate inode attributes */ diff --git a/include/linux/bpf_fuse.h b/include/linux/bpf_fuse.h index 2183a7a45c92..159b850e1b46 100644 --- a/include/linux/bpf_fuse.h +++ b/include/linux/bpf_fuse.h @@ -64,6 +64,7 @@ struct bpf_fuse_args { uint32_t in_numargs; uint32_t out_numargs; uint32_t flags; + uint32_t ret; struct bpf_fuse_arg in_args[FUSE_MAX_ARGS_IN]; struct bpf_fuse_arg out_args[FUSE_MAX_ARGS_OUT]; }; diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index e779064f5fad..bbcda421ee8e 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -520,6 +520,7 @@ enum fuse_ext_type { /* Types 0..31 are reserved for fuse_secctx_header */ FUSE_MAX_NR_SECCTX = 31, FUSE_EXT_GROUPS = 32, + FUSE_ERROR_IN = 33, }; enum fuse_opcode { From patchwork Tue Apr 18 01:40:34 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214994 X-Patchwork-Delegate: bpf@iogearbox.net 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 12CF4C77B7E for ; Tue, 18 Apr 2023 01:44:32 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230489AbjDRBoa (ORCPT ); Mon, 17 Apr 2023 21:44:30 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53884 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230229AbjDRBn2 (ORCPT ); Mon, 17 Apr 2023 21:43:28 -0400 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 C045C8687 for ; Mon, 17 Apr 2023 18:42:07 -0700 (PDT) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-54be7584b28so368347317b3.16 for ; Mon, 17 Apr 2023 18:42:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782127; x=1684374127; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=ddXTLGLPlq5L3n42BI32GqoM9UbKxCD+81BzG53VpiQ=; b=0MA+38wAROiLJwnnmkOR/8zmb/14kIavQqPqw5eB3G6C7GmifVtBaTDn3Wsfybkp37 b6dSdIwLTyNCmW3wmimtWhAUVTIK1yom6ecNpdWCmuMN8VINN/r4G1jXgbOY4cu+cZ+a Pt2r2hp2yE4x1rZokntaJd0+V/IAOiTEqK0lvMYUT9NoXy+nusYQTLGzsc6+78Z99LtC SjL6n9ZideGEO833c88L+9rdxIqF491lV1zcvATg2XLJ7T64aoqdeqgUJwGQDDFLOtda 4vQypY8y9YjForGSQpK/s/rOXTF8rdgpZ+X4T737iBV7hJiGed/JtsKQjYrD8ZaDlk5T P0sw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782127; x=1684374127; 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=ddXTLGLPlq5L3n42BI32GqoM9UbKxCD+81BzG53VpiQ=; b=MEt2vG8TlqsoQJgEdELoaedn3iG5cbJfijrTqSMTe4iZLrEX7FcLL/atx0HvWzLs/N Y7YY9dVDGtXi4+cq6Ue0A5GiSlXx/TgLXMtYxfWLFEIdgq57PUKkHXUfRSkpq2lWETi6 dzJT8hNlzTPLMJyRM8MEW0UYISE2g29HzKmOa1Rge0GISFUFhyV3qFdG6OcgjTmS8FG0 iT0feEjSc5MQNn5vULorPGQKb0FkWv2bw+4p0NMkoWeYbnE5wfPRjauxtAKIsUaebB5/ BOeGs/xLReFzJZ3PDDjIqIq/SA8DI+LMy+whgPFtxi9gF9bfua0k4QDev4FluFDUdT2j /VlQ== X-Gm-Message-State: AAQBX9dyDp0rdZqNVdjDnLJBWNc31uu9uAx3ZUtxp54PAp9fuzjdnSTP qfrPXYWwkAE5RUWH93g7cG0Zwl/8LRs= X-Google-Smtp-Source: AKy350bwpQo+izJgr59DpjqCYtbKMquWrfrRxB2SDqJN3j5Kmuer+Tdqg9Aw5PoVXSidCg0l3wDXvhOOkII= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a81:4415:0:b0:54f:9718:1d39 with SMTP id r21-20020a814415000000b0054f97181d39mr10593880ywa.0.1681782127537; Mon, 17 Apr 2023 18:42:07 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:34 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-35-drosen@google.com> Subject: [RFC PATCH v3 34/37] WIP: fuse-bpf: add error_out From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , kernel-team@android.com, Daniel Rosenberg Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net X-Patchwork-State: RFC error_out field will allow differentiating between altering error code from bpf programs, and the bpf program returning an error. TODO Signed-off-by: Daniel Rosenberg --- include/linux/bpf_fuse.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/bpf_fuse.h b/include/linux/bpf_fuse.h index 159b850e1b46..15646ba59c41 100644 --- a/include/linux/bpf_fuse.h +++ b/include/linux/bpf_fuse.h @@ -57,6 +57,7 @@ struct bpf_fuse_meta_info { uint64_t nodeid; uint32_t opcode; uint32_t error_in; + uint32_t error_out; // TODO: struct_op programs may set this to alter reported error code }; struct bpf_fuse_args { From patchwork Tue Apr 18 01:40:35 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13215004 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 20CD8C77B7C for ; Tue, 18 Apr 2023 01:46:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231360AbjDRBqN (ORCPT ); Mon, 17 Apr 2023 21:46:13 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53706 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231181AbjDRBp2 (ORCPT ); Mon, 17 Apr 2023 21:45:28 -0400 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 7C5E2A244 for ; Mon, 17 Apr 2023 18:42:45 -0700 (PDT) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-54f8b46f399so159478407b3.10 for ; Mon, 17 Apr 2023 18:42:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782130; x=1684374130; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=sonv2s4tEj+40/A7AfoN8AqUe0dfHfI4UdTNmRzEFxw=; b=oBeVq5vPwvXmVXOF0/baapps028QmRMh+Vlm0GWkzmw+jgbsqFHQ9sBTAZd0JvZW7H scU0JaIRxGobib6u6o+5zkjMzh3ppHGop73In0Ks/95MOV9IySs+APkuaui0dDeA7Xs2 TNvxJEoPS5uUE3SKbFMDmqLPAzAN+EJZX72saESajrud3lyX+7DUAurdWqlQrzXD/2NP SeN8VkGxabld1EkHRBpg69tL/kTM+CsY5ugSdmrcY5bWD/9zwLIU70IlmO0rvJiKt9Ta x1P81M1rWJhi4F9P7KBAyNmIkaqe9wFJAvJZLcBnygwrhgMV8cacNucqUlI87UDqlhWR dDxw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782130; x=1684374130; 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=sonv2s4tEj+40/A7AfoN8AqUe0dfHfI4UdTNmRzEFxw=; b=E1A6BZsG5cbmm0RuA2JhPBDmKpwnZDAyk5MHrEA3zGd4GJcFNcA8VQCuP1bQIPflrm GEHG4khTSUP15pnt3J3kC5K6/rRE3zGLjIljVkqiqch4Cymw5DR1OpHd9QvRbSVf05Wg uQpPnr1wF0U53bkbVVP2HJwb5UONv975wfvI8leAsFcqKi/xjY1oyvY/7il8qFms6+w6 58j3A0myQMQxo01hWRUA/ym6bBe1nD12HF0MPnrb3S32+mOSx+UZNbvJY18Q52AGRGDy jkWtfzNV8MWOhcPrR683mKnjg6LxMFCzGe+RWA0eX1SHGAGBzD0tFrlluguaOdhQ6mVV 2wzQ== X-Gm-Message-State: AAQBX9eR3hohO14lwOJULH1W3vwtrY1LfSGLmxD/FGM+tlYvvjxVECU1 5DTQoalsu/7XPZxSBraKbvgKowSnvAI= X-Google-Smtp-Source: AKy350atgbwvzcoS6D9krUGkQk5hMb9O8+MnqZi16WWjaHH6yiJdYPqnKXAdzvHYzUemaFFJFZgGkpCmwCw= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a25:d40f:0:b0:b8c:ad5:6b4e with SMTP id m15-20020a25d40f000000b00b8c0ad56b4emr10977247ybf.12.1681782129901; Mon, 17 Apr 2023 18:42:09 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:35 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-36-drosen@google.com> Subject: [RFC PATCH v3 35/37] tools: Add FUSE, update bpf includes From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , kernel-team@android.com, Daniel Rosenberg Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC Updates the bpf includes under tools, and adds fuse Signed-off-by: Daniel Rosenberg --- tools/include/uapi/linux/bpf.h | 12 + tools/include/uapi/linux/fuse.h | 1135 +++++++++++++++++++++++++++++++ 2 files changed, 1147 insertions(+) create mode 100644 tools/include/uapi/linux/fuse.h diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 4b20a7269bee..6521c40875c7 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -7155,4 +7155,16 @@ struct bpf_iter_num { __u64 __opaque[1]; } __attribute__((aligned(8))); +/* Return Codes for Fuse BPF struct_op programs */ +#define BPF_FUSE_CONTINUE 0 +#define BPF_FUSE_USER 1 +#define BPF_FUSE_USER_PREFILTER 2 +#define BPF_FUSE_POSTFILTER 3 +#define BPF_FUSE_USER_POSTFILTER 4 + +/* Op Code Filter values for BPF Programs */ +#define FUSE_OPCODE_FILTER 0x0ffff +#define FUSE_PREFILTER 0x10000 +#define FUSE_POSTFILTER 0x20000 + #endif /* _UAPI__LINUX_BPF_H__ */ diff --git a/tools/include/uapi/linux/fuse.h b/tools/include/uapi/linux/fuse.h new file mode 100644 index 000000000000..72c2190a1b0a --- /dev/null +++ b/tools/include/uapi/linux/fuse.h @@ -0,0 +1,1135 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) */ +/* + This file defines the kernel interface of FUSE + Copyright (C) 2001-2008 Miklos Szeredi + + This program can be distributed under the terms of the GNU GPL. + See the file COPYING. + + This -- and only this -- header file may also be distributed under + the terms of the BSD Licence as follows: + + Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ + +/* + * This file defines the kernel interface of FUSE + * + * Protocol changelog: + * + * 7.1: + * - add the following messages: + * FUSE_SETATTR, FUSE_SYMLINK, FUSE_MKNOD, FUSE_MKDIR, FUSE_UNLINK, + * FUSE_RMDIR, FUSE_RENAME, FUSE_LINK, FUSE_OPEN, FUSE_READ, FUSE_WRITE, + * FUSE_RELEASE, FUSE_FSYNC, FUSE_FLUSH, FUSE_SETXATTR, FUSE_GETXATTR, + * FUSE_LISTXATTR, FUSE_REMOVEXATTR, FUSE_OPENDIR, FUSE_READDIR, + * FUSE_RELEASEDIR + * - add padding to messages to accommodate 32-bit servers on 64-bit kernels + * + * 7.2: + * - add FOPEN_DIRECT_IO and FOPEN_KEEP_CACHE flags + * - add FUSE_FSYNCDIR message + * + * 7.3: + * - add FUSE_ACCESS message + * - add FUSE_CREATE message + * - add filehandle to fuse_setattr_in + * + * 7.4: + * - add frsize to fuse_kstatfs + * - clean up request size limit checking + * + * 7.5: + * - add flags and max_write to fuse_init_out + * + * 7.6: + * - add max_readahead to fuse_init_in and fuse_init_out + * + * 7.7: + * - add FUSE_INTERRUPT message + * - add POSIX file lock support + * + * 7.8: + * - add lock_owner and flags fields to fuse_release_in + * - add FUSE_BMAP message + * - add FUSE_DESTROY message + * + * 7.9: + * - new fuse_getattr_in input argument of GETATTR + * - add lk_flags in fuse_lk_in + * - add lock_owner field to fuse_setattr_in, fuse_read_in and fuse_write_in + * - add blksize field to fuse_attr + * - add file flags field to fuse_read_in and fuse_write_in + * - Add ATIME_NOW and MTIME_NOW flags to fuse_setattr_in + * + * 7.10 + * - add nonseekable open flag + * + * 7.11 + * - add IOCTL message + * - add unsolicited notification support + * - add POLL message and NOTIFY_POLL notification + * + * 7.12 + * - add umask flag to input argument of create, mknod and mkdir + * - add notification messages for invalidation of inodes and + * directory entries + * + * 7.13 + * - make max number of background requests and congestion threshold + * tunables + * + * 7.14 + * - add splice support to fuse device + * + * 7.15 + * - add store notify + * - add retrieve notify + * + * 7.16 + * - add BATCH_FORGET request + * - FUSE_IOCTL_UNRESTRICTED shall now return with array of 'struct + * fuse_ioctl_iovec' instead of ambiguous 'struct iovec' + * - add FUSE_IOCTL_32BIT flag + * + * 7.17 + * - add FUSE_FLOCK_LOCKS and FUSE_RELEASE_FLOCK_UNLOCK + * + * 7.18 + * - add FUSE_IOCTL_DIR flag + * - add FUSE_NOTIFY_DELETE + * + * 7.19 + * - add FUSE_FALLOCATE + * + * 7.20 + * - add FUSE_AUTO_INVAL_DATA + * + * 7.21 + * - add FUSE_READDIRPLUS + * - send the requested events in POLL request + * + * 7.22 + * - add FUSE_ASYNC_DIO + * + * 7.23 + * - add FUSE_WRITEBACK_CACHE + * - add time_gran to fuse_init_out + * - add reserved space to fuse_init_out + * - add FATTR_CTIME + * - add ctime and ctimensec to fuse_setattr_in + * - add FUSE_RENAME2 request + * - add FUSE_NO_OPEN_SUPPORT flag + * + * 7.24 + * - add FUSE_LSEEK for SEEK_HOLE and SEEK_DATA support + * + * 7.25 + * - add FUSE_PARALLEL_DIROPS + * + * 7.26 + * - add FUSE_HANDLE_KILLPRIV + * - add FUSE_POSIX_ACL + * + * 7.27 + * - add FUSE_ABORT_ERROR + * + * 7.28 + * - add FUSE_COPY_FILE_RANGE + * - add FOPEN_CACHE_DIR + * - add FUSE_MAX_PAGES, add max_pages to init_out + * - add FUSE_CACHE_SYMLINKS + * + * 7.29 + * - add FUSE_NO_OPENDIR_SUPPORT flag + * + * 7.30 + * - add FUSE_EXPLICIT_INVAL_DATA + * - add FUSE_IOCTL_COMPAT_X32 + * + * 7.31 + * - add FUSE_WRITE_KILL_PRIV flag + * - add FUSE_SETUPMAPPING and FUSE_REMOVEMAPPING + * - add map_alignment to fuse_init_out, add FUSE_MAP_ALIGNMENT flag + * + * 7.32 + * - add flags to fuse_attr, add FUSE_ATTR_SUBMOUNT, add FUSE_SUBMOUNTS + * + * 7.33 + * - add FUSE_HANDLE_KILLPRIV_V2, FUSE_WRITE_KILL_SUIDGID, FATTR_KILL_SUIDGID + * - add FUSE_OPEN_KILL_SUIDGID + * - extend fuse_setxattr_in, add FUSE_SETXATTR_EXT + * - add FUSE_SETXATTR_ACL_KILL_SGID + * + * 7.34 + * - add FUSE_SYNCFS + * + * 7.35 + * - add FOPEN_NOFLUSH + * + * 7.36 + * - extend fuse_init_in with reserved fields, add FUSE_INIT_EXT init flag + * - add flags2 to fuse_init_in and fuse_init_out + * - add FUSE_SECURITY_CTX init flag + * - add security context to create, mkdir, symlink, and mknod requests + * - add FUSE_HAS_INODE_DAX, FUSE_ATTR_DAX + * + * 7.37 + * - add FUSE_TMPFILE + * + * 7.38 + * - add FUSE_EXPIRE_ONLY flag to fuse_notify_inval_entry + * - add FOPEN_PARALLEL_DIRECT_WRITES + * - add total_extlen to fuse_in_header + * - add FUSE_MAX_NR_SECCTX + * - add extension header + * - add FUSE_EXT_GROUPS + * - add FUSE_CREATE_SUPP_GROUP + */ + +#ifndef _LINUX_FUSE_H +#define _LINUX_FUSE_H + +#ifdef __KERNEL__ +#include +#else +#include +#endif + +/* + * Version negotiation: + * + * Both the kernel and userspace send the version they support in the + * INIT request and reply respectively. + * + * If the major versions match then both shall use the smallest + * of the two minor versions for communication. + * + * If the kernel supports a larger major version, then userspace shall + * reply with the major version it supports, ignore the rest of the + * INIT message and expect a new INIT message from the kernel with a + * matching major version. + * + * If the library supports a larger major version, then it shall fall + * back to the major protocol version sent by the kernel for + * communication and reply with that major version (and an arbitrary + * supported minor version). + */ + +/** Version number of this interface */ +#define FUSE_KERNEL_VERSION 7 + +/** Minor version number of this interface */ +#define FUSE_KERNEL_MINOR_VERSION 38 + +/** The node ID of the root inode */ +#define FUSE_ROOT_ID 1 + +/* Make sure all structures are padded to 64bit boundary, so 32bit + userspace works under 64bit kernels */ + +struct fuse_attr { + uint64_t ino; + uint64_t size; + uint64_t blocks; + uint64_t atime; + uint64_t mtime; + uint64_t ctime; + uint32_t atimensec; + uint32_t mtimensec; + uint32_t ctimensec; + uint32_t mode; + uint32_t nlink; + uint32_t uid; + uint32_t gid; + uint32_t rdev; + uint32_t blksize; + uint32_t flags; +}; + +struct fuse_kstatfs { + uint64_t blocks; + uint64_t bfree; + uint64_t bavail; + uint64_t files; + uint64_t ffree; + uint32_t bsize; + uint32_t namelen; + uint32_t frsize; + uint32_t padding; + uint32_t spare[6]; +}; + +struct fuse_file_lock { + uint64_t start; + uint64_t end; + uint32_t type; + uint32_t pid; /* tgid */ +}; + +/** + * Bitmasks for fuse_setattr_in.valid + */ +#define FATTR_MODE (1 << 0) +#define FATTR_UID (1 << 1) +#define FATTR_GID (1 << 2) +#define FATTR_SIZE (1 << 3) +#define FATTR_ATIME (1 << 4) +#define FATTR_MTIME (1 << 5) +#define FATTR_FH (1 << 6) +#define FATTR_ATIME_NOW (1 << 7) +#define FATTR_MTIME_NOW (1 << 8) +#define FATTR_LOCKOWNER (1 << 9) +#define FATTR_CTIME (1 << 10) +#define FATTR_KILL_SUIDGID (1 << 11) + +/** + * Flags returned by the OPEN request + * + * FOPEN_DIRECT_IO: bypass page cache for this open file + * FOPEN_KEEP_CACHE: don't invalidate the data cache on open + * FOPEN_NONSEEKABLE: the file is not seekable + * FOPEN_CACHE_DIR: allow caching this directory + * FOPEN_STREAM: the file is stream-like (no file position at all) + * FOPEN_NOFLUSH: don't flush data cache on close (unless FUSE_WRITEBACK_CACHE) + * FOPEN_PARALLEL_DIRECT_WRITES: Allow concurrent direct writes on the same inode + */ +#define FOPEN_DIRECT_IO (1 << 0) +#define FOPEN_KEEP_CACHE (1 << 1) +#define FOPEN_NONSEEKABLE (1 << 2) +#define FOPEN_CACHE_DIR (1 << 3) +#define FOPEN_STREAM (1 << 4) +#define FOPEN_NOFLUSH (1 << 5) +#define FOPEN_PARALLEL_DIRECT_WRITES (1 << 6) + +/** + * INIT request/reply flags + * + * FUSE_ASYNC_READ: asynchronous read requests + * FUSE_POSIX_LOCKS: remote locking for POSIX file locks + * FUSE_FILE_OPS: kernel sends file handle for fstat, etc... (not yet supported) + * FUSE_ATOMIC_O_TRUNC: handles the O_TRUNC open flag in the filesystem + * FUSE_EXPORT_SUPPORT: filesystem handles lookups of "." and ".." + * FUSE_BIG_WRITES: filesystem can handle write size larger than 4kB + * FUSE_DONT_MASK: don't apply umask to file mode on create operations + * FUSE_SPLICE_WRITE: kernel supports splice write on the device + * FUSE_SPLICE_MOVE: kernel supports splice move on the device + * FUSE_SPLICE_READ: kernel supports splice read on the device + * FUSE_FLOCK_LOCKS: remote locking for BSD style file locks + * FUSE_HAS_IOCTL_DIR: kernel supports ioctl on directories + * FUSE_AUTO_INVAL_DATA: automatically invalidate cached pages + * FUSE_DO_READDIRPLUS: do READDIRPLUS (READDIR+LOOKUP in one) + * FUSE_READDIRPLUS_AUTO: adaptive readdirplus + * FUSE_ASYNC_DIO: asynchronous direct I/O submission + * FUSE_WRITEBACK_CACHE: use writeback cache for buffered writes + * FUSE_NO_OPEN_SUPPORT: kernel supports zero-message opens + * FUSE_PARALLEL_DIROPS: allow parallel lookups and readdir + * FUSE_HANDLE_KILLPRIV: fs handles killing suid/sgid/cap on write/chown/trunc + * FUSE_POSIX_ACL: filesystem supports posix acls + * FUSE_ABORT_ERROR: reading the device after abort returns ECONNABORTED + * FUSE_MAX_PAGES: init_out.max_pages contains the max number of req pages + * FUSE_CACHE_SYMLINKS: cache READLINK responses + * FUSE_NO_OPENDIR_SUPPORT: kernel supports zero-message opendir + * FUSE_EXPLICIT_INVAL_DATA: only invalidate cached pages on explicit request + * FUSE_MAP_ALIGNMENT: init_out.map_alignment contains log2(byte alignment) for + * foffset and moffset fields in struct + * fuse_setupmapping_out and fuse_removemapping_one. + * FUSE_SUBMOUNTS: kernel supports auto-mounting directory submounts + * FUSE_HANDLE_KILLPRIV_V2: fs kills suid/sgid/cap on write/chown/trunc. + * Upon write/truncate suid/sgid is only killed if caller + * does not have CAP_FSETID. Additionally upon + * write/truncate sgid is killed only if file has group + * execute permission. (Same as Linux VFS behavior). + * FUSE_SETXATTR_EXT: Server supports extended struct fuse_setxattr_in + * FUSE_INIT_EXT: extended fuse_init_in request + * FUSE_INIT_RESERVED: reserved, do not use + * FUSE_SECURITY_CTX: add security context to create, mkdir, symlink, and + * mknod + * FUSE_HAS_INODE_DAX: use per inode DAX + * FUSE_CREATE_SUPP_GROUP: add supplementary group info to create, mkdir, + * symlink and mknod (single group that matches parent) + */ +#define FUSE_ASYNC_READ (1 << 0) +#define FUSE_POSIX_LOCKS (1 << 1) +#define FUSE_FILE_OPS (1 << 2) +#define FUSE_ATOMIC_O_TRUNC (1 << 3) +#define FUSE_EXPORT_SUPPORT (1 << 4) +#define FUSE_BIG_WRITES (1 << 5) +#define FUSE_DONT_MASK (1 << 6) +#define FUSE_SPLICE_WRITE (1 << 7) +#define FUSE_SPLICE_MOVE (1 << 8) +#define FUSE_SPLICE_READ (1 << 9) +#define FUSE_FLOCK_LOCKS (1 << 10) +#define FUSE_HAS_IOCTL_DIR (1 << 11) +#define FUSE_AUTO_INVAL_DATA (1 << 12) +#define FUSE_DO_READDIRPLUS (1 << 13) +#define FUSE_READDIRPLUS_AUTO (1 << 14) +#define FUSE_ASYNC_DIO (1 << 15) +#define FUSE_WRITEBACK_CACHE (1 << 16) +#define FUSE_NO_OPEN_SUPPORT (1 << 17) +#define FUSE_PARALLEL_DIROPS (1 << 18) +#define FUSE_HANDLE_KILLPRIV (1 << 19) +#define FUSE_POSIX_ACL (1 << 20) +#define FUSE_ABORT_ERROR (1 << 21) +#define FUSE_MAX_PAGES (1 << 22) +#define FUSE_CACHE_SYMLINKS (1 << 23) +#define FUSE_NO_OPENDIR_SUPPORT (1 << 24) +#define FUSE_EXPLICIT_INVAL_DATA (1 << 25) +#define FUSE_MAP_ALIGNMENT (1 << 26) +#define FUSE_SUBMOUNTS (1 << 27) +#define FUSE_HANDLE_KILLPRIV_V2 (1 << 28) +#define FUSE_SETXATTR_EXT (1 << 29) +#define FUSE_INIT_EXT (1 << 30) +#define FUSE_INIT_RESERVED (1 << 31) +/* bits 32..63 get shifted down 32 bits into the flags2 field */ +#define FUSE_SECURITY_CTX (1ULL << 32) +#define FUSE_HAS_INODE_DAX (1ULL << 33) +#define FUSE_CREATE_SUPP_GROUP (1ULL << 34) + +/** + * CUSE INIT request/reply flags + * + * CUSE_UNRESTRICTED_IOCTL: use unrestricted ioctl + */ +#define CUSE_UNRESTRICTED_IOCTL (1 << 0) + +/** + * Release flags + */ +#define FUSE_RELEASE_FLUSH (1 << 0) +#define FUSE_RELEASE_FLOCK_UNLOCK (1 << 1) + +/** + * Getattr flags + */ +#define FUSE_GETATTR_FH (1 << 0) + +/** + * Lock flags + */ +#define FUSE_LK_FLOCK (1 << 0) + +/** + * WRITE flags + * + * FUSE_WRITE_CACHE: delayed write from page cache, file handle is guessed + * FUSE_WRITE_LOCKOWNER: lock_owner field is valid + * FUSE_WRITE_KILL_SUIDGID: kill suid and sgid bits + */ +#define FUSE_WRITE_CACHE (1 << 0) +#define FUSE_WRITE_LOCKOWNER (1 << 1) +#define FUSE_WRITE_KILL_SUIDGID (1 << 2) + +/* Obsolete alias; this flag implies killing suid/sgid only. */ +#define FUSE_WRITE_KILL_PRIV FUSE_WRITE_KILL_SUIDGID + +/** + * Read flags + */ +#define FUSE_READ_LOCKOWNER (1 << 1) + +/** + * Ioctl flags + * + * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine + * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed + * FUSE_IOCTL_RETRY: retry with new iovecs + * FUSE_IOCTL_32BIT: 32bit ioctl + * FUSE_IOCTL_DIR: is a directory + * FUSE_IOCTL_COMPAT_X32: x32 compat ioctl on 64bit machine (64bit time_t) + * + * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs + */ +#define FUSE_IOCTL_COMPAT (1 << 0) +#define FUSE_IOCTL_UNRESTRICTED (1 << 1) +#define FUSE_IOCTL_RETRY (1 << 2) +#define FUSE_IOCTL_32BIT (1 << 3) +#define FUSE_IOCTL_DIR (1 << 4) +#define FUSE_IOCTL_COMPAT_X32 (1 << 5) + +#define FUSE_IOCTL_MAX_IOV 256 + +/** + * Poll flags + * + * FUSE_POLL_SCHEDULE_NOTIFY: request poll notify + */ +#define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0) + +/** + * Fsync flags + * + * FUSE_FSYNC_FDATASYNC: Sync data only, not metadata + */ +#define FUSE_FSYNC_FDATASYNC (1 << 0) + +/** + * fuse_attr flags + * + * FUSE_ATTR_SUBMOUNT: Object is a submount root + * FUSE_ATTR_DAX: Enable DAX for this file in per inode DAX mode + */ +#define FUSE_ATTR_SUBMOUNT (1 << 0) +#define FUSE_ATTR_DAX (1 << 1) + +/** + * Open flags + * FUSE_OPEN_KILL_SUIDGID: Kill suid and sgid if executable + */ +#define FUSE_OPEN_KILL_SUIDGID (1 << 0) + +/** + * setxattr flags + * FUSE_SETXATTR_ACL_KILL_SGID: Clear SGID when system.posix_acl_access is set + */ +#define FUSE_SETXATTR_ACL_KILL_SGID (1 << 0) + +/** + * notify_inval_entry flags + * FUSE_EXPIRE_ONLY + */ +#define FUSE_EXPIRE_ONLY (1 << 0) + +/** + * extension type + * FUSE_MAX_NR_SECCTX: maximum value of &fuse_secctx_header.nr_secctx + * FUSE_EXT_GROUPS: &fuse_supp_groups extension + */ +enum fuse_ext_type { + /* Types 0..31 are reserved for fuse_secctx_header */ + FUSE_MAX_NR_SECCTX = 31, + FUSE_EXT_GROUPS = 32, + FUSE_ERROR_IN = 33, +}; + +enum fuse_opcode { + FUSE_LOOKUP = 1, + FUSE_FORGET = 2, /* no reply */ + FUSE_GETATTR = 3, + FUSE_SETATTR = 4, + FUSE_READLINK = 5, + FUSE_SYMLINK = 6, + FUSE_MKNOD = 8, + FUSE_MKDIR = 9, + FUSE_UNLINK = 10, + FUSE_RMDIR = 11, + FUSE_RENAME = 12, + FUSE_LINK = 13, + FUSE_OPEN = 14, + FUSE_READ = 15, + FUSE_WRITE = 16, + FUSE_STATFS = 17, + FUSE_RELEASE = 18, + FUSE_FSYNC = 20, + FUSE_SETXATTR = 21, + FUSE_GETXATTR = 22, + FUSE_LISTXATTR = 23, + FUSE_REMOVEXATTR = 24, + FUSE_FLUSH = 25, + FUSE_INIT = 26, + FUSE_OPENDIR = 27, + FUSE_READDIR = 28, + FUSE_RELEASEDIR = 29, + FUSE_FSYNCDIR = 30, + FUSE_GETLK = 31, + FUSE_SETLK = 32, + FUSE_SETLKW = 33, + FUSE_ACCESS = 34, + FUSE_CREATE = 35, + FUSE_INTERRUPT = 36, + FUSE_BMAP = 37, + FUSE_DESTROY = 38, + FUSE_IOCTL = 39, + FUSE_POLL = 40, + FUSE_NOTIFY_REPLY = 41, + FUSE_BATCH_FORGET = 42, + FUSE_FALLOCATE = 43, + FUSE_READDIRPLUS = 44, + FUSE_RENAME2 = 45, + FUSE_LSEEK = 46, + FUSE_COPY_FILE_RANGE = 47, + FUSE_SETUPMAPPING = 48, + FUSE_REMOVEMAPPING = 49, + FUSE_SYNCFS = 50, + FUSE_TMPFILE = 51, + + /* CUSE specific operations */ + CUSE_INIT = 4096, + + /* Reserved opcodes: helpful to detect structure endian-ness */ + CUSE_INIT_BSWAP_RESERVED = 1048576, /* CUSE_INIT << 8 */ + FUSE_INIT_BSWAP_RESERVED = 436207616, /* FUSE_INIT << 24 */ +}; + +enum fuse_notify_code { + FUSE_NOTIFY_POLL = 1, + FUSE_NOTIFY_INVAL_INODE = 2, + FUSE_NOTIFY_INVAL_ENTRY = 3, + FUSE_NOTIFY_STORE = 4, + FUSE_NOTIFY_RETRIEVE = 5, + FUSE_NOTIFY_DELETE = 6, + FUSE_NOTIFY_CODE_MAX, +}; + +/* The read buffer is required to be at least 8k, but may be much larger */ +#define FUSE_MIN_READ_BUFFER 8192 + +#define FUSE_COMPAT_ENTRY_OUT_SIZE 120 + +struct fuse_entry_out { + uint64_t nodeid; /* Inode ID */ + uint64_t generation; /* Inode generation: nodeid:gen must + be unique for the fs's lifetime */ + uint64_t entry_valid; /* Cache timeout for the name */ + uint64_t attr_valid; /* Cache timeout for the attributes */ + uint32_t entry_valid_nsec; + uint32_t attr_valid_nsec; + 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, +}; + +#define BPF_FUSE_NAME_MAX 15 + +struct fuse_bpf_entry_out { + uint32_t entry_type; + uint32_t unused; + union { + struct { + uint64_t unused2; + uint64_t fd; + }; + char name[BPF_FUSE_NAME_MAX + 1]; + }; +}; + +struct fuse_forget_in { + uint64_t nlookup; +}; + +struct fuse_forget_one { + uint64_t nodeid; + uint64_t nlookup; +}; + +struct fuse_batch_forget_in { + uint32_t count; + uint32_t dummy; +}; + +struct fuse_getattr_in { + uint32_t getattr_flags; + uint32_t dummy; + uint64_t fh; +}; + +#define FUSE_COMPAT_ATTR_OUT_SIZE 96 + +struct fuse_attr_out { + uint64_t attr_valid; /* Cache timeout for the attributes */ + uint32_t attr_valid_nsec; + uint32_t dummy; + struct fuse_attr attr; +}; + +#define FUSE_COMPAT_MKNOD_IN_SIZE 8 + +struct fuse_mknod_in { + uint32_t mode; + uint32_t rdev; + uint32_t umask; + uint32_t padding; +}; + +struct fuse_mkdir_in { + uint32_t mode; + uint32_t umask; +}; + +struct fuse_rename_in { + uint64_t newdir; +}; + +struct fuse_rename2_in { + uint64_t newdir; + uint32_t flags; + uint32_t padding; +}; + +struct fuse_link_in { + uint64_t oldnodeid; +}; + +struct fuse_setattr_in { + uint32_t valid; + uint32_t padding; + uint64_t fh; + uint64_t size; + uint64_t lock_owner; + uint64_t atime; + uint64_t mtime; + uint64_t ctime; + uint32_t atimensec; + uint32_t mtimensec; + uint32_t ctimensec; + uint32_t mode; + uint32_t unused4; + uint32_t uid; + uint32_t gid; + uint32_t unused5; +}; + +struct fuse_open_in { + uint32_t flags; + uint32_t open_flags; /* FUSE_OPEN_... */ +}; + +struct fuse_create_in { + uint32_t flags; + uint32_t mode; + uint32_t umask; + uint32_t open_flags; /* FUSE_OPEN_... */ +}; + +struct fuse_open_out { + uint64_t fh; + uint32_t open_flags; + uint32_t padding; +}; + +struct fuse_release_in { + uint64_t fh; + uint32_t flags; + uint32_t release_flags; + uint64_t lock_owner; +}; + +struct fuse_flush_in { + uint64_t fh; + uint32_t unused; + uint32_t padding; + uint64_t lock_owner; +}; + +struct fuse_read_in { + uint64_t fh; + uint64_t offset; + uint32_t size; + uint32_t read_flags; + uint64_t lock_owner; + uint32_t flags; + uint32_t padding; +}; + +struct fuse_read_out { + uint64_t offset; + uint32_t again; + uint32_t padding; +}; + +// This is likely not what we want +struct fuse_read_iter_out { + uint64_t ret; +}; + +#define FUSE_COMPAT_WRITE_IN_SIZE 24 + +struct fuse_write_in { + uint64_t fh; + uint64_t offset; + uint32_t size; + uint32_t write_flags; + uint64_t lock_owner; + uint32_t flags; + uint32_t padding; +}; + +struct fuse_write_out { + uint32_t size; + uint32_t padding; +}; + +// This is likely not what we want +struct fuse_write_iter_out { + uint64_t ret; +}; + +#define FUSE_COMPAT_STATFS_SIZE 48 + +struct fuse_statfs_out { + struct fuse_kstatfs st; +}; + +struct fuse_fsync_in { + uint64_t fh; + uint32_t fsync_flags; + uint32_t padding; +}; + +#define FUSE_COMPAT_SETXATTR_IN_SIZE 8 + +struct fuse_setxattr_in { + uint32_t size; + uint32_t flags; + uint32_t setxattr_flags; + uint32_t padding; +}; + +struct fuse_getxattr_in { + uint32_t size; + uint32_t padding; +}; + +struct fuse_getxattr_out { + uint32_t size; + uint32_t padding; +}; + +struct fuse_lk_in { + uint64_t fh; + uint64_t owner; + struct fuse_file_lock lk; + uint32_t lk_flags; + uint32_t padding; +}; + +struct fuse_lk_out { + struct fuse_file_lock lk; +}; + +struct fuse_access_in { + uint32_t mask; + uint32_t padding; +}; + +struct fuse_init_in { + uint32_t major; + uint32_t minor; + uint32_t max_readahead; + uint32_t flags; + uint32_t flags2; + uint32_t unused[11]; +}; + +#define FUSE_COMPAT_INIT_OUT_SIZE 8 +#define FUSE_COMPAT_22_INIT_OUT_SIZE 24 + +struct fuse_init_out { + uint32_t major; + uint32_t minor; + uint32_t max_readahead; + uint32_t flags; + uint16_t max_background; + uint16_t congestion_threshold; + uint32_t max_write; + uint32_t time_gran; + uint16_t max_pages; + uint16_t map_alignment; + uint32_t flags2; + uint32_t unused[7]; +}; + +#define CUSE_INIT_INFO_MAX 4096 + +struct cuse_init_in { + uint32_t major; + uint32_t minor; + uint32_t unused; + uint32_t flags; +}; + +struct cuse_init_out { + uint32_t major; + uint32_t minor; + uint32_t unused; + uint32_t flags; + uint32_t max_read; + uint32_t max_write; + uint32_t dev_major; /* chardev major */ + uint32_t dev_minor; /* chardev minor */ + uint32_t spare[10]; +}; + +struct fuse_interrupt_in { + uint64_t unique; +}; + +struct fuse_bmap_in { + uint64_t block; + uint32_t blocksize; + uint32_t padding; +}; + +struct fuse_bmap_out { + uint64_t block; +}; + +struct fuse_ioctl_in { + uint64_t fh; + uint32_t flags; + uint32_t cmd; + uint64_t arg; + uint32_t in_size; + uint32_t out_size; +}; + +struct fuse_ioctl_iovec { + uint64_t base; + uint64_t len; +}; + +struct fuse_ioctl_out { + int32_t result; + uint32_t flags; + uint32_t in_iovs; + uint32_t out_iovs; +}; + +struct fuse_poll_in { + uint64_t fh; + uint64_t kh; + uint32_t flags; + uint32_t events; +}; + +struct fuse_poll_out { + uint32_t revents; + uint32_t padding; +}; + +struct fuse_notify_poll_wakeup_out { + uint64_t kh; +}; + +struct fuse_fallocate_in { + uint64_t fh; + uint64_t offset; + uint64_t length; + uint32_t mode; + uint32_t padding; +}; + +struct fuse_in_header { + uint32_t len; + uint32_t opcode; + uint64_t unique; + uint64_t nodeid; + uint32_t uid; + uint32_t gid; + uint32_t pid; + uint16_t total_extlen; /* length of extensions in 8byte units */ + uint16_t padding; + //uint32_t error_in; uh oh +}; + +struct fuse_out_header { + uint32_t len; + int32_t error; + uint64_t unique; +}; + +struct fuse_dirent { + uint64_t ino; + uint64_t off; + uint32_t namelen; + uint32_t type; + char name[]; +}; + +/* Align variable length records to 64bit boundary */ +#define FUSE_REC_ALIGN(x) \ + (((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1)) + +#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name) +#define FUSE_DIRENT_ALIGN(x) FUSE_REC_ALIGN(x) +#define FUSE_DIRENT_SIZE(d) \ + FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen) + +struct fuse_direntplus { + struct fuse_entry_out entry_out; + struct fuse_dirent dirent; +}; + +#define FUSE_NAME_OFFSET_DIRENTPLUS \ + offsetof(struct fuse_direntplus, dirent.name) +#define FUSE_DIRENTPLUS_SIZE(d) \ + FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET_DIRENTPLUS + (d)->dirent.namelen) + +struct fuse_notify_inval_inode_out { + uint64_t ino; + int64_t off; + int64_t len; +}; + +struct fuse_notify_inval_entry_out { + uint64_t parent; + uint32_t namelen; + uint32_t flags; +}; + +struct fuse_notify_delete_out { + uint64_t parent; + uint64_t child; + uint32_t namelen; + uint32_t padding; +}; + +struct fuse_notify_store_out { + uint64_t nodeid; + uint64_t offset; + uint32_t size; + uint32_t padding; +}; + +struct fuse_notify_retrieve_out { + uint64_t notify_unique; + uint64_t nodeid; + uint64_t offset; + uint32_t size; + uint32_t padding; +}; + +/* Matches the size of fuse_write_in */ +struct fuse_notify_retrieve_in { + uint64_t dummy1; + uint64_t offset; + uint32_t size; + uint32_t dummy2; + uint64_t dummy3; + uint64_t dummy4; +}; + +/* 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; + uint64_t offset; + uint32_t whence; + uint32_t padding; +}; + +struct fuse_lseek_out { + uint64_t offset; +}; + +struct fuse_copy_file_range_in { + uint64_t fh_in; + uint64_t off_in; + uint64_t nodeid_out; + uint64_t fh_out; + uint64_t off_out; + uint64_t len; + uint64_t flags; +}; + +#define FUSE_SETUPMAPPING_FLAG_WRITE (1ull << 0) +#define FUSE_SETUPMAPPING_FLAG_READ (1ull << 1) +struct fuse_setupmapping_in { + /* An already open handle */ + uint64_t fh; + /* Offset into the file to start the mapping */ + uint64_t foffset; + /* Length of mapping required */ + uint64_t len; + /* Flags, FUSE_SETUPMAPPING_FLAG_* */ + uint64_t flags; + /* Offset in Memory Window */ + uint64_t moffset; +}; + +struct fuse_removemapping_in { + /* number of fuse_removemapping_one follows */ + uint32_t count; +}; + +struct fuse_removemapping_one { + /* Offset into the dax window start the unmapping */ + uint64_t moffset; + /* Length of mapping required */ + uint64_t len; +}; + +#define FUSE_REMOVEMAPPING_MAX_ENTRY \ + (PAGE_SIZE / sizeof(struct fuse_removemapping_one)) + +struct fuse_syncfs_in { + uint64_t padding; +}; + +/* + * For each security context, send fuse_secctx with size of security context + * fuse_secctx will be followed by security context name and this in turn + * will be followed by actual context label. + * fuse_secctx, name, context + */ +struct fuse_secctx { + uint32_t size; + uint32_t padding; +}; + +/* + * Contains the information about how many fuse_secctx structures are being + * sent and what's the total size of all security contexts (including + * size of fuse_secctx_header). + * + */ +struct fuse_secctx_header { + uint32_t size; + uint32_t nr_secctx; +}; + +/** + * struct fuse_ext_header - extension header + * @size: total size of this extension including this header + * @type: type of extension + * + * This is made compatible with fuse_secctx_header by using type values > + * FUSE_MAX_NR_SECCTX + */ +struct fuse_ext_header { + uint32_t size; + uint32_t type; +}; + +/** + * struct fuse_supp_groups - Supplementary group extension + * @nr_groups: number of supplementary groups + * @groups: flexible array of group IDs + */ +struct fuse_supp_groups { + uint32_t nr_groups; + uint32_t groups[]; +}; + +#endif /* _LINUX_FUSE_H */ From patchwork Tue Apr 18 01:40:37 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Rosenberg X-Patchwork-Id: 13214997 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 4D455C77B76 for ; Tue, 18 Apr 2023 01:44:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231140AbjDRBoe (ORCPT ); Mon, 17 Apr 2023 21:44:34 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53826 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230461AbjDRBnc (ORCPT ); Mon, 17 Apr 2023 21:43:32 -0400 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 20BCC6E99 for ; Mon, 17 Apr 2023 18:42:15 -0700 (PDT) Received: by mail-yb1-xb4a.google.com with SMTP id d132-20020a254f8a000000b00b868826cdfeso2898986ybb.0 for ; Mon, 17 Apr 2023 18:42:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782135; x=1684374135; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=qi1Po8S34Kt7QbElMTr2iZrr48NSEyGebr4FBWlSvn0=; b=MSsv99K1wdyZt0ehocN3dFGnyuMghLajhZABLr8jg/8pE9UjNyOpDMEfWyojc7bZxL aIdIEzKOVqQM6eAiGt15nHbmio8M/8pFdNUqUKZ2eWJVCzjDh32WmTdHJZ10f5/POWF2 pAAEo+YIza9Rwrb6KL2LEkS8NvJFgrwQvEqP9WD4eGIxb87r1fEPbOg58UfyeUZSKVSH sLjYM7/L0kSRcgdwJcbirerE7VCZnsIM4Z5WtlxXaqbbgNOGaNOEVE53+XYyUpwoREZh Y35+i/5dEgBZjBXFGcHvoTytJSDXWndNAxfmRes5d05cuW6DV5UOQZPI/AnztxHlgPeK mLsw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782135; x=1684374135; 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=qi1Po8S34Kt7QbElMTr2iZrr48NSEyGebr4FBWlSvn0=; b=BZCkwL3fWAQabFMryrXLXnWcWCrX3/qAnqfHBP581j32bJU7xq42rq2K21k+w5B81R M7yWgxT/2UsOkdFwhGdgh0RU8NHrHtyYBOoXUyrbstiZY17LsaqFc/jUdoLBHqLGna41 ifeHgbA4OjFkbW0DCiPCUmAj2inA1F5YEIl5eLKBcVI648uWpow2GDdQWrx6RamTLbCD aIbA8aJpqw+vcmQ5hg6jO0DdR4LWqc8IpEF3Sq6cS6ZE7phctA1kX/Ce4P2QFoNy7hzP dNT1jVNfXqSuk24o1QZUuNripfDH/z015mkSpkJDjn94uaX80IppdV13uxH3wnu+UUBR SGHQ== X-Gm-Message-State: AAQBX9fWErM2K+CHXwBYm6u7dYdsc6TJZdsYV+yOsorLhY5Rpp+ABEK2 3N7UFskpYsI4J/QFKZvR179kdPjtikU= X-Google-Smtp-Source: AKy350abrQ11ASojcePeUYEdiv/KTIkMP9GG/A+eTvQ5myvKQeYGNjXLXkAgj+/aiYds5ZlRZX6RdyULoaA= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a25:6c41:0:b0:b8f:1d2c:243f with SMTP id h62-20020a256c41000000b00b8f1d2c243fmr8371770ybc.1.1681782134824; Mon, 17 Apr 2023 18:42:14 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:37 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-38-drosen@google.com> Subject: [RFC PATCH v3 37/37] fuse: Provide easy way to test fuse struct_op call From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , kernel-team@android.com, Daniel Rosenberg Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-State: RFC This is useful for quickly testing a struct_op program. I've been using this set up to test verifier changes. I'll eventually move those sorts of tests to bpf selftests Signed-off-by: Daniel Rosenberg --- fs/fuse/inode.c | 70 ++ .../selftests/filesystems/fuse/Makefile | 1 + .../filesystems/fuse/struct_op_test.bpf.c | 642 ++++++++++++++++++ 3 files changed, 713 insertions(+) create mode 100644 tools/testing/selftests/filesystems/fuse/struct_op_test.bpf.c diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 7fd79efbdac1..d80c7282c91c 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -2071,16 +2071,83 @@ static void fuse_fs_cleanup(void) static struct kobject *fuse_kobj; +static char struct_op_name[BPF_FUSE_NAME_MAX]; +static struct fuse_ops *fop = NULL; + +static ssize_t struct_op_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + size_t max = count; + + if (max > BPF_FUSE_NAME_MAX) max = BPF_FUSE_NAME_MAX; + strncpy(struct_op_name, buf, max); + if (struct_op_name[max-1] == '\n') + struct_op_name[max-1] = 0; + put_fuse_ops(fop); + fop = find_fuse_ops(struct_op_name); + if (!fop) + printk("No struct op named %s found", struct_op_name); + + return count; +} + +static ssize_t struct_op_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct fuse_ops *op; + uint32_t result = 0; + struct bpf_fuse_meta_info meta; + struct fuse_mkdir_in in; + struct fuse_buffer name; + char name_buff[10] = "test"; + + name.data = &name_buff[0]; + name.flags = BPF_FUSE_VARIABLE_SIZE; + name.max_size = 10; + name.size = 5; + + op = fop; + if (!op) { + printk("Could not find fuse_op for %s", struct_op_name); + return 0; + } + + if (op->mkdir_prefilter) + result = op->mkdir_prefilter(&meta, &in, &name); + else + printk("No func!!"); + + printk("in->mode:%d, name:%s result:%d", in.mode, (char *)name.data, result); + return sprintf(buf, "%d dyn:%s\n", result, (char *)name.data); +} + +static struct kobj_attribute test_attr = __ATTR_RW(struct_op); + +static struct attribute *test_attrs[] = { + &test_attr.attr, + NULL, +}; + +static const struct attribute_group test_attr_group = { + .attrs = test_attrs, +}; + static int fuse_sysfs_init(void) { int err; + memset(struct_op_name, 0, BPF_FUSE_NAME_MAX); fuse_kobj = kobject_create_and_add("fuse", fs_kobj); if (!fuse_kobj) { err = -ENOMEM; goto out_err; } + err = sysfs_create_group(fuse_kobj, &test_attr_group); + if (err) + goto tmp; + err = sysfs_create_mount_point(fuse_kobj, "connections"); if (err) goto out_fuse_unregister; @@ -2089,6 +2156,8 @@ static int fuse_sysfs_init(void) out_fuse_unregister: kobject_put(fuse_kobj); +tmp: + sysfs_remove_group(fuse_kobj, &test_attr_group); out_err: return err; } @@ -2096,6 +2165,7 @@ static int fuse_sysfs_init(void) static void fuse_sysfs_cleanup(void) { sysfs_remove_mount_point(fuse_kobj, "connections"); + sysfs_remove_group(fuse_kobj, &test_attr_group); kobject_put(fuse_kobj); } diff --git a/tools/testing/selftests/filesystems/fuse/Makefile b/tools/testing/selftests/filesystems/fuse/Makefile index b2df4dec0651..ff28859f3268 100644 --- a/tools/testing/selftests/filesystems/fuse/Makefile +++ b/tools/testing/selftests/filesystems/fuse/Makefile @@ -52,6 +52,7 @@ SELFTESTS:=$(TOOLSDIR)/testing/selftests/ LDLIBS := -lpthread -lelf -lz TEST_GEN_PROGS := fuse_test fuse_daemon TEST_GEN_FILES := \ + struct_op_test.bpf.o \ test.skel.h \ fd.sh \ diff --git a/tools/testing/selftests/filesystems/fuse/struct_op_test.bpf.c b/tools/testing/selftests/filesystems/fuse/struct_op_test.bpf.c new file mode 100644 index 000000000000..2cb178d2fa0c --- /dev/null +++ b/tools/testing/selftests/filesystems/fuse/struct_op_test.bpf.c @@ -0,0 +1,642 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +// Copyright (c) 2021 Google LLC + +#include "vmlinux.h" +//#include +#include +#include +//#include +#include +#include +#include "bpf_common.h" + +char _license[] SEC("license") = "GPL"; + +#define BPF_STRUCT_OPS(type, name, args...) \ +SEC("struct_ops/"#name) \ +type BPF_PROG(name, ##args) + +/* +struct test_struct { + uint32_t a; + uint32_t b; +}; + + +*/ +//struct fuse_buffer; +#define BPF_FUSE_CONTINUE 0 +/*struct fuse_ops { + uint32_t (*test_func)(void); + uint32_t (*test_func2)(struct test_struct *a); + uint32_t (*test_func3)(struct fuse_name *ptr); + //u32 (*open_prefilter)(struct bpf_fuse_hidden_info meh, struct bpf_fuse_meta_info header, struct fuse_open_in foi); + //u32 (*open_postfilter)(struct bpf_fuse_hidden_info meh, struct bpf_fuse_meta_info header, const struct fuse_open_in foi, struct fuse_open_out foo); + char name[BPF_FUSE_NAME_MAX]; +}; +*/ +extern uint32_t bpf_fuse_return_len(struct fuse_buffer *ptr) __ksym; +extern void bpf_fuse_get_rw_dynptr(struct fuse_buffer *buffer, struct bpf_dynptr *dynptr, u64 size, bool copy) __ksym; +extern void bpf_fuse_get_ro_dynptr(const struct fuse_buffer *buffer, struct bpf_dynptr *dynptr) __ksym; + +//extern struct bpf_key *bpf_lookup_user_key(__u32 serial, __u64 flags) __ksym; +//extern struct bpf_key *bpf_lookup_system_key(__u64 id) __ksym; +//extern void bpf_key_put(struct bpf_key *key) __ksym; +//extern int bpf_verify_pkcs7_signature(struct bpf_dynptr *data_ptr, +// struct bpf_dynptr *sig_ptr, +// struct bpf_key *trusted_keyring) __ksym; + +BPF_STRUCT_OPS(uint32_t, test_func, const struct bpf_fuse_meta_info *meta, + struct fuse_mkdir_in *in, struct fuse_buffer *name) +{ + int res = 0; + struct bpf_dynptr name_ptr; + char *name_buf; + //char dummy[7] = {}; + + bpf_fuse_get_ro_dynptr(name, &name_ptr); + name_buf = bpf_dynptr_slice(&name_ptr, 0, NULL, 4); + bpf_printk("Hello test print"); + if (!name_buf) + return -ENOMEM; + if (!bpf_strncmp(name_buf, 4, "test")) + return 42; + + //if (bpf_fuse_namecmp(name, "test", 4) == 0) + // return 42; + + return res; +} + +SEC(".struct_ops") +struct fuse_ops test_ops = { + .mkdir_prefilter = (void *)test_func, + .name = "test", +}; + +BPF_STRUCT_OPS(uint32_t, open_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_open_in *in) +{ + bpf_printk("open_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, open_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_open_in *in, + struct fuse_open_out *out) +{ + bpf_printk("open_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, opendir_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_open_in *in) +{ + bpf_printk("opendir_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, opendir_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_open_in *in, + struct fuse_open_out *out) +{ + bpf_printk("opendir_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, create_open_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_create_in *in, struct fuse_buffer *name) +{ + bpf_printk("create_open_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, create_open_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_create_in *in, const struct fuse_buffer *name, + struct fuse_entry_out *entry_out, struct fuse_open_out *out) +{ + bpf_printk("create_open_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, release_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_release_in *in) +{ + bpf_printk("release_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, release_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_release_in *in) +{ + bpf_printk("release_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, releasedir_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_release_in *in) +{ + bpf_printk("releasedir_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, releasedir_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_release_in *in) +{ + bpf_printk("releasedir_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, flush_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_flush_in *in) +{ + bpf_printk("flush_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, flush_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_flush_in *in) +{ + bpf_printk("flush_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, lseek_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_lseek_in *in) +{ + bpf_printk("lseek_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, lseek_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_lseek_in *in, + struct fuse_lseek_out *out) +{ + bpf_printk("lseek_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, copy_file_range_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_copy_file_range_in *in) +{ + bpf_printk("copy_file_range_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, copy_file_range_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_copy_file_range_in *in, + struct fuse_write_out *out) +{ + bpf_printk("copy_file_range_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, fsync_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_fsync_in *in) +{ + bpf_printk("fsync_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, fsync_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_fsync_in *in) +{ + bpf_printk("fsync_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, dir_fsync_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_fsync_in *in) +{ + bpf_printk("dir_fsync_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, dir_fsync_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_fsync_in *in) +{ + bpf_printk("dir_fsync_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, getxattr_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_getxattr_in *in, struct fuse_buffer *name) +{ + bpf_printk("getxattr_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, getxattr_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_getxattr_in *in, const struct fuse_buffer *name, + struct fuse_buffer *value, struct fuse_getxattr_out *out) +{ + bpf_printk("getxattr_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, listxattr_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_getxattr_in *in) +{ + bpf_printk("listxattr_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, listxattr_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_getxattr_in *in, + struct fuse_buffer *value, struct fuse_getxattr_out *out) +{ + bpf_printk("listxattr_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, setxattr_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_setxattr_in *in, struct fuse_buffer *name, + struct fuse_buffer *value) +{ + bpf_printk("setxattr_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, setxattr_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_setxattr_in *in, const struct fuse_buffer *name, + const struct fuse_buffer *value) +{ + bpf_printk("setxattr_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, removexattr_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_buffer *name) +{ + bpf_printk("removexattr_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, removexattr_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_buffer *name) +{ + bpf_printk("removexattr_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, read_iter_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_read_in *in) +{ + bpf_printk("read_iter_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, read_iter_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_read_in *in, + struct fuse_read_iter_out *out) +{ + bpf_printk("read_iter_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, write_iter_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_write_in *in) +{ + bpf_printk("write_iter_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, write_iter_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_write_in *in, + struct fuse_write_iter_out *out) +{ + bpf_printk("write_iter_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, file_fallocate_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_fallocate_in *in) +{ + bpf_printk("file_fallocate_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, file_fallocate_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_fallocate_in *in) +{ + bpf_printk("file_fallocate_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, lookup_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_buffer *name) +{ + bpf_printk("lookup_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, lookup_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_buffer *name, + struct fuse_entry_out *out, struct fuse_buffer *entries) +{ + bpf_printk("lookup_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, mknod_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_mknod_in *in, struct fuse_buffer *name) +{ + bpf_printk("mknod_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, mknod_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_mknod_in *in, const struct fuse_buffer *name) +{ + bpf_printk("mknod_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, mkdir_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_mkdir_in *in, struct fuse_buffer *name) +{ + bpf_printk("mkdir_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, mkdir_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_mkdir_in *in, const struct fuse_buffer *name) +{ + bpf_printk("mkdir_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, rmdir_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_buffer *name) +{ + bpf_printk("rmdir_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, rmdir_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_buffer *name) +{ + bpf_printk("rmdir_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, rename2_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_rename2_in *in, struct fuse_buffer *old_name, + struct fuse_buffer *new_name) +{ + bpf_printk("rename2_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, rename2_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_rename2_in *in, const struct fuse_buffer *old_name, + const struct fuse_buffer *new_name) +{ + bpf_printk("rename2_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, rename_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_rename_in *in, struct fuse_buffer *old_name, + struct fuse_buffer *new_name) +{ + bpf_printk("rename_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, rename_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_rename_in *in, const struct fuse_buffer *old_name, + const struct fuse_buffer *new_name) +{ + bpf_printk("rename_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, unlink_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_buffer *name) +{ + bpf_printk("unlink_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, unlink_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_buffer *name) +{ + bpf_printk("unlink_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, link_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_link_in *in, struct fuse_buffer *name) +{ + bpf_printk("link_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, link_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_link_in *in, const struct fuse_buffer *name) +{ + bpf_printk("link_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, getattr_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_getattr_in *in) +{ + bpf_printk("getattr_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, getattr_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_getattr_in *in, + struct fuse_attr_out *out) +{ + bpf_printk("getattr_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, setattr_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_setattr_in *in) +{ + bpf_printk("setattr_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, setattr_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_setattr_in *in, + struct fuse_attr_out *out) +{ + bpf_printk("setattr_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, statfs_prefilter, const struct bpf_fuse_meta_info *meta) +{ + bpf_printk("statfs_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, statfs_postfilter, const struct bpf_fuse_meta_info *meta, + struct fuse_statfs_out *out) +{ + bpf_printk("statfs_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, get_link_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_buffer *name) +{ + bpf_printk("get_link_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, get_link_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_buffer *name) +{ + bpf_printk("get_link_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, symlink_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_buffer *name, struct fuse_buffer *path) +{ + bpf_printk("symlink_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, symlink_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_buffer *name, const struct fuse_buffer *path) +{ + bpf_printk("symlink_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, readdir_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_read_in *in) +{ + bpf_printk("readdir_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, readdir_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_read_in *in, + struct fuse_read_out *out, struct fuse_buffer *buffer) +{ + bpf_printk("readdir_postfilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, access_prefilter, const struct bpf_fuse_meta_info *meta, + struct fuse_access_in *in) +{ + bpf_printk("access_prefilter"); + return BPF_FUSE_CONTINUE; +} + +BPF_STRUCT_OPS(uint32_t, access_postfilter, const struct bpf_fuse_meta_info *meta, + const struct fuse_access_in *in) +{ + bpf_printk("access_postfilter"); + return BPF_FUSE_CONTINUE; +} + +SEC(".struct_ops") +struct fuse_ops trace_ops = { + .open_prefilter = (void *)open_prefilter, + .open_postfilter = (void *)open_postfilter, + + .opendir_prefilter = (void *)opendir_prefilter, + .opendir_postfilter = (void *)opendir_postfilter, + + .create_open_prefilter = (void *)create_open_prefilter, + .create_open_postfilter = (void *)create_open_postfilter, + + .release_prefilter = (void *)release_prefilter, + .release_postfilter = (void *)release_postfilter, + + .releasedir_prefilter = (void *)releasedir_prefilter, + .releasedir_postfilter = (void *)releasedir_postfilter, + + .flush_prefilter = (void *)flush_prefilter, + .flush_postfilter = (void *)flush_postfilter, + + .lseek_prefilter = (void *)lseek_prefilter, + .lseek_postfilter = (void *)lseek_postfilter, + + .copy_file_range_prefilter = (void *)copy_file_range_prefilter, + .copy_file_range_postfilter = (void *)copy_file_range_postfilter, + + .fsync_prefilter = (void *)fsync_prefilter, + .fsync_postfilter = (void *)fsync_postfilter, + + .dir_fsync_prefilter = (void *)dir_fsync_prefilter, + .dir_fsync_postfilter = (void *)dir_fsync_postfilter, + + .getxattr_prefilter = (void *)getxattr_prefilter, + .getxattr_postfilter = (void *)getxattr_postfilter, + + .listxattr_prefilter = (void *)listxattr_prefilter, + .listxattr_postfilter = (void *)listxattr_postfilter, + + .setxattr_prefilter = (void *)setxattr_prefilter, + .setxattr_postfilter = (void *)setxattr_postfilter, + + .removexattr_prefilter = (void *)removexattr_prefilter, + .removexattr_postfilter = (void *)removexattr_postfilter, + + .read_iter_prefilter = (void *)read_iter_prefilter, + .read_iter_postfilter = (void *)read_iter_postfilter, + + .write_iter_prefilter = (void *)write_iter_prefilter, + .write_iter_postfilter = (void *)write_iter_postfilter, + + .file_fallocate_prefilter = (void *)file_fallocate_prefilter, + .file_fallocate_postfilter = (void *)file_fallocate_postfilter, + + .lookup_prefilter = (void *)lookup_prefilter, + .lookup_postfilter = (void *)lookup_postfilter, + + .mknod_prefilter = (void *)mknod_prefilter, + .mknod_postfilter = (void *)mknod_postfilter, + + .mkdir_prefilter = (void *)mkdir_prefilter, + .mkdir_postfilter = (void *)mkdir_postfilter, + + .rmdir_prefilter = (void *)rmdir_prefilter, + .rmdir_postfilter = (void *)rmdir_postfilter, + + .rename2_prefilter = (void *)rename2_prefilter, + .rename2_postfilter = (void *)rename2_postfilter, + + .rename_prefilter = (void *)rename_prefilter, + .rename_postfilter = (void *)rename_postfilter, + + .unlink_prefilter = (void *)unlink_prefilter, + .unlink_postfilter = (void *)unlink_postfilter, + + .link_prefilter = (void *)link_prefilter, + .link_postfilter = (void *)link_postfilter, + + .getattr_prefilter = (void *)getattr_prefilter, + .getattr_postfilter = (void *)getattr_postfilter, + + .setattr_prefilter = (void *)setattr_prefilter, + .setattr_postfilter = (void *)setattr_postfilter, + + .statfs_prefilter = (void *)statfs_prefilter, + .statfs_postfilter = (void *)statfs_postfilter, + + .get_link_prefilter = (void *)get_link_prefilter, + .get_link_postfilter = (void *)get_link_postfilter, + + .symlink_prefilter = (void *)symlink_prefilter, + .symlink_postfilter = (void *)symlink_postfilter, + + .readdir_prefilter = (void *)readdir_prefilter, + .readdir_postfilter = (void *)readdir_postfilter, + + .access_prefilter = (void *)access_prefilter, + .access_postfilter = (void *)access_postfilter, + + .name = "trace_pre_ops", +};