public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
From: Adrian Reber <adrian@lisas.de>
To: git-commits@fedoraproject.org
Subject: [rpms/criu] f43: Route veth restore through usernsd for userns mode (upstream PR#3006)
Date: Mon, 06 Jul 2026 06:07:54 GMT	[thread overview]
Message-ID: <178331807411.1.14890462587482111330.rpms-criu-7ca58ef6a75f@fedoraproject.org> (raw)

            A new commit has been pushed.

            Repo   : rpms/criu
            Branch : f43
            Commit : 7ca58ef6a75fdd015dccfcdda9c4e29d22f3c485
            Author : Adrian Reber <adrian@lisas.de>
            Date   : 2026-04-22T07:20:37+00:00
            Stats  : +444/-1 in 5 file(s)
            URL    : https://src.fedoraproject.org/rpms/criu/c/7ca58ef6a75fdd015dccfcdda9c4e29d22f3c485?branch=f43

            Log:
            Route veth restore through usernsd for userns mode (upstream PR#3006)

Fix rseq01 test for kernel 7.0 rseq changes (upstream PR#3007)
Handle UDPLITE removal in kernel 7.1 (upstream PR#3003)

Signed-off-by: Adrian Reber <adrian@lisas.de>

---
diff --git a/0002-net-Route-veth-restore-through-usernsd-for-userns-mode.patch b/0002-net-Route-veth-restore-through-usernsd-for-userns-mode.patch
new file mode 100644
index 0000000..cbedd55
--- /dev/null
+++ b/0002-net-Route-veth-restore-through-usernsd-for-userns-mode.patch
@@ -0,0 +1,178 @@
+From 7ad6bb97b4e3951fc79cbda51b05e649aea2854a Mon Sep 17 00:00:00 2001
+From: Adrian Reber <areber@redhat.com>
+Date: Tue, 21 Apr 2026 09:04:51 +0000
+Subject: [PATCH] net: Route veth restore through usernsd for userns mode
+
+Starting with Linux kernel commit 7b735ef81286 ("rtnetlink: add
+missing netlink_ns_capable() check for peer netns"), creating a
+veth pair with a peer in a different network namespace requires
+CAP_NET_ADMIN in the peer namespace as well:
+
+    rtnetlink: add missing netlink_ns_capable() check for peer netns
+
+    rtnl_newlink() lacks a CAP_NET_ADMIN capability check on the peer
+    network namespace when creating paired devices (veth, vxcan,
+    netkit). This allows an unprivileged user with a user namespace
+    to create interfaces in arbitrary network namespaces, including
+    init_net.
+
+    Add a netlink_ns_capable() check for CAP_NET_ADMIN in the peer
+    namespace before allowing device creation to proceed.
+
+Link: https://github.com/torvalds/linux/commit/7b735ef81286007794a227ce2539419479c02a5f
+
+When CRIU restores a veth in user namespace mode, it sends the
+RTM_NEWLINK request from a netlink socket inside the child user
+namespace.  The veth peer is placed into the root network namespace
+via IFLA_NET_NS_FD, but the child user namespace does not have
+CAP_NET_ADMIN in the root namespace, so the new kernel check
+rejects the request with EPERM.
+
+Fix this by routing the veth creation through usernsd when the
+peer lives in an external (host) namespace specified via the
+--external veth[name] restore option.  usernsd runs with real
+root privileges in the init user namespace, so it passes the
+capability check in both namespaces.
+
+The usernsd path is only used for external veth mappings, not for
+child-to-child namespace veths (has_peer_nsid), because in that
+case both namespaces share the same user namespace and already
+have CAP_NET_ADMIN in each other.
+
+The approach mirrors restore_one_macvlan(), which already solves
+the same CAP_NET_ADMIN-in-both-namespaces problem for macvlan
+devices.  A new veth_link_info_userns() builds the RTM_NEWLINK
+request without IFLA_NET_NS_FD for the peer -- since usernsd
+sends the request from the root network namespace, the peer
+naturally stays there.  userns_restore_one_link() then adds a
+top-level IFLA_NET_NS_FD to move the main device into the child
+namespace.
+
+Assisted-by: Claude Code (claude-opus-4-6):claude-opus-4-6@default
+Signed-off-by: Adrian Reber <areber@redhat.com>
+---
+ criu/net.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 104 insertions(+)
+
+diff --git a/criu/net.c b/criu/net.c
+index e5775a3287..e1dc5973fd 100644
+--- a/criu/net.c
++++ b/criu/net.c
+@@ -1653,6 +1653,94 @@ static int restore_one_macvlan(struct ns_id *ns, struct net_link *link, int nlsk
+ 	return ret;
+ }
+ 
++/*
++ * Simplified veth_link_info for the usernsd path: builds the veth peer
++ * info without adding IFLA_NET_NS_FD for the peer, since the netlink
++ * request will be sent from usernsd's namespace (the root/peer namespace)
++ * and the peer should remain there.
++ */
++static int veth_link_info_userns(struct ns_id *ns, struct net_link *link, struct newlink_req *req)
++{
++	NetDeviceEntry *nde = link->nde;
++	struct rtattr *veth_data, *peer_data;
++	struct ifinfomsg ifm;
++	char key[100], *val;
++
++	addattr_l(&req->h, sizeof(*req), IFLA_INFO_KIND, "veth", 4);
++
++	veth_data = NLMSG_TAIL(&req->h);
++	addattr_l(&req->h, sizeof(*req), IFLA_INFO_DATA, NULL, 0);
++	peer_data = NLMSG_TAIL(&req->h);
++	memset(&ifm, 0, sizeof(ifm));
++
++	if (nde->has_peer_nsid)
++		ifm.ifi_index = nde->peer_ifindex;
++	addattr_l(&req->h, sizeof(*req), VETH_INFO_PEER, &ifm, sizeof(ifm));
++
++	/* Set peer name from external mapping if available */
++	snprintf(key, sizeof(key), "veth[%s]", nde->name);
++	val = external_lookup_by_key(key);
++	if (!IS_ERR_OR_NULL(val)) {
++		char *aux = strchrnul(val, '@');
++
++		addattr_l(&req->h, sizeof(*req), IFLA_IFNAME, val, aux - val);
++	}
++
++	/*
++	 * No IFLA_NET_NS_FD for the peer -- the request is sent from
++	 * usernsd's namespace (the root network namespace) and the
++	 * peer stays there.  userns_restore_one_link() adds a top-level
++	 * IFLA_NET_NS_FD to move the main device into the child netns.
++	 */
++
++	peer_data->rta_len = (char *)NLMSG_TAIL(&req->h) - (char *)peer_data;
++	veth_data->rta_len = (char *)NLMSG_TAIL(&req->h) - (char *)veth_data;
++
++	link->created = true;
++
++	return 0;
++}
++
++/*
++ * When running in user namespaces with a cross-namespace veth peer,
++ * CAP_NET_ADMIN is required in both namespaces for veth creation
++ * (kernel >= 7.0, commit 7b735ef81286). Route the request through
++ * usernsd which runs with real root privileges, similar to
++ * restore_one_macvlan().
++ *
++ * The netlink request is sent from usernsd's namespace (the root
++ * network namespace), so the veth peer stays there. The main device
++ * is moved to the child namespace via a top-level IFLA_NET_NS_FD
++ * added by userns_restore_one_link().
++ */
++static int restore_one_veth_userns(struct ns_id *ns, struct net_link *link)
++{
++	struct newlink_req req;
++	int my_netns, ret;
++
++	my_netns = open_proc(PROC_SELF, "ns/net");
++	if (my_netns < 0)
++		return -1;
++
++	if (populate_newlink_req(ns, &req, RTM_NEWLINK, link,
++				veth_link_info_userns, NULL) < 0) {
++		close(my_netns);
++		return -1;
++	}
++
++	pr_info("Restoring netdev %s idx %d via usernsd\n",
++		link->nde->name, link->nde->ifindex);
++
++	ret = userns_call(userns_restore_one_link, 0,
++			  &req, sizeof(req), my_netns);
++	if (ret < 0)
++		pr_err("couldn't restore veth interface %s via usernsd\n",
++		       link->nde->name);
++
++	close(my_netns);
++	return ret;
++}
++
+ static int sit_link_info(struct ns_id *ns, struct net_link *link, struct newlink_req *req)
+ {
+ 	NetDeviceEntry *nde = link->nde;
+@@ -1762,6 +1850,22 @@ static int __restore_link(struct ns_id *ns, struct net_link *link, int nlsk)
+ 		if (!IS_ERR_OR_NULL(val))
+ 			return move_veth(val, ns, link, nlsk);
+ 
++		/*
++		 * Creating a veth with a cross-namespace peer requires
++		 * CAP_NET_ADMIN in both namespaces (kernel >= 7.0).
++		 * In user namespace mode, route through usernsd which
++		 * has real root when the peer lives in an external
++		 * (host) namespace specified via --external veth[name].
++		 */
++		if (root_ns_mask & CLONE_NEWUSER) {
++			char vkey[100];
++
++			snprintf(vkey, sizeof(vkey), "veth[%s]", nde->name);
++			val = external_lookup_by_key(vkey);
++			if (!IS_ERR_OR_NULL(val))
++				return restore_one_veth_userns(ns, link);
++		}
++
+ 		return restore_one_link(ns, link, nlsk, veth_link_info, NULL);
+ 	case ND_TYPE__TUN:
+ 		return restore_one_tun(ns, link, nlsk);

diff --git a/0003-zdtm-Fix-rseq01-test-for-kernel-7.0-rseq-changes.patch b/0003-zdtm-Fix-rseq01-test-for-kernel-7.0-rseq-changes.patch
new file mode 100644
index 0000000..e0ead93
--- /dev/null
+++ b/0003-zdtm-Fix-rseq01-test-for-kernel-7.0-rseq-changes.patch
@@ -0,0 +1,163 @@
+From 14574afd322113630b897f5f7d08a292fb06ce5a Mon Sep 17 00:00:00 2001
+From: Adrian Reber <areber@redhat.com>
+Date: Tue, 21 Apr 2026 09:30:17 +0000
+Subject: [PATCH] zdtm: Fix rseq01 test for kernel 7.0 rseq changes
+
+Starting with Linux 7.0, the rseq feature size has grown to 33 bytes
+and AT_RSEQ_ALIGN has increased to 64.  This causes sizeof(struct rseq)
+from the uapi header to be 64 (33 bytes padded to aligned(32)), while
+the actual registration size used by glibc (__rseq_size) is 33.
+
+The rseq01 test was using sizeof(struct rseq) as the registration
+size and a test-local __rseq_abi variable (with only 32-byte
+alignment from the uapi header) as the rseq area.  Both are
+wrong on kernel 7.0:
+
+ - The kernel now checks alignment against __alignof__(struct rseq)
+   which is 64 internally; the 32-byte-aligned test variable may
+   not satisfy this.
+
+ - sizeof(struct rseq) = 64 does not match the feature size of 33
+   that glibc registered with, so re-registration with a different
+   size fails.
+
+Fix by:
+
+ - Using __rseq_size (the feature size reported by glibc) as the
+   registration size instead of sizeof(struct rseq).
+
+ - Using the glibc-provided rseq area (at thread_pointer() +
+   __rseq_offset) which is allocated with proper AT_RSEQ_ALIGN
+   alignment, instead of a test-local variable.
+
+Both fall back to the previous behavior when glibc rseq support
+is not available.
+
+This mirrors the glibc fix:
+
+    https://sourceware.org/cgit/glibc/commit/?id=67f303b47dc584f204e3f2441b9832082415eebc
+
+Assisted-by: Claude Code (claude-opus-4-6):claude-opus-4-6@default
+Signed-off-by: Adrian Reber <areber@redhat.com>
+---
+ test/zdtm/transition/rseq01.c | 59 +++++++++++++++++++++++++++++------
+ 1 file changed, 50 insertions(+), 9 deletions(-)
+
+diff --git a/test/zdtm/transition/rseq01.c b/test/zdtm/transition/rseq01.c
+index 08a7a8e1a6..9c5925bc45 100644
+--- a/test/zdtm/transition/rseq01.c
++++ b/test/zdtm/transition/rseq01.c
+@@ -20,9 +20,19 @@
+ #endif
+ #endif
+ 
++/*
++ * HAVE_GLIBC_RSEQ is set when the C library provides rseq support
++ * (__rseq_size, __rseq_offset).  glibc >= 2.35 defines RSEQ_SIG in
++ * <sys/rseq.h>; musl does not, so the test falls back to its own
++ * definitions below.
++ */
++#if defined(RSEQ_SIG)
++#define HAVE_GLIBC_RSEQ 1
++#endif
++
+ #if defined(__x86_64__)
+ 
+-#if defined(__x86_64__) && defined(RSEQ_SIG)
++#ifdef HAVE_GLIBC_RSEQ
+ static inline void *thread_pointer(void)
+ {
+ 	void *result;
+@@ -38,11 +48,11 @@ static inline void unregister_old_rseq(void)
+ 		size = 32;
+ 	syscall(__NR_rseq, (void *)((char *)thread_pointer() + __rseq_offset), size, 1, RSEQ_SIG);
+ }
+-#else
++#else /* !HAVE_GLIBC_RSEQ */
+ static inline void unregister_old_rseq(void)
+ {
+ }
+-#endif
++#endif /* HAVE_GLIBC_RSEQ */
+ 
+ const char *test_doc = "rseq() transition test";
+ const char *test_author = "Alexander Mikhalitsyn <alexander.mikhalitsyn@virtuozzo.com>";
+@@ -97,11 +107,27 @@ static int sys_rseq(volatile struct rseq *rseq_abi, uint32_t rseq_len, int flags
+ 	return syscall(__NR_rseq, rseq_abi, rseq_len, flags, sig);
+ }
+ 
++/*
++ * Return the rseq registration size.  Starting with Linux 7.0,
++ * AT_RSEQ_ALIGN is 64 but the feature size is 33, so sizeof(struct rseq)
++ * (padded to alignment) no longer matches the registration size the kernel
++ * expects.  Use __rseq_size when available, clamped to a minimum of 32
++ * for older kernels (mirroring glibc's rseq-internal.h).
++ */
++static uint32_t rseq_reg_size(void)
++{
++#ifdef HAVE_GLIBC_RSEQ
++	if (__rseq_size)
++		return (__rseq_size < 32) ? 32 : __rseq_size;
++#endif
++	return sizeof(struct rseq);
++}
++
+ static void register_thread(void)
+ {
+ 	int rc;
+ 	unregister_old_rseq();
+-	rc = sys_rseq(rseq_ptr, sizeof(struct rseq), 0, RSEQ_SIG);
++	rc = sys_rseq(rseq_ptr, rseq_reg_size(), 0, RSEQ_SIG);
+ 	if (rc) {
+ 		fail("Failed to register rseq");
+ 		exit(1);
+@@ -111,7 +137,7 @@ static void register_thread(void)
+ static void check_thread(void)
+ {
+ 	int rc;
+-	rc = sys_rseq(rseq_ptr, sizeof(struct rseq), 0, RSEQ_SIG);
++	rc = sys_rseq(rseq_ptr, rseq_reg_size(), 0, RSEQ_SIG);
+ 	if (!(rc && errno == EBUSY)) {
+ 		fail("Failed to check rseq %d", rc);
+ 		exit(1);
+@@ -206,12 +232,27 @@ static intptr_t *cpu_data;
+ bool ignore_abort = true;
+ int thread_ret;
+ 
++static volatile struct rseq *rseq_area(void)
++{
++#ifdef HAVE_GLIBC_RSEQ
++	/*
++	 * Use the glibc-provided rseq area which is allocated with the
++	 * correct alignment (AT_RSEQ_ALIGN, 64 on kernel >= 7.0).
++	 * The test-local __rseq_abi only has the uapi header alignment
++	 * (32 bytes) which may not satisfy the kernel's requirement.
++	 */
++	if (__rseq_size)
++		return (volatile struct rseq *)((char *)thread_pointer() + __rseq_offset);
++#endif
++	return &__rseq_abi;
++}
++
+ void *thread_routine(void *args)
+ {
+ 	int cpu;
+ 
+-	rseq_ptr = &__rseq_abi;
+-	memset((void *)rseq_ptr, 0, sizeof(struct rseq));
++	rseq_ptr = rseq_area();
++	memset((void *)rseq_ptr, 0, rseq_reg_size());
+ 	register_thread();
+ 	task_waiter_complete(&waiter, 1);
+ 	task_waiter_wait4(&waiter, 2);
+@@ -235,8 +276,8 @@ int main(int argc, char *argv[])
+ 	long nr_cpus;
+ 	pthread_t thread;
+ 
+-	rseq_ptr = &__rseq_abi;
+-	memset((void *)rseq_ptr, 0, sizeof(struct rseq));
++	rseq_ptr = rseq_area();
++	memset((void *)rseq_ptr, 0, rseq_reg_size());
+ 
+ 	test_init(argc, argv);
+ 	nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);

diff --git a/0004-sockets-Treat-UDPLITE-as-optional-in-collect_err.patch b/0004-sockets-Treat-UDPLITE-as-optional-in-collect_err.patch
new file mode 100644
index 0000000..1cd8f12
--- /dev/null
+++ b/0004-sockets-Treat-UDPLITE-as-optional-in-collect_err.patch
@@ -0,0 +1,52 @@
+From 1cf8d3b21c64f0ef3310301019136433fc9f6392 Mon Sep 17 00:00:00 2001
+From: Adrian Reber <areber@redhat.com>
+Date: Mon, 20 Apr 2026 19:19:42 +0000
+Subject: [PATCH] sockets: Treat UDPLITE as optional in collect_err()
+
+Kernel 7.1 removed IPPROTO_UDPLITE support. When the UDPLITE diag
+module is absent, the netlink request returns -ENOENT, which CRIU
+was treating as a fatal error causing the entire dump to fail.
+
+Apply the same treatment as IPPROTO_RAW: return success from the
+error callback so the dump can proceed. If a UDPLITE socket is
+actually encountered, it will fail at lookup time with a clear
+error rather than failing the entire dump preemptively.
+
+Fixes: https://github.com/checkpoint-restore/criu/issues/3002
+
+Assisted-by: Claude Code (claude-opus-4-6):claude-opus-4-6@default
+Signed-off-by: Adrian Reber <areber@redhat.com>
+---
+ criu/sockets.c | 14 +++++++++-----
+ 1 file changed, 9 insertions(+), 5 deletions(-)
+
+diff --git a/criu/sockets.c b/criu/sockets.c
+index e4adae03cd..7778b46881 100644
+--- a/criu/sockets.c
++++ b/criu/sockets.c
+@@ -827,16 +827,20 @@ static int collect_err(int err, struct ns_id *ns, void *arg)
+ 	if (err == -ENOENT) {
+ 		pr_debug("%s: %d\n", msg, err);
+ 		/*
+-		 * Unlike other modules RAW sockets are
+-		 * always optional and not commonly used.
++		 * Unlike other modules RAW and UDPLITE sockets
++		 * are always optional and not commonly used.
+ 		 * Currently we warn user about lack of
+ 		 * a particular module support in "check"
+ 		 * procedure. Thus don't fail on lack of
+-		 * RAW diags in a regular dump. If we meet
+-		 * a raw socket we will simply fail on dump
++		 * these diags in a regular dump. If we meet
++		 * such a socket we will simply fail on dump
+ 		 * procedure because it won't be resolved.
++		 *
++		 * Note: IPPROTO_UDPLITE support was removed
++		 * from the kernel starting with v7.1.
+ 		 */
+-		if (gr->protocol == IPPROTO_RAW)
++		if (gr->protocol == IPPROTO_RAW ||
++		    gr->protocol == IPPROTO_UDPLITE)
+ 			return 0;
+ 		return -ENOENT;
+ 	}

diff --git a/0005-zdtm-Skip-socket_udplite-test-when-kernel-lacks-UDPLITE.patch b/0005-zdtm-Skip-socket_udplite-test-when-kernel-lacks-UDPLITE.patch
new file mode 100644
index 0000000..5d9d5d5
--- /dev/null
+++ b/0005-zdtm-Skip-socket_udplite-test-when-kernel-lacks-UDPLITE.patch
@@ -0,0 +1,37 @@
+From 23905c26b86afdc01bcfa85ae0a3aab015fc81f1 Mon Sep 17 00:00:00 2001
+From: Adrian Reber <areber@redhat.com>
+Date: Tue, 21 Apr 2026 07:58:00 +0000
+Subject: [PATCH] zdtm: Skip socket_udplite test when kernel lacks UDPLITE
+
+Kernel 7.1 removed IPPROTO_UDPLITE support. Add a checkskip
+script that probes for a UDPLITE socket and skips the test
+with EPROTONOSUPPORT instead of failing.
+
+Assisted-by: Claude Code (claude-opus-4-6):claude-opus-4-6@default
+Signed-off-by: Adrian Reber <areber@redhat.com>
+---
+ test/zdtm/static/socket_udplite.checkskip | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+ create mode 100755 test/zdtm/static/socket_udplite.checkskip
+
+diff --git a/test/zdtm/static/socket_udplite.checkskip b/test/zdtm/static/socket_udplite.checkskip
+new file mode 100755
+index 0000000000..a562550843
+--- /dev/null
++++ b/test/zdtm/static/socket_udplite.checkskip
+@@ -0,0 +1,15 @@
++#!/usr/bin/env python3
++# IPPROTO_UDPLITE was removed in kernel 7.1, skip the test if the
++# protocol is not available.
++import socket
++import errno
++
++try:
++	socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDPLITE).close()
++except OSError as e:
++	if e.errno == errno.EPROTONOSUPPORT:
++		print("UDPLITE is not supported by this kernel.")
++		exit(1)
++	raise
++
++exit(0)

diff --git a/criu.spec b/criu.spec
index af083a2..383213a 100644
--- a/criu.spec
+++ b/criu.spec
@@ -13,13 +13,17 @@
 
 Name: criu
 Version: 4.2
-Release: 17%{?dist}
+Release: 18%{?dist}
 Summary: Tool for Checkpoint/Restore in User-space
 License: GPL-2.0-only AND LGPL-2.1-only AND MIT
 URL: http://criu.org/
 Source0: https://github.com/checkpoint-restore/criu/archive/v%{version}/criu-%{version}.tar.gz
 Patch0: 0001-rseq-use-kernel-rseq.h-when-glibc-detects-it.patch
 Patch1: 0001-tty-fix-compiler-error.patch
+Patch2: 0002-net-Route-veth-restore-through-usernsd-for-userns-mode.patch
+Patch3: 0003-zdtm-Fix-rseq01-test-for-kernel-7.0-rseq-changes.patch
+Patch4: 0004-sockets-Treat-UDPLITE-as-optional-in-collect_err.patch
+Patch5: 0005-zdtm-Skip-socket_udplite-test-when-kernel-lacks-UDPLITE.patch
 
 # Add protobuf-c as a dependency.
 # We use this patch because the protobuf-c package name
@@ -118,6 +122,10 @@ This script can help to workaround the so called "PID mismatch" problem.
 %setup -q
 %patch -P 0 -p1
 %patch -P 1 -p1
+%patch -P 2 -p1
+%patch -P 3 -p1
+%patch -P 4 -p1
+%patch -P 5 -p1
 %patch -P 99 -p1
 
 %build
@@ -206,6 +214,11 @@ rm -f $RPM_BUILD_ROOT%{_libdir}/libcriu.a
 %tmpfiles_create %{name}.conf
 
 %changelog
+* Wed Apr 22 2026 Adrian Reber <adrian@lisas.de> - 4.2-18
+- Route veth restore through usernsd for userns mode (upstream PR#3006)
+- Fix rseq01 test for kernel 7.0 rseq changes (upstream PR#3007)
+- Handle UDPLITE removal in kernel 7.1 (upstream PR#3003)
+
 * Thu Mar 26 2026 Adrian Reber <adrian@lisas.de> - 4.2-17
 - Always use nftables network locking backend
 

                 reply	other threads:[~2026-07-06  6:07 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=178331807411.1.14890462587482111330.rpms-criu-7ca58ef6a75f@fedoraproject.org \
    --to=adrian@lisas.de \
    --cc=git-commits@fedoraproject.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox