From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp1.migadu.com ([2001:41d0:303:e224::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms13.migadu.com with LMTPS id AIKEF9FgNmc1TwEA62LTzQ:P1 (envelope-from ) for ; Thu, 14 Nov 2024 20:42:57 +0000 Received: from aspmx1.migadu.com ([2001:41d0:303:e224::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp1.migadu.com with LMTPS id AIKEF9FgNmc1TwEA62LTzQ (envelope-from ) for ; Thu, 14 Nov 2024 21:42:57 +0100 X-Envelope-To: larch@yhetil.org Authentication-Results: aspmx1.migadu.com; dkim=fail ("body hash did not verify") header.d=debbugs.gnu.org header.s=debbugs-gnu-org header.b=LjCZUd+j; dkim=fail ("body hash did not verify") header.d=dokmelody.org header.s=mail header.b=GTHHujfd; dmarc=fail reason="SPF not aligned (relaxed)" header.from=dokmelody.org (policy=none); spf=pass (aspmx1.migadu.com: domain of "guix-patches-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="guix-patches-bounces+larch=yhetil.org@gnu.org" ARC-Seal: i=1; s=key1; d=yhetil.org; t=1731616977; a=rsa-sha256; cv=none; b=HalEmz7h8LCdvpesfhd10FXjQzulMGy9nDD0YSEE0yC4esKHsOOXgkgPQMyVrhWMA9tEgb 8U1qqoSqzfefoxZ+QWFyoqw1+rTYGKsT0GyRl1xZUZOjurz1Z76I6XfiuTyL1KM9USYUN/ SBMIuEqcQUgORDhFSOAQJlu24wZBkv/Ofg6xX0N11PSkgzHB0Fo136PnVzD8s+gWlwyFqg VkppLazwnDxcpG3Ok3C2gfv1uwm1//M5vbWi+hF35VRkc+f9VG2f1g23/XQg4bJrFhvqld Qrjyp/nqMQpXx3Ctff7L7TtZzInZ4AXXNJidXw0hcmKuxYOETxSfRoipV8wRwg== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=fail ("body hash did not verify") header.d=debbugs.gnu.org header.s=debbugs-gnu-org header.b=LjCZUd+j; dkim=fail ("body hash did not verify") header.d=dokmelody.org header.s=mail header.b=GTHHujfd; dmarc=fail reason="SPF not aligned (relaxed)" header.from=dokmelody.org (policy=none); spf=pass (aspmx1.migadu.com: domain of "guix-patches-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="guix-patches-bounces+larch=yhetil.org@gnu.org" ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1731616977; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding:resent-cc: resent-from:resent-sender:resent-message-id:in-reply-to:in-reply-to: references:references:list-id:list-help:list-unsubscribe: list-subscribe:list-post:dkim-signature; bh=nYJuivaiNy7HScSVjGxbDjXnvcnCa/DVjk/EscZZiJI=; b=E/FA/aUxWd1Kw4SVpz3TzfMo607gcit8UyEflCS+qMzvC8zvRJRN6rqWH363q5RlUhEKWu RR11oSu125GLrUOs4a1eVe67UvHnytfDmu/2NT8AFTGwzRrxt1CoFZoRN8QpWzl9Kw+RKf gYL4V16dL8Ge/qloKgwtKZ+ZpaOZ71BAFwpWmLzKwSHvgnafWLEIU84grzBI3CxsHT2WE2 yWDweYqMcc+30Hhm8tIbQSQSVCyvGKczNX9jeN+nAefk1QSiuwynC/vaEvpXE87Hu7x6Cl GPfvI7dRXwvMnmNQo9oPGcEg3iS2OuHboW5+oejOdJc1QSr5GqttXvJiZuSung== Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id CE9817845 for ; Thu, 14 Nov 2024 21:42:56 +0100 (CET) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tBfRs-0006N5-8v; Thu, 14 Nov 2024 14:24:12 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tBfRk-0006LL-Bk for guix-patches@gnu.org; Thu, 14 Nov 2024 14:24:05 -0500 Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1tBfRj-000179-RP; Thu, 14 Nov 2024 14:24:04 -0500 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=debbugs.gnu.org; s=debbugs-gnu-org; h=MIME-Version:Date:From:To:In-Reply-To:References:Subject; bh=KBz+yjaD20EQUVPAvq6+ftL0d6s6YB5AeydVawdTx+A=; b=LjCZUd+jSGhxEreJt1vjcHfNoUDnZTAm/qbak7AQJd3pfKixYrnLn9M8esxduSdA/Xuf85Ev1+o3fbsrkNNtS5G36UhZ/nyAw9EqOPEPUHDdKZ+TDSpIEZFAkQG8d3fckFaCwC3/01LfCXgO9tn4jOBH/guEyHnh3TKRBm2fOuyxkRTDAVvIo+vxpTUuueCippfmCY77+9hmzBkSxD0bwNRKzHXq5+C4WbWjlcJZaC/8YTHKx6CzvUrpHLFfkNolEbaGiBzXO1HqN8lhWjhu327MGRY20mJeRwUgADXLJVKdQ3ND8B/i3ZtwmR/CaT/wCcTlrXo50XDEGxf7vIZmSQ==; Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1tBfRi-0007Sk-5m; Thu, 14 Nov 2024 14:24:02 -0500 X-Loop: help-debbugs@gnu.org Subject: [bug#74273] [PATCH v2] Improve bcachefs support References: <2142f04036761f24a045a176098b1d0f958ce3bf.1731111823.git.mzan@dokmelody.org> In-Reply-To: <2142f04036761f24a045a176098b1d0f958ce3bf.1731111823.git.mzan@dokmelody.org> Resent-From: Massimo Zaniboni Original-Sender: "Debbugs-submit" Resent-CC: guix@cbaines.net, dev@jpoiret.xyz, ludo@gnu.org, othacehe@gnu.org, maxim.cournoyer@gmail.com, zimon.toutoune@gmail.com, me@tobias.gr, guix-patches@gnu.org Resent-Date: Thu, 14 Nov 2024 19:24:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 74273 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: patch To: 74273@debbugs.gnu.org Cc: Massimo Zaniboni , Christopher Baines , Josselin Poiret , Ludovic =?UTF-8?Q?Court=C3=A8s?= , Mathieu Othacehe , Maxim Cournoyer , Simon Tournier , Tobias Geerinckx-Rice X-Debbugs-Original-Xcc: Christopher Baines , Josselin Poiret , Ludovic =?UTF-8?Q?Court=C3=A8s?= , Mathieu Othacehe , Maxim Cournoyer , Simon Tournier , Tobias Geerinckx-Rice Received: via spool by 74273-submit@debbugs.gnu.org id=B74273.173161222228641 (code B ref 74273); Thu, 14 Nov 2024 19:24:02 +0000 Received: (at 74273) by debbugs.gnu.org; 14 Nov 2024 19:23:42 +0000 Received: from localhost ([127.0.0.1]:47215 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1tBfRN-0007Rs-8e for submit@debbugs.gnu.org; Thu, 14 Nov 2024 14:23:42 -0500 Received: from mail.asterisell.com ([193.30.121.134]:46140) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1tBfRI-0007RZ-Nc for 74273@debbugs.gnu.org; Thu, 14 Nov 2024 14:23:39 -0500 From: Massimo Zaniboni DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dokmelody.org; s=mail; t=1731612177; bh=KBz+yjaD20EQUVPAvq6+ftL0d6s6YB5AeydVawdTx+A=; h=From:To:Cc:Subject:Date; b=GTHHujfdPcdduujH0fXyk1Noo+ZqedgidLPptSitU91Rv+ACFmJAoTuOgrPJ4/aPH R7cu3+X3TeWugecezPxWZJFFRxqB/6paVg7agt87oL2C1Ha8dfeJ7cIoX7rAhtJLG7 9r/fOQPNV9iQQi6Z9CGW4/I4JUcekkmMZ4Vujt/g= Date: Thu, 14 Nov 2024 20:18:56 +0100 Message-ID: <6c9b20a33f44c3412a094ba0f75b9a6cb4eecb02.1731611936.git.mzan@dokmelody.org> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: guix-patches@gnu.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-patches-bounces+larch=yhetil.org@gnu.org Sender: guix-patches-bounces+larch=yhetil.org@gnu.org X-Migadu-Flow: FLOW_IN X-Migadu-Country: US X-Migadu-Spam-Score: 4.19 X-Spam-Score: 4.19 X-Migadu-Queue-Id: CE9817845 X-Migadu-Scanner: mx12.migadu.com X-TUID: oDt//Yzm/Yn8 Improve bcachefs support: - recognize multi-device setup; - mount degraded file-system with missing devices; - use the built-in kernel fscheck instead of user-space bcachefs-tools; Change-Id: Ic741b70a7bce930da02c821c83c0a060875f4771 --- doc/guix.texi | 22 ++++++++ gnu/build/file-systems.scm | 105 ++++++++++++++++++++++++++++++------ gnu/build/linux-boot.scm | 3 +- gnu/machine/ssh.scm | 23 +++++++- gnu/system/file-systems.scm | 15 ++++++ guix/scripts/system.scm | 25 ++++++++- 6 files changed, 175 insertions(+), 18 deletions(-) diff --git a/doc/guix.texi b/doc/guix.texi index 2ab78d6..d962536 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -18152,6 +18152,28 @@ Btrfs file system compress-force=zstd,space_cache=v2")) @end lisp +@menu +* Bcachefs file system:: +@end menu + +@node Bcachefs file system +@subsection Bcachefs file system + +Bcachefs supports RAID1/10-style redundancy, replicating data across multiple devices. +To mount a file system with potentially missing devices but all data intact, +the @code{degraded} option is required. This is an example of a multi-device setup: + +@lisp +(file-system + (mount-point "/home") + (device "/dev/sdb:/dev/sdc:/dev/sdd") + (type "bcachefs") + (options "degraded") +@end lisp + +Currently, bcachefs cannot be used as the root file-system in Guix, +nor can it contain the Guix store. + @node Mapped Devices @section Mapped Devices diff --git a/gnu/build/file-systems.scm b/gnu/build/file-systems.scm index 6fd9f95..89ef18c 100644 --- a/gnu/build/file-systems.scm +++ b/gnu/build/file-systems.scm @@ -10,6 +10,7 @@ ;;; Copyright © 2024 Nicolas Graves ;;; Copyright © 2024 Richard Sent ;;; Copyright © 2024 Janneke Nieuwenhuizen +;;; Copyright © 2024 Massimo Zaniboni ;;; ;;; This file is part of GNU Guix. ;;; @@ -348,6 +349,8 @@ (define-syntax %bcachefs-endianness ;; Endianness of bcachefs file systems. (identifier-syntax (endianness little))) +;; FIXME at least since Linux kernel 6.11, the superblock is not +;; recognized anymore. (define (bcachefs-superblock? sblock) "Return #t when SBLOCK is an bcachefs superblock." (bytevector=? (sub-bytevector sblock 24 16) @@ -1143,10 +1146,10 @@ (define find-partition-by-luks-uuid (find-partition luks-partition-uuid-predicate)) -(define (canonicalize-device-spec spec) - "Return the device name corresponding to SPEC, which can be a , a -, the string 'none' or another string (typically a /dev -file name or an nfs-root containing ':/')." +(define* (canonicalize-device-spec spec #:optional file-system-type) + "Return, usually at boot-time, the device name corresponding to SPEC, +which can be a , a , the string 'none' +or another string like a device, a multi-device, file name, nfs-root." (define max-trials ;; Number of times we retry partition label resolution, 1 second per ;; trial. Note: somebody reported a delay of 16 seconds (!) before their @@ -1154,6 +1157,11 @@ (define (canonicalize-device-spec spec) ;; this long. 20) + (define file-system-type-str + (if (string? file-system-type) + file-system-type + "unknown")) + (define (resolve find-partition spec fmt) (let loop ((count 0)) (let ((device (find-partition spec))) @@ -1168,20 +1176,73 @@ (define (canonicalize-device-spec spec) (sleep 1) (loop (+ 1 count)))))))) + (define (stat-device device) + (stat device #f)) + + (define (check-bcachefs-superblock dev) + (= 0 (system*/tty "bcachefs" "show-super" "--field-only" "disk_groups" dev))) + + (define (resolve-bcachefs-multi-device multi-device) + (let ((devices (string-split multi-device #\:))) + ;; Some devices take a bit of time to appear, most notably USB + ;; storage devices. Thus, wait for the device to appear. + ;; NOTE: it will wait MAX-TRIALS for all the devices, + ;; and not for any device. + (let loop + ((count 0)) + (let ((missing-dev (find (lambda (d) (not (stat-device d))) devices))) + (when (and missing-dev (<= count max-trials)) + (format #t "waiting for device '~a' to appear...~%" missing-dev) + (sleep 1) + (loop (+ 1 count))))) + + ;; bcachefs can work in degraded mode using only few of the devices. + ;; As of Linux kernel 6.11.6, it requires that the missing/fault + ;; devices are removed from the multi-device specification, + ;; and that it is mounted with the "degraded" option. + (let ((valid-specs + (filter + (lambda (d) (and (stat-device d) + (check-bcachefs-superblock d))) + devices))) + (if (null? valid-specs) + (error "failed to resolve multi-device " multi-device)) + (string-join valid-specs ":")))) + (match spec ((? string?) - (if (or (string-contains spec ":/") ;nfs - (and (>= (string-length spec) 2) - (equal? (string-take spec 2) "//")) ;cifs - (string=? spec "none")) - spec ; do not resolve NFS / CIFS / tmpfs devices - ;; Nothing to do, but wait until SPEC shows up. - (resolve identity spec identity))) + (cond + ((multi-device-spec? spec) + (cond + ((string=? file-system-type-str "bcachefs") + (resolve-bcachefs-multi-device spec)) + (else (error + (string-append + "unsupported multi-device specification " + spec + " for file-system type " + file-system-type-str))))) + ((string-contains spec ":/") + ;NFS, something like 'server:/some/path' + spec) + ((and (>= (string-length spec) 2) + (equal? (string-take spec 2) "//")) + ;CIFS + spec) + ((string=? spec "none") + ;tmpfs + spec) + (else + ;; Nothing to do, but wait until SPEC shows up. + ; TODO it should use STAT on some devices instead of IDENTITY. + ; But using STAT on all DEVICES, the boot process will block. + ; At least, all other devices specified using labels and UUID are + ; processed using the more robust STAT function. + (resolve identity spec identity)))) ((? file-system-label?) ;; Resolve the label. (resolve find-partition-by-label - (file-system-label->string spec) - identity)) + (file-system-label->string spec) identity)) ((? uuid?) (resolve find-partition-by-uuid (uuid-bytevector spec) @@ -1194,10 +1255,24 @@ (define (check-file-system device type force? repair) found. Otherwise, fix only those considered safe to repair automatically. Not all TYPEs support all values or combinations of FORCE? and REPAIR. Don't throw an exception in such cases but perform the nearest sane action." + + (define (built-in-file-system-check device force? repair) + 'pass) + (define check-procedure (cond ((string-prefix? "ext" type) check-ext2-file-system) - ((string-prefix? "bcachefs" type) check-bcachefs-file-system) + ((string-prefix? "bcachefs" type) + ;; According bcachefs manual: "No special handling is needed for recovering + ;; from unclean shutdown. Journal replay happens automatically, + ;; and diagnostic messages in the dmesg log will indicate whether recovery + ;; was from clean or unclean shutdown." + ;; Moreover, at least until Linux kernel 6.11, the bcachefs-tools package + ;; does not try to respect the bcachefs format supported by the kernel. + ;; So, the fsck of bcachefs-tools is called only if explicitely stated. + (if force? + check-bcachefs-file-system + built-in-file-system-check)) ((string-prefix? "btrfs" type) check-btrfs-file-system) ((string-suffix? "exfat" type) check-exfat-file-system) ((string-suffix? "fat" type) check-fat-file-system) @@ -1385,7 +1460,7 @@ (define* (mount-file-system fs #:key (root "/root") ""))))) (let* ((type (file-system-type fs)) - (source (canonicalize-device-spec (file-system-device fs))) + (source (canonicalize-device-spec (file-system-device fs) type)) (target (string-append root "/" (file-system-mount-point fs))) (flags (logior (mount-flags->bit-mask (file-system-flags fs)) diff --git a/gnu/build/linux-boot.scm b/gnu/build/linux-boot.scm index 548e28a..d184fcd 100644 --- a/gnu/build/linux-boot.scm +++ b/gnu/build/linux-boot.scm @@ -635,7 +635,8 @@ (define* (boot-system #:key ;; Mount the root file system. (mount-root-file-system (canonicalize-device-spec - (file-system-device root-fs)) + (file-system-device root-fs) + (file-system-type root-fs)) (file-system-type root-fs) #:volatile-root? volatile-root? #:flags (mount-flags->bit-mask diff --git a/gnu/machine/ssh.scm b/gnu/machine/ssh.scm index 3e10d98..0054adf 100644 --- a/gnu/machine/ssh.scm +++ b/gnu/machine/ssh.scm @@ -2,6 +2,7 @@ ;;; Copyright © 2019 Jakob L. Kreuze ;;; Copyright © 2020-2023 Ludovic Courtès ;;; Copyright © 2024 Ricardo +;;; Copyright © 2024 Massimo Zaniboni ;;; ;;; This file is part of GNU Guix. ;;; @@ -241,6 +242,22 @@ (define (machine-check-file-system-availability machine) (file-system-device fs) (strerror errno)))))) + (define (check-multi-device-file-system fs) + (define multi-device (file-system-device fs)) + (define devices (string-split multi-device #\:)) + (define (check-device device) + (remote-let ((errno #~(catch 'system-error + (lambda () + (stat #$device) + #t) + (lambda args + (system-error-errno args))))) + (when (number? errno) + (raise (formatted-message (G_ "device '~a' not found: ~a") + device + (strerror errno)))))) + (map check-device devices)) + (define (check-labeled-file-system fs) (define remote-exp (with-imported-modules (source-module-closure @@ -278,8 +295,12 @@ (define (machine-check-file-system-availability machine) (machine-configuration machine)) (append (map check-literal-file-system (filter (lambda (fs) - (string? (file-system-device fs))) + (single-device-spec? (file-system-device fs))) file-systems)) + (append-map check-multi-device-file-system + (filter (lambda (fs) + (multi-device-spec? (file-system-device fs))) + file-systems)) (map check-labeled-file-system (filter (lambda (fs) (file-system-label? (file-system-device fs))) diff --git a/gnu/system/file-systems.scm b/gnu/system/file-systems.scm index 4ea8237..9f91bd7 100644 --- a/gnu/system/file-systems.scm +++ b/gnu/system/file-systems.scm @@ -5,6 +5,7 @@ ;;; Copyright © 2020, 2021 Maxim Cournoyer ;;; Copyright © 2021 Tobias Geerinckx-Rice ;;; Copyright © 2022 Oleg Pykhalov +;;; Copyright © 2024 Massimo Zaniboni ;;; ;;; This file is part of GNU Guix. ;;; @@ -73,6 +74,9 @@ (define-module (gnu system file-systems) spec->file-system specification->file-system-mapping + multi-device-spec? + single-device-spec? + %pseudo-file-system-types %fuse-control-file-system %binary-format-file-system @@ -309,6 +313,17 @@ (define (file-system-needed-for-boot? fs) (and (file-prefix? (file-system-mount-point fs) (%store-prefix)) (not (memq 'bind-mount (file-system-flags fs)))))) +(define (multi-device-spec? spec) + "Return #t if the specification is like '/dev/sda:/dev/sdb'." + (and (string? spec) + (string-contains spec ":/") + (string-prefix? "/dev/" spec))) + +(define (single-device-spec? spec) + "Return #t if the specification is a string, but not a multi-device." + (and (string? spec) + (not (multi-device-spec? spec)))) + (define (file-system->spec fs) "Return a list corresponding to file-system FS that can be passed to the initrd code." diff --git a/guix/scripts/system.scm b/guix/scripts/system.scm index 7989b18..4e9c581 100644 --- a/guix/scripts/system.scm +++ b/guix/scripts/system.scm @@ -11,6 +11,7 @@ ;;; Copyright © 2021 Brice Waegeneire ;;; Copyright © 2021 Simon Tournier ;;; Copyright © 2022 Tobias Geerinckx-Rice +;;; Copyright © 2024 Massimo Zaniboni ;;; ;;; This file is part of GNU Guix. ;;; @@ -605,9 +606,16 @@ (define (check-file-system-availability file-systems) (file-system-label? (file-system-device fs))) relevant)) + (define multi-device + (filter (lambda (fs) + (and (string? (file-system-device fs)) + (multi-device-spec? (file-system-device fs)))) + relevant)) + (define literal (filter (lambda (fs) - (string? (file-system-device fs))) + (and (string? (file-system-device fs)) + (single-device-spec? (file-system-device fs)))) relevant)) (define uuid @@ -641,6 +649,21 @@ (define (check-file-system-availability file-systems) label, write @code{(file-system-label ~s)} in your @code{device} field.") device device)))))) literal) + (for-each + (lambda (fs) + (let* ((devices-str (file-system-device fs)) + (devices (string-split devices-str #\:))) + (for-each + (lambda (device) + (catch 'system-error + (lambda () (stat device)) + (lambda args + (let ((errno (system-error-errno args))) + (error (file-system-location* fs) + (G_ " #8605 device '~a' not found in multi-device '~a': ~a~%") + device devices-str (strerror errno)))))) + devices))) + multi-device) (for-each (lambda (fs) (let ((label (file-system-label->string (file-system-device fs)))) base-commit: c1cb7f1031c5dde2a260d8d8ad7547d6c79cc532 prerequisite-patch-id: e3ec1271b30da286e1a2fdd1519a8c504e52d64a prerequisite-patch-id: 25d78fbfbd3268c16c93cd5d222386a7f421979b prerequisite-patch-id: 8ca774fc68440ec5233b5353f11886b1712e6b43 prerequisite-patch-id: 0000000000000000000000000000000000000000 prerequisite-patch-id: e22870a8d4b3ab67b12e05b6242b7f1bf5ac193b -- 2.46.0