mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Josef Bacik <josef@toxicpanda.com>
To: Christian Brauner <brauner@kernel.org>,
	 James Bottomley <James.Bottomley@HansenPartnership.com>,
	 Andrew Halaney <andrew@amutable.com>,
	Jeremy Bingham <jbingham@gmail.com>,
	 Tahera Fahimi <taherafahimi@linux.microsoft.com>,
	 Serge Hallyn <sergeh@kernel.org>
Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org,
	 Josef Bacik <josef@toxicpanda.com>
Subject: [PATCH RFC v3 04/90] user_namespace: let a cell own a user namespace
Date: Thu, 05 Nov 2026 14:56:55 +0000	[thread overview]
Message-ID: <20261105-b4-cells-v3-4-bc9095937a84@toxicpanda.com> (raw)
In-Reply-To: <20261105-b4-cells-v3-0-bc9095937a84@toxicpanda.com>

Every user of a kuid in the kernel already knows how to deal with user
namespaces. So give each cell one user namespace, which the kernel creates
together with the cell.

Add create_cell_user_ns(). The namespace holds a reference on its cell for
as long as the namespace exists. The pointer to the cell is set before
anybody else can find the namespace.

This makes the user namespace the link from a credential to its cell. Add
the accessors cred_cell(), current_cell(), task_cell() and a few more.
They return NULL for a task that is in no cell. Because the cell is found
through the credential, override_creds() and file->f_cred work without any
extra code.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 include/linux/cell.h           | 148 +++++++++++++++++++++++++++++++++++++++++
 include/linux/cell_ids.h       |   4 ++
 include/linux/user_namespace.h |   7 ++
 kernel/user_namespace.c        |  56 +++++++++++++++-
 4 files changed, 214 insertions(+), 1 deletion(-)

diff --git a/include/linux/cell.h b/include/linux/cell.h
new file mode 100644
index 000000000000..6a65d035ffca
--- /dev/null
+++ b/include/linux/cell.h
@@ -0,0 +1,148 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_CELL_H
+#define _LINUX_CELL_H
+
+/*
+ * Cells, see Documentation/userspace-api/cells.rst.
+ *
+ * Each group of declarations below comes with its own stubs for a kernel
+ * without CONFIG_CELLS.
+ */
+
+#include <linux/cell_ids.h>
+#include <linux/cred.h>
+#include <linux/refcount.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/uidgid.h>
+#include <linux/user_namespace.h>
+
+struct cell;
+struct cgroup;
+struct file;
+struct kernel_clone_args;
+struct nsproxy;
+struct path;
+struct pid;
+struct task_struct;
+
+/* Who is in which cell. */
+#ifdef CONFIG_CELLS
+
+/**
+ * userns_cell - the cell a user namespace belongs to
+ * @ns: a user namespace
+ *
+ * Return: the cell, or NULL if @ns is a legacy user namespace. No reference is
+ * taken: @ns holds one for as long as it lives.
+ */
+static inline struct cell *userns_cell(const struct user_namespace *ns)
+{
+	return ns->cell;
+}
+
+/**
+ * cred_cell - the cell a credential belongs to
+ * @cred: a credential
+ *
+ * The cell is reached through the user namespace of the credential, so it
+ * follows override_creds() and file->f_cred without any help.
+ *
+ * Return: the cell, or NULL for the host. No reference is taken: @cred pins its
+ * user namespace, which pins the cell.
+ */
+static inline struct cell *cred_cell(const struct cred *cred)
+{
+	return userns_cell(cred->user_ns);
+}
+
+/**
+ * current_cell - the cell the caller acts in
+ *
+ * Goes by the subjective credentials, so a caller that has overridden its
+ * credentials acts in the cell of those.
+ *
+ * Return: the cell, or NULL for the host.
+ */
+static inline struct cell *current_cell(void)
+{
+	return cred_cell(current_cred());
+}
+
+/**
+ * task_cell - the cell a task is in
+ * @task: a task
+ *
+ * A task is born in a cell and never changes cell.
+ *
+ * Context: The caller holds rcu_read_lock(), or otherwise keeps the objective
+ * credentials of @task from going away.
+ * Return: the cell, or NULL for the host.
+ */
+static inline struct cell *task_cell(const struct task_struct *task)
+{
+	return cred_cell(__task_cred(task));
+}
+
+/**
+ * cell_same - do two credentials belong to the same cell?
+ * @a: a credential
+ * @b: another
+ *
+ * Across cells an equal uid means nothing: a one-id cell runs on the kuid of
+ * whoever made it. A check that lets one task act on another because their uids
+ * are equal asks this as well, and otherwise falls back to whatever capability
+ * check it has.
+ *
+ * Return: true if both belong to the same cell, or both to the host.
+ */
+static inline bool cell_same(const struct cred *a, const struct cred *b)
+{
+	return cred_cell(a) == cred_cell(b);
+}
+
+u64 cell_id(const struct cell *cell);
+
+/**
+ * cred_cell_id - the id of the cell a credential belongs to
+ * @cred: a credential
+ *
+ * For where a cell has to be remembered beyond the life of anything that pins
+ * it. Ids are never reused, so they can be compared at any later time.
+ *
+ * Return: the id of the cell, or 0 for the host.
+ */
+static inline u64 cred_cell_id(const struct cred *cred)
+{
+	struct cell *cell = cred_cell(cred);
+
+	return cell ? cell_id(cell) : 0;
+}
+
+/**
+ * current_cell_id - the id of the cell the caller acts in
+ *
+ * Return: the id of the cell, or 0 for the host.
+ */
+static inline u64 current_cell_id(void)
+{
+	return cred_cell_id(current_cred());
+}
+
+struct cell *cell_get(struct cell *cell);
+void cell_put(struct cell *cell);
+
+#else
+
+static inline struct cell *userns_cell(const struct user_namespace *ns) { return NULL; }
+static inline struct cell *cred_cell(const struct cred *cred) { return NULL; }
+static inline struct cell *current_cell(void) { return NULL; }
+static inline struct cell *task_cell(const struct task_struct *t) { return NULL; }
+static inline bool cell_same(const struct cred *a, const struct cred *b) { return true; }
+static inline u64 cred_cell_id(const struct cred *cred) { return 0; }
+static inline u64 current_cell_id(void) { return 0; }
+static inline void cell_put(struct cell *cell) { }
+
+#endif
+
+#endif /* _LINUX_CELL_H */
diff --git a/include/linux/cell_ids.h b/include/linux/cell_ids.h
index df0b874d5788..dcb0a559d0e9 100644
--- a/include/linux/cell_ids.h
+++ b/include/linux/cell_ids.h
@@ -53,9 +53,13 @@ static inline bool cell_range_in_window(u32 first, u32 count)
 	return first <= CELL_ID_WINDOW_LAST && last >= CELL_ID_WINDOW_FIRST;
 }
 
+bool userns_is_cell(const struct user_namespace *ns);
+
 #else
 
 static inline bool cell_id_in_window(u32 id) { return false; }
 static inline bool cell_range_in_window(u32 first, u32 count) { return false; }
+static inline bool userns_is_cell(const struct user_namespace *ns) { return false; }
+
 #endif
 #endif /* _LINUX_CELL_IDS_H */
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 91232053775d..5c8cf31ab9d4 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -40,6 +40,7 @@ struct uid_gid_map { /* 64 bytes -- 1 cache line */
 #define USERNS_INIT_FLAGS USERNS_SETGROUPS_ALLOWED
 
 struct ucounts;
+struct cell;
 
 enum ucount_type {
 	UCOUNT_USER_NAMESPACES,
@@ -117,6 +118,9 @@ struct user_namespace {
 #if IS_ENABLED(CONFIG_BINFMT_MISC)
 	struct binfmt_misc *binfmt_misc;
 #endif
+#ifdef CONFIG_CELLS
+	struct cell		*cell;		/* whose namespace this is, counted */
+#endif
 } __randomize_layout;
 
 struct ucounts {
@@ -184,6 +188,9 @@ static inline struct user_namespace *get_user_ns(struct user_namespace *ns)
 }
 
 extern int create_user_ns(struct cred *new);
+#ifdef CONFIG_CELLS
+int create_cell_user_ns(struct cred *new, struct cell *cell);
+#endif
 extern int unshare_userns(unsigned long unshare_flags, struct cred **new_cred);
 extern void __put_user_ns(struct user_namespace *ns);
 
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index cd748e1cbace..9301d0d41cb9 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -2,6 +2,7 @@
 
 #include <kunit/visibility.h>
 #include <linux/export.h>
+#include <linux/cell.h>
 #include <linux/nsproxy.h>
 #include <linux/slab.h>
 #include <linux/sched/signal.h>
@@ -64,6 +65,21 @@ static void set_cred_user_ns(struct cred *cred, struct user_namespace *user_ns)
 	cred->user_ns = user_ns;
 }
 
+#ifdef CONFIG_CELLS
+/**
+ * userns_is_cell - is this the user namespace of a cell?
+ * @ns: a user namespace
+ *
+ * Out of line for the headers that cannot see into struct user_namespace.
+ *
+ * Return: true if it is, false for a legacy user namespace.
+ */
+bool userns_is_cell(const struct user_namespace *ns)
+{
+	return ns->cell;
+}
+#endif
+
 static unsigned long enforced_nproc_rlimit(void)
 {
 	unsigned long limit = RLIM_INFINITY;
@@ -83,8 +99,10 @@ static unsigned long enforced_nproc_rlimit(void)
  *
  * This is called by copy_creds(), which will finish setting the target task's
  * credentials.
+ *
+ * @cell is the cell the namespace is made for, or NULL for a legacy one.
  */
-int create_user_ns(struct cred *new)
+static int __create_user_ns(struct cred *new, struct cell *cell)
 {
 	struct user_namespace *ns, *parent_ns = new->user_ns;
 	kuid_t owner = new->euid;
@@ -162,6 +180,11 @@ int create_user_ns(struct cred *new)
 	if (!setup_userns_sysctls(ns))
 		goto fail_keyring;
 
+#ifdef CONFIG_CELLS
+	/* Before anybody can find it. */
+	if (cell)
+		ns->cell = cell_get(cell);
+#endif
 	set_cred_user_ns(new, ns);
 	ns_tree_add(ns);
 	return 0;
@@ -175,6 +198,34 @@ int create_user_ns(struct cred *new)
 	return ret;
 }
 
+/**
+ * create_user_ns - create a new, legacy, user namespace
+ * @new: the credentials to derive the creator from, and to put into it
+ *
+ * Return: 0 or a negative error.
+ */
+int create_user_ns(struct cred *new)
+{
+	return __create_user_ns(new, NULL);
+}
+
+#ifdef CONFIG_CELLS
+/**
+ * create_cell_user_ns - create the user namespace of a cell
+ * @new:  a scratch credential, as for create_user_ns()
+ * @cell: the cell
+ *
+ * The namespace holds a reference on @cell for as long as it lives, and knows
+ * its cell before anybody can find it.
+ *
+ * Return: 0 or a negative error.
+ */
+int create_cell_user_ns(struct cred *new, struct cell *cell)
+{
+	return __create_user_ns(new, cell);
+}
+#endif
+
 int unshare_userns(unsigned long unshare_flags, struct cred **new_cred)
 {
 	struct cred *cred;
@@ -221,6 +272,9 @@ static void free_user_ns(struct work_struct *work)
 #endif
 		retire_userns_sysctls(ns);
 		key_free_user_ns(ns);
+#ifdef CONFIG_CELLS
+		cell_put(ns->cell);
+#endif
 		ns_common_free(ns);
 		/* Concurrent nstree traversal depends on a grace period. */
 		kfree_rcu(ns, ns.ns_rcu);

-- 
2.55.0


  parent reply	other threads:[~2026-11-05 14:58 UTC|newest]

Thread overview: 93+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-05 14:56 [PATCH RFC v3 00/90] cells: a container as one kernel object Josef Bacik
2026-11-05 14:56 ` [PATCH RFC v3 01/90] Documentation: add a design document for cells Josef Bacik
2026-11-05 17:06   ` Serge E. Hallyn
2026-11-05 18:23     ` Josef Bacik
2026-11-05 14:56 ` [PATCH RFC v3 02/90] Documentation: add examples " Josef Bacik
2026-11-05 14:56 ` [PATCH RFC v3 03/90] cells: reserve a window of ids Josef Bacik
2026-11-05 14:56 ` Josef Bacik [this message]
2026-11-05 14:56 ` [PATCH RFC v3 05/90] user_namespace: refuse setns() into a cell Josef Bacik
2026-11-05 14:56 ` [PATCH RFC v3 06/90] capability: give the owner no say over " Josef Bacik
2026-11-05 14:56 ` [PATCH RFC v3 07/90] ucount: count the id blocks a user holds Josef Bacik
2026-11-05 14:56 ` [PATCH RFC v3 08/90] user_namespace: let the kernel map the ids of a cell Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 09/90] user_namespace: keep legacy maps out of the window Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 10/90] user: add uid_range_in_use() Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 11/90] keys: add key_user_range_in_use() Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 12/90] fs: add kuid_has_fsmapping() and kgid_has_fsmapping() Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 13/90] fs: never store an id that was lent to a cell Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 14/90] fs: take no id of a cell for a mount option Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 15/90] fs: let an idmapping pin the ids of a cell Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 16/90] cgroup: let a cgroup become the budget " Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 17/90] cgroup: add cgroup_cell_chown() Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 18/90] cgroup: give the budget of a cell back Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 19/90] cgroup: add cgroup_cell_kill() Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 20/90] cgroup: root a cgroup namespace at a given cgroup Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 21/90] fs: seal a mount namespace for a cell Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 22/90] nsfs: make open_namespace() available outside of fs/ Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 23/90] nsproxy: build the nsproxy the tasks of a cell share Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 24/90] cells: add the cell object and cell_create() Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 25/90] cells: add cell_kill() Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 26/90] cells: add cell_configure() Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 27/90] cells: add the "budget" slot Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 28/90] cells: add the "root" slot Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 29/90] cells: add the "ids" slot, for a single id Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 30/90] cells: lend a cell a block of ids Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 31/90] cells: reserve the "spawn" slot Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 32/90] cells: add CELL_CMD_SEAL Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 33/90] cells: add cell_open() Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 34/90] pid: allocate a struct pid ahead of its numbers Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 35/90] fork: let copy_process() place a task in a cell Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 36/90] cgroup: fork a task of a cell into the budget Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 37/90] x86: give a task without an mm no user state Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 38/90] uprobes: cope with a new task that has no mm Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 39/90] fork: add tasks that start out with nothing Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 40/90] pidfd: add PIDFD_EMPTY Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 41/90] pidfd: add pidfd_configure() Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 42/90] pidfd: let pidfd_configure() place files Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 43/90] pidfd: let pidfd_configure() set credentials Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 44/90] pidfd: let pidfd_configure() name a cgroup to start in Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 45/90] ptrace: keep everybody off an embryo Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 46/90] cells: end a cell when its init exits Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 47/90] cells: refuse a task of a cell a change of namespaces Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 48/90] fs: refuse a task of a cell a new mount namespace Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 49/90] nsproxy: refuse to enter a namespace of a cell Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 50/90] cgroup: keep a task of a cell within the budget Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 51/90] cgroup: let nothing but a spawn into a budget Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 52/90] kernfs: let a filesystem refuse a change to a node Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 53/90] cgroup: let a cell change nothing outside its budget Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 54/90] cgroup: leave the limits of a budget to who is above Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 55/90] fs: let a filesystem hold ids that go with the cell Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 56/90] cgroup: let a cell make cgroups below its budget Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 57/90] resctrl: let a cell change nothing Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 58/90] cred: let no id count across cells Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 59/90] ptrace: refuse PTRACE_TRACEME " Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 60/90] keys: ignore an equal uid or gid " Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 61/90] fs: accept a cellfd for MOUNT_ATTR_IDMAP Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 62/90] fs: let a cell set no set-id bits Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 63/90] fs: drop privileges from what a cell may write to Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 64/90] cells: add the "caps" slot Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 65/90] capabilities: take nothing from a file in a cell Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 66/90] capabilities: let a cell shrink its bounding sets Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 67/90] af_unix: deliver ancillary data only within a cell Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 68/90] af_unix: tell a sender that control data will not cross Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 69/90] net: do not identify a peer in another cell Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 70/90] seccomp: let a listener put no file into a cell Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 71/90] umh: run no program for a task of " Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 72/90] coredump: let no core dump leave " Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 73/90] cells: add the "net" slot Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 74/90] net: give a cell without a network no sockets Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 75/90] net: add net_may() Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 76/90] rtnetlink: use net_may() for requests that change state Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 77/90] net: use net_may() in the configuration ioctls Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 78/90] netfilter: use net_may() for configuration Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 79/90] tun: use net_may() to create and attach to devices Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 80/90] net: use net_may() for the net sysctls Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 81/90] net: use net_may() for privileged socket options Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 82/90] net: use net_may() for raw and packet sockets Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 83/90] cells: add the "net-rights" slot Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 84/90] net: honour the rights of a cell in net_may() Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 85/90] tun: ignore an equal uid or gid across cells Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 86/90] cells: add cell_info() Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 87/90] cells: add CONFIG_CELLS Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 88/90] arch: wire up the cell system calls Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 89/90] MAINTAINERS: add an entry for cells Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 90/90] selftests: add tests " Josef Bacik

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=20261105-b4-cells-v3-4-bc9095937a84@toxicpanda.com \
    --to=josef@toxicpanda.com \
    --cc=James.Bottomley@HansenPartnership.com \
    --cc=andrew@amutable.com \
    --cc=brauner@kernel.org \
    --cc=jbingham@gmail.com \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=sergeh@kernel.org \
    --cc=taherafahimi@linux.microsoft.com \
    /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

all inboxes | Powered by JetHome®