* [ndctl PATCH 1/2] ndctl/check-namespace: Updates for BTT log compatibility
@ 2018-01-13 0:03 Vishal Verma
2018-01-13 0:03 ` [ndctl PATCH 2/2] ndctl: add an option to check-namespace to rewrite the log Vishal Verma
0 siblings, 1 reply; 2+ messages in thread
From: Vishal Verma @ 2018-01-13 0:03 UTC (permalink / raw)
To: linux-nvdimm
Update ndctl check-namespace with the BTT log compatibility fixes. This
detects the existing log/padding scheme, and uses that to perform its
checks.
Reported-by: Juston Li <juston.li@intel.com>
Cc: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
---
ndctl/check.c | 205 +++++++++++++++++++++++++++++++++++++++++++++---------
ndctl/namespace.h | 46 +++++++++++-
2 files changed, 216 insertions(+), 35 deletions(-)
diff --git a/ndctl/check.c b/ndctl/check.c
index 3d58f89..d3aa1aa 100644
--- a/ndctl/check.c
+++ b/ndctl/check.c
@@ -82,6 +82,7 @@ struct arena_info {
u32 flags;
int num;
struct btt_chk *bttc;
+ int log_index[2];
};
static sigjmp_buf sj_env;
@@ -239,10 +240,15 @@ static int btt_map_write(struct arena_info *a, u32 lba, u32 mapping)
return 0;
}
-static void btt_log_read_pair(struct arena_info *a, u32 lane,
- struct log_entry *ent)
+static void btt_log_group_read(struct arena_info *a, u32 lane,
+ struct log_group *log)
{
- memcpy(ent, &a->map.log[lane * 2], 2 * sizeof(struct log_entry));
+ memcpy(log, &a->map.log[lane], LOG_GRP_SIZE);
+}
+
+static u32 log_seq(struct log_group *log, int log_idx)
+{
+ return le32_to_cpu(log->ent[log_idx].seq);
}
/*
@@ -250,22 +256,24 @@ static void btt_log_read_pair(struct arena_info *a, u32 lane,
* find the 'older' entry. The return value indicates which of the two was
* the 'old' entry
*/
-static int btt_log_get_old(struct log_entry *ent)
+static int btt_log_get_old(struct arena_info *a, struct log_group *log)
{
+ int idx0 = a->log_index[0];
+ int idx1 = a->log_index[1];
int old;
- if (ent[0].seq == 0) {
- ent[0].seq = cpu_to_le32(1);
+ if (log_seq(log, idx0) == 0) {
+ log->ent[idx0].seq = cpu_to_le32(1);
return 0;
}
- if (le32_to_cpu(ent[0].seq) < le32_to_cpu(ent[1].seq)) {
- if (le32_to_cpu(ent[1].seq) - le32_to_cpu(ent[0].seq) == 1)
+ if (log_seq(log, idx0) < log_seq(log, idx1)) {
+ if ((log_seq(log, idx1) - log_seq(log, idx0)) == 1)
old = 0;
else
old = 1;
} else {
- if (le32_to_cpu(ent[0].seq) - le32_to_cpu(ent[1].seq) == 1)
+ if ((log_seq(log, idx0) - log_seq(log, idx1)) == 1)
old = 1;
else
old = 0;
@@ -277,13 +285,13 @@ static int btt_log_get_old(struct log_entry *ent)
static int btt_log_read(struct arena_info *a, u32 lane, struct log_entry *ent)
{
int new_ent;
- struct log_entry log[2];
+ struct log_group log;
if (ent == NULL)
return -EINVAL;
- btt_log_read_pair(a, lane, log);
- new_ent = 1 - btt_log_get_old(log);
- memcpy(ent, &log[new_ent], sizeof(struct log_entry));
+ btt_log_group_read(a, lane, &log);
+ new_ent = 1 - btt_log_get_old(a, &log);
+ memcpy(ent, &log.ent[a->log_index[new_ent]], LOG_ENT_SIZE);
return 0;
}
@@ -406,6 +414,8 @@ static void btt_xlat_status(struct arena_info *a, int errcode)
/* Check that log entries are self consistent */
static int btt_check_log_entries(struct arena_info *a)
{
+ int idx0 = a->log_index[0];
+ int idx1 = a->log_index[1];
unsigned int i;
int rc = 0;
@@ -413,28 +423,30 @@ static int btt_check_log_entries(struct arena_info *a)
* First, check both 'slots' for sequence numbers being distinct
* and in bounds
*/
- for (i = 0; i < (2 * a->nfree); i+=2) {
- if (a->map.log[i].seq == a->map.log[i + 1].seq)
+ for (i = 0; i < a->nfree; i++) {
+ struct log_group *log = &a->map.log[i];
+
+ if (log_seq(log, idx0) == log_seq(log, idx1))
return BTT_LOG_EQL_SEQ;
- if (a->map.log[i].seq > 3 || a->map.log[i + 1].seq > 3)
+ if (log_seq(log, idx0) > 3 || log_seq(log, idx1) > 3)
return BTT_LOG_OOB_SEQ;
}
/*
* Next, check only the 'new' slot in each lane for the remaining
- * entries being in bounds
+ * fields being in bounds
*/
for (i = 0; i < a->nfree; i++) {
- struct log_entry log;
+ struct log_entry ent;
- rc = btt_log_read(a, i, &log);
+ rc = btt_log_read(a, i, &ent);
if (rc)
return rc;
- if (log.lba >= a->external_nlba)
+ if (ent.lba >= a->external_nlba)
return BTT_LOG_OOB_LBA;
- if (log.old_map >= a->internal_nlba)
+ if (ent.old_map >= a->internal_nlba)
return BTT_LOG_OOB_OLD;
- if (log.new_map >= a->internal_nlba)
+ if (ent.new_map >= a->internal_nlba)
return BTT_LOG_OOB_NEW;
}
return rc;
@@ -462,23 +474,23 @@ static int btt_check_log_map(struct arena_info *a)
int rc = 0, rc_saved = 0;
for (i = 0; i < a->nfree; i++) {
- struct log_entry log;
+ struct log_entry ent;
- rc = btt_log_read(a, i, &log);
+ rc = btt_log_read(a, i, &ent);
if (rc)
return rc;
- mapping = btt_map_lookup(a, log.lba);
+ mapping = btt_map_lookup(a, ent.lba);
/*
* Case where the flog was written, but map couldn't be
* updated. The kernel should also be able to detect and
* fix this condition.
*/
- if (log.new_map != mapping && log.old_map == mapping) {
+ if (ent.new_map != mapping && ent.old_map == mapping) {
info(a->bttc,
"arena %d: log[%d].new_map (%#x) doesn't match map[%#x] (%#x)\n",
- a->num, i, log.new_map, log.lba, mapping);
- rc = btt_map_write(a, log.lba, log.new_map);
+ a->num, i, ent.new_map, ent.lba, mapping);
+ rc = btt_map_write(a, ent.lba, ent.new_map);
if (rc)
rc_saved = rc;
}
@@ -528,19 +540,19 @@ static int btt_check_bitmap(struct arena_info *a)
/* map 'nfree' number of flog entries */
for (i = 0; i < a->nfree; i++) {
- struct log_entry log;
+ struct log_entry ent;
- rc = btt_log_read(a, i, &log);
+ rc = btt_log_read(a, i, &ent);
if (rc)
goto out;
- if (test_bit(log.old_map, bm)) {
+ if (test_bit(ent.old_map, bm)) {
info(a->bttc,
"arena %d: internal block %#x is referenced by two map/log entries\n",
- a->num, log.old_map);
+ a->num, ent.old_map);
rc = BTT_BITMAP_ERROR;
goto out;
}
- bitmap_set(bm, log.old_map, 1);
+ bitmap_set(bm, ent.old_map, 1);
}
/* check that the bitmap is full */
@@ -632,6 +644,123 @@ static int btt_parse_meta(struct arena_info *arena, struct btt_sb *btt_sb,
return 0;
}
+static bool ent_is_padding(struct log_entry *ent)
+{
+ return (ent->lba == 0) && (ent->old_map == 0) && (ent->new_map == 0)
+ && (ent->seq == 0);
+}
+
+/*
+ * Detecting valid log indices: We read a log group, and iterate over its
+ * four slots. We expect that a padding slot will be all-zeroes, and use this
+ * to detect a padding slot vs. an actual entry.
+ *
+ * If a log_group is in the initial state, i.e. hasn't been used since the
+ * creation of this BTT layout, it will have three of the four slots with
+ * zeroes. We skip over these log_groups for the detection of log_index. If
+ * all log_groups are in the initial state (i.e. the BTT has never been
+ * written to), it is safe to assume the 'new format' of log entries in slots
+ * (0, 1).
+ */
+static int log_set_indices(struct arena_info *arena)
+{
+ bool idx_set = false, initial_state = true;
+ int log_index[2] = {-1, -1};
+ struct log_group log;
+ int j, next_idx = 0;
+ u32 pad_count = 0;
+ u32 i;
+
+ for (i = 0; i < arena->nfree; i++) {
+ btt_log_group_read(arena, i, &log);
+
+ for (j = 0; j < 4; j++) {
+ if (!idx_set) {
+ if (ent_is_padding(&log.ent[j])) {
+ pad_count++;
+ continue;
+ } else {
+ /* Skip if index has been recorded */
+ if ((next_idx == 1) &&
+ (j == log_index[0]))
+ continue;
+ /* valid entry, record index */
+ log_index[next_idx] = j;
+ next_idx++;
+ }
+ if (next_idx == 2) {
+ /* two valid entries found */
+ idx_set = true;
+ } else if (next_idx > 2) {
+ /* too many valid indices */
+ return -ENXIO;
+ }
+ } else {
+ /*
+ * once the indices have been set, just verify
+ * that all subsequent log groups are either in
+ * their initial state or follow the same
+ * indices.
+ */
+ if (j == log_index[0]) {
+ /* entry must be 'valid' */
+ if (ent_is_padding(&log.ent[j]))
+ return -ENXIO;
+ } else if (j == log_index[1]) {
+ ;
+ /*
+ * log_index[1] can be padding if the
+ * lane never got used and it is still
+ * in the initial state (three 'padding'
+ * entries)
+ */
+ } else {
+ /* entry must be invalid (padding) */
+ if (!ent_is_padding(&log.ent[j]))
+ return -ENXIO;
+ }
+ }
+ }
+ /*
+ * If any of the log_groups have more than one valid,
+ * non-padding entry, then the we are no longer in the
+ * initial_state
+ */
+ if (pad_count < 3)
+ initial_state = false;
+ pad_count = 0;
+ }
+
+ if (!initial_state && !idx_set)
+ return -ENXIO;
+
+ /*
+ * If all the entries in the log were in the initial state,
+ * assume new padding scheme
+ */
+ if (initial_state)
+ log_index[1] = 1;
+
+ /*
+ * Only allow the known permutations of log/padding indices,
+ * i.e. (0, 1), and (0, 2)
+ */
+ if ((log_index[0] == 0) && ((log_index[1] == 1) || (log_index[1] == 2)))
+ ; /* known index possibilities */
+ else {
+ err(arena->bttc, "Found an unknown padding scheme\n");
+ return -ENXIO;
+ }
+
+ arena->log_index[0] = log_index[0];
+ arena->log_index[1] = log_index[1];
+ info(arena->bttc, "arena[%d]: log_index_0 = %d\n",
+ arena->num, log_index[0]);
+ info(arena->bttc, "arena[%d]: log_index_1 = %d\n",
+ arena->num, log_index[1]);
+ return 0;
+}
+
static int btt_discover_arenas(struct btt_chk *bttc)
{
int ret = 0;
@@ -978,6 +1107,7 @@ int namespace_check(struct ndctl_namespace *ndns, bool verbose, bool force,
struct btt_chk *bttc;
struct sigaction act;
char path[50];
+ int i;
bttc = calloc(1, sizeof(*bttc));
if (bttc == NULL)
@@ -1108,6 +1238,15 @@ int namespace_check(struct ndctl_namespace *ndns, bool verbose, bool force,
if (rc)
goto out_close;
+ for (i = 0; i < bttc->num_arenas; i++) {
+ rc = log_set_indices(&bttc->arena[i]);
+ if (rc) {
+ err(bttc,
+ "Unable to deduce log/padding indices\n");
+ goto out_close;
+ }
+ }
+
rc = btt_check_arenas(bttc);
btt_remove_mappings(bttc);
diff --git a/ndctl/namespace.h b/ndctl/namespace.h
index 6d56468..bc21085 100644
--- a/ndctl/namespace.h
+++ b/ndctl/namespace.h
@@ -107,6 +107,8 @@ struct namespace_label {
#define ARENA_MAX_SIZE (1ULL << 39) /* 512 GB */
#define BTT_INFO_SIZE 4096
#define IB_FLAG_ERROR_MASK 0x00000001
+#define LOG_GRP_SIZE sizeof(struct log_group)
+#define LOG_ENT_SIZE sizeof(struct log_entry)
#define BTT_NUM_OFFSETS 2
#define BTT1_START_OFFSET 4096
@@ -117,7 +119,47 @@ struct log_entry {
le32 old_map;
le32 new_map;
le32 seq;
- le64 padding[2];
+};
+
+/*
+ * A log group represents one log 'lane', and consists of four log entries.
+ * Two of the four entries are valid entries, and the remaining two are
+ * padding. Due to an old bug in the padding location, we need to perform a
+ * test to determine the padding scheme being used, and use that scheme
+ * thereafter.
+ *
+ * In kernels prior to 4.15, 'log group' would have actual log entries at
+ * indices (0, 2) and padding at indices (1, 3), where as the correct/updated
+ * format has log entries at indices (0, 1) and padding at indices (2, 3).
+ *
+ * Old (pre 4.15) format:
+ * +-----------------+-----------------+
+ * | ent[0] | ent[1] |
+ * | 16B | 16B |
+ * | lba/old/new/seq | pad |
+ * +-----------------------------------+
+ * | ent[2] | ent[3] |
+ * | 16B | 16B |
+ * | lba/old/new/seq | pad |
+ * +-----------------+-----------------+
+ *
+ * New format:
+ * +-----------------+-----------------+
+ * | ent[0] | ent[1] |
+ * | 16B | 16B |
+ * | lba/old/new/seq | lba/old/new/seq |
+ * +-----------------------------------+
+ * | ent[2] | ent[3] |
+ * | 16B | 16B |
+ * | pad | pad |
+ * +-----------------+-----------------+
+ *
+ * We detect during start-up which format is in use, and set
+ * arena->log_index[(0, 1)] with the detected format.
+ */
+
+struct log_group {
+ struct log_entry ent[4];
};
struct btt_sb {
@@ -155,7 +197,7 @@ struct arena_map {
size_t data_len;
u32 *map;
size_t map_len;
- struct log_entry *log;
+ struct log_group *log;
size_t log_len;
struct btt_sb *info2;
size_t info2_len;
--
2.14.3
_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm
^ permalink raw reply related [flat|nested] 2+ messages in thread
* [ndctl PATCH 2/2] ndctl: add an option to check-namespace to rewrite the log
2018-01-13 0:03 [ndctl PATCH 1/2] ndctl/check-namespace: Updates for BTT log compatibility Vishal Verma
@ 2018-01-13 0:03 ` Vishal Verma
0 siblings, 0 replies; 2+ messages in thread
From: Vishal Verma @ 2018-01-13 0:03 UTC (permalink / raw)
To: linux-nvdimm
Add a --rewrite-log option to ndctl check-namespace which reads the
active log entries, and rewrites them as though initializing a new BTT.
This allows us to convert an old (pre 4.15) format of log/padding layout
to a new one that is compatible with other BTT implementations.
In the btt-pad-compat unit test, add testing for the format conversion
operation.
Cc: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
---
Documentation/ndctl/ndctl-check-namespace.txt | 11 +++++
ndctl/check.c | 70 ++++++++++++++++++++++++++-
ndctl/namespace.c | 6 ++-
test/btt-pad-compat.sh | 9 ++++
4 files changed, 93 insertions(+), 3 deletions(-)
diff --git a/Documentation/ndctl/ndctl-check-namespace.txt b/Documentation/ndctl/ndctl-check-namespace.txt
index 49353b1..ea4183a 100644
--- a/Documentation/ndctl/ndctl-check-namespace.txt
+++ b/Documentation/ndctl/ndctl-check-namespace.txt
@@ -42,6 +42,17 @@ OPTIONS
Perform metadata repairs if possible. Without this option,
the raw namespace contents will not be touched.
+-L::
+--rewrite-log::
+ Regenerate the BTT log and write it to media. This can be used to
+ convert from the old (pre 4.15) padding format that was incompatible
+ with other BTT implementations to the updated format. This requires
+ the --repair option to be provided.
+
+ WARNING: Do not interrupt this operation as it can potentially cause
+ unrecoverable metadata corruption. It is highly recommended to create
+ a backup of the raw namespace before attempting this.
+
-f::
--force::
Unless this option is specified, a check-namespace operation
diff --git a/ndctl/check.c b/ndctl/check.c
index d3aa1aa..09dd125 100644
--- a/ndctl/check.c
+++ b/ndctl/check.c
@@ -46,6 +46,7 @@ struct check_opts {
bool verbose;
bool force;
bool repair;
+ bool logfix;
};
struct btt_chk {
@@ -246,6 +247,12 @@ static void btt_log_group_read(struct arena_info *a, u32 lane,
memcpy(log, &a->map.log[lane], LOG_GRP_SIZE);
}
+static void btt_log_group_write(struct arena_info *a, u32 lane,
+ struct log_group *log)
+{
+ memcpy(&a->map.log[lane], log, LOG_GRP_SIZE);
+}
+
static u32 log_seq(struct log_group *log, int log_idx)
{
return le32_to_cpu(log->ent[log_idx].seq);
@@ -358,6 +365,7 @@ enum btt_errcodes {
BTT_LOG_MAP_ERR,
BTT_MAP_OOB,
BTT_BITMAP_ERROR,
+ BTT_LOGFIX_ERR,
};
static void btt_xlat_status(struct arena_info *a, int errcode)
@@ -405,6 +413,11 @@ static void btt_xlat_status(struct arena_info *a, int errcode)
"arena %d: bitmap error: internal blocks are incorrectly referenced\n",
a->num);
break;
+ case BTT_LOGFIX_ERR:
+ err(a->bttc,
+ "arena %d: rewrite-log error: log may be in an unknown/unrecoverable state\n",
+ a->num);
+ break;
default:
err(a->bttc, "arena %d: unknown error: %d\n",
a->num, errcode);
@@ -563,6 +576,44 @@ static int btt_check_bitmap(struct arena_info *a)
return rc;
}
+static int btt_rewrite_log(struct arena_info *a)
+{
+ struct log_group log;
+ int rc;
+ u32 i;
+
+ info(a->bttc, "arena %d: rewriting log\n", a->num);
+ /*
+ * To rewrite the log, we implicitly use the 'new' padding scheme of
+ * (0, 1) but resetting the log to a completely initial state (i.e.
+ * slot-0 contains a made-up entry containing the 'free' block from
+ * the existing current log entry, and a sequence number of '1'. All
+ * other slots are zeroed.
+ *
+ * This way of rewriting the log is the most flexible as it can be
+ * (ab)used to convert a new padding format back to the old one.
+ * Since it only recreates slot-0, which is common between both
+ * existing formats, an older kernel will simply initialize the free
+ * list using those slot-0 entries, and run with it as though slot-2
+ * is the other valid slot.
+ */
+ memset(&log, 0, LOG_GRP_SIZE);
+ for (i = 0; i < a->nfree; i++) {
+ struct log_entry ent;
+
+ rc = btt_log_read(a, i, &ent);
+ if (rc)
+ return BTT_LOGFIX_ERR;
+
+ log.ent[0].lba = ent.lba;
+ log.ent[0].old_map = ent.old_map;
+ log.ent[0].new_map = ent.new_map;
+ log.ent[0].seq = 1;
+ btt_log_group_write(a, i, &log);
+ }
+ return 0;
+}
+
static int btt_check_arenas(struct btt_chk *bttc)
{
struct arena_info *a = NULL;
@@ -591,6 +642,12 @@ static int btt_check_arenas(struct btt_chk *bttc)
rc = btt_check_bitmap(a);
if (rc)
break;
+
+ if (bttc->opts->logfix) {
+ rc = btt_rewrite_log(a);
+ if (rc)
+ break;
+ }
}
if (a && rc != BTT_OK) {
@@ -1094,13 +1151,14 @@ static int btt_recover_first_sb(struct btt_chk *bttc)
}
int namespace_check(struct ndctl_namespace *ndns, bool verbose, bool force,
- bool repair)
+ bool repair, bool logfix)
{
const char *devname = ndctl_namespace_get_devname(ndns);
struct check_opts __opts = {
.verbose = verbose,
.force = force,
.repair = repair,
+ .logfix = logfix,
}, *opts = &__opts;
int raw_mode, rc, disabled_flag = 0, open_flags;
struct btt_sb *btt_sb;
@@ -1127,6 +1185,16 @@ int namespace_check(struct ndctl_namespace *ndns, bool verbose, bool force,
goto out_bttc;
}
+ if (opts->logfix) {
+ if (!opts->repair) {
+ err(bttc, "--rewrite-log also requires --repair\n");
+ rc = -EINVAL;
+ goto out_bttc;
+ }
+ info(bttc,
+ "WARNING: interruption may cause unrecoverable metadata corruption\n");
+ }
+
bttc->opts = opts;
bttc->sys_page_size = sysconf(_SC_PAGESIZE);
bttc->rawsize = ndctl_namespace_get_size(ndns);
diff --git a/ndctl/namespace.c b/ndctl/namespace.c
index d31244b..6b19f28 100644
--- a/ndctl/namespace.c
+++ b/ndctl/namespace.c
@@ -39,6 +39,7 @@
static bool verbose;
static bool force;
static bool repair;
+static bool logfix;
static struct parameters {
bool do_scan;
bool mode_default;
@@ -121,6 +122,7 @@ OPT_BOOLEAN('L', "autolabel", ¶m.autolabel, "automatically initialize labels
#define CHECK_OPTIONS() \
OPT_BOOLEAN('R', "repair", &repair, "perform metadata repairs"), \
+OPT_BOOLEAN('L', "rewrite-log", &logfix, "regenerate the log"), \
OPT_BOOLEAN('f', "force", &force, "check namespace even if currently active")
static const struct option base_options[] = {
@@ -985,7 +987,7 @@ static int namespace_reconfig(struct ndctl_region *region,
}
int namespace_check(struct ndctl_namespace *ndns, bool verbose, bool force,
- bool repair);
+ bool repair, bool logfix);
static int do_xaction_namespace(const char *namespace,
enum device_action action, struct ndctl_ctx *ctx)
@@ -1049,7 +1051,7 @@ static int do_xaction_namespace(const char *namespace,
break;
case ACTION_CHECK:
rc = namespace_check(ndns, verbose,
- force, repair);
+ force, repair, logfix);
if (rc < 0)
return rc;
break;
diff --git a/test/btt-pad-compat.sh b/test/btt-pad-compat.sh
index cbfe990..d10efe3 100755
--- a/test/btt-pad-compat.sh
+++ b/test/btt-pad-compat.sh
@@ -193,6 +193,15 @@ do_tests()
cycle_ns "$dev"
verify_idx 0 2
+ # rewrite log using ndctl, verify conversion to new format
+ ndctl check-namespace --rewrite-log --repair --force --verbose $dev
+ do_random_io "/dev/$blockdev"
+ cycle_ns "$dev"
+ verify_idx 0 1
+
+ # check-namespace again to make sure everything is ok
+ ndctl check-namespace --force --verbose $dev
+
# the old format btt metadata was created with a null parent uuid,
# making it 'stickier' than a normally created btt. Be sure to clean
# it up by wiping the info block
--
2.14.3
_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2018-01-12 23:58 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-01-13 0:03 [ndctl PATCH 1/2] ndctl/check-namespace: Updates for BTT log compatibility Vishal Verma
2018-01-13 0:03 ` [ndctl PATCH 2/2] ndctl: add an option to check-namespace to rewrite the log Vishal Verma
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).