* [PATCH 1/2] dump.f2fs: dump owner of data given block address
@ 2014-12-21 16:37 Jaegeuk Kim
2014-12-21 16:37 ` [PATCH 2/2] parse.f2fs: add a tool to parse IO traces made by runtime f2fs Jaegeuk Kim
0 siblings, 1 reply; 2+ messages in thread
From: Jaegeuk Kim @ 2014-12-21 16:37 UTC (permalink / raw)
To: linux-f2fs-devel; +Cc: Jaegeuk Kim
This patch introduces a feature to dump owner information of given block
address.
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
fsck/dump.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++++-------
fsck/f2fs.h | 12 +++++
fsck/fsck.h | 4 +-
fsck/main.c | 2 +-
fsck/mount.c | 14 +++++-
5 files changed, 169 insertions(+), 24 deletions(-)
diff --git a/fsck/dump.c b/fsck/dump.c
index 4bb906f..3c4a8d1 100644
--- a/fsck/dump.c
+++ b/fsck/dump.c
@@ -11,6 +11,7 @@
#include <inttypes.h>
#include "fsck.h"
+#include <locale.h>
#define BUF_SZ 80
@@ -298,13 +299,114 @@ void dump_node(struct f2fs_sb_info *sbi, nid_t nid)
free(node_blk);
}
-int dump_inode_from_blkaddr(struct f2fs_sb_info *sbi, u32 blk_addr)
+static void dump_node_from_blkaddr(u32 blk_addr)
{
- nid_t ino, nid;
- int type, ret;
- struct f2fs_summary sum_entry;
- struct node_info ni;
struct f2fs_node *node_blk;
+ int ret;
+
+ node_blk = calloc(BLOCK_SZ, 1);
+ ASSERT(node_blk);
+
+ ret = dev_read_block(node_blk, blk_addr);
+ ASSERT(ret >= 0);
+
+ if (config.dbg_lv > 0)
+ print_node_info(node_blk);
+ else
+ print_inode_info(&node_blk->i, 1);
+
+ free(node_blk);
+}
+
+static void dump_data_offset(u32 blk_addr, int ofs_in_node)
+{
+ struct f2fs_node *node_blk;
+ unsigned int indirect_blks = 2 * NIDS_PER_BLOCK + 4;
+ unsigned int bidx = 0;
+ unsigned int node_ofs;
+ int ret;
+
+ node_blk = calloc(BLOCK_SZ, 1);
+ ASSERT(node_blk);
+
+ ret = dev_read_block(node_blk, blk_addr);
+ ASSERT(ret >= 0);
+
+ node_ofs = ofs_of_node(node_blk);
+
+ if (node_ofs == 0)
+ goto got_it;
+
+ if (node_ofs > 0 && node_ofs <= 2) {
+ bidx = node_ofs - 1;
+ } else if (node_ofs <= indirect_blks) {
+ int dec = (node_ofs - 4) / (NIDS_PER_BLOCK + 1);
+ bidx = node_ofs - 2 - dec;
+ } else {
+ int dec = (node_ofs - indirect_blks - 3) / (NIDS_PER_BLOCK + 1);
+ bidx = node_ofs - 5 - dec;
+ }
+ bidx = bidx * ADDRS_PER_BLOCK + ADDRS_PER_INODE(&node_blk->i);
+got_it:
+ bidx += ofs_in_node;
+
+ setlocale(LC_ALL, "");
+ MSG(0, " - Data offset : 0x%x (4KB), %'u (bytes)\n",
+ bidx, bidx * 4096);
+ free(node_blk);
+}
+
+static void dump_node_offset(u32 blk_addr)
+{
+ struct f2fs_node *node_blk;
+ int ret;
+
+ node_blk = calloc(BLOCK_SZ, 1);
+ ASSERT(node_blk);
+
+ ret = dev_read_block(node_blk, blk_addr);
+ ASSERT(ret >= 0);
+
+ MSG(0, " - Node offset : 0x%x\n", ofs_of_node(node_blk));
+ free(node_blk);
+}
+
+int dump_info_from_blkaddr(struct f2fs_sb_info *sbi, u32 blk_addr)
+{
+ nid_t nid;
+ int type;
+ struct f2fs_summary sum_entry;
+ struct node_info ni, ino_ni;
+ int ret = 0;
+
+ MSG(0, "\n== Dump data from block address ==\n\n");
+
+ if (blk_addr < SM_I(sbi)->seg0_blkaddr) {
+ MSG(0, "\nFS Reserved Area for SEG #0: ");
+ ret = -EINVAL;
+ } else if (blk_addr < SIT_I(sbi)->sit_base_addr) {
+ MSG(0, "\nFS Metadata Area: ");
+ ret = -EINVAL;
+ } else if (blk_addr < NM_I(sbi)->nat_blkaddr) {
+ MSG(0, "\nFS SIT Area: ");
+ ret = -EINVAL;
+ } else if (blk_addr < SM_I(sbi)->ssa_blkaddr) {
+ MSG(0, "\nFS NAT Area: ");
+ ret = -EINVAL;
+ } else if (blk_addr < SM_I(sbi)->main_blkaddr) {
+ MSG(0, "\nFS SSA Area: ");
+ ret = -EINVAL;
+ } else if (blk_addr > __end_block_addr(sbi)) {
+ MSG(0, "\nOut of address space: ");
+ ret = -EINVAL;
+ }
+
+ if (ret) {
+ MSG(0, "User data is from 0x%x to 0x%x\n\n",
+ SM_I(sbi)->main_blkaddr,
+ __end_block_addr(sbi));
+ return ret;
+ }
type = get_sum_entry(sbi, blk_addr, &sum_entry);
nid = le32_to_cpu(sum_entry.nid);
@@ -318,26 +420,47 @@ int dump_inode_from_blkaddr(struct f2fs_sb_info *sbi, u32 blk_addr)
DBG(1, "SUM.nid [0x%x]\n", nid);
DBG(1, "SUM.type [%s]\n", seg_type_name[type]);
DBG(1, "SUM.version [%d]\n", sum_entry.version);
- DBG(1, "SUM.ofs_in_node [%d]\n", sum_entry.ofs_in_node);
+ DBG(1, "SUM.ofs_in_node [0x%x]\n", sum_entry.ofs_in_node);
DBG(1, "NAT.blkaddr [0x%x]\n", ni.blk_addr);
DBG(1, "NAT.ino [0x%x]\n", ni.ino);
- node_blk = calloc(BLOCK_SZ, 1);
-
-read_node_blk:
- ret = dev_read_block(node_blk, blk_addr);
- ASSERT(ret >= 0);
+ get_node_info(sbi, ni.ino, &ino_ni);
- ino = le32_to_cpu(node_blk->footer.ino);
- nid = le32_to_cpu(node_blk->footer.nid);
+ /* inode block address */
+ if (ni.blk_addr == NULL_ADDR || ino_ni.blk_addr == NULL_ADDR) {
+ MSG(0, "FS Userdata Area: Obsolete block from 0x%x\n",
+ blk_addr);
+ return -EINVAL;
+ }
- if (ino == nid) {
- print_node_info(node_blk);
+ /* print inode */
+ if (config.dbg_lv > 0)
+ dump_node_from_blkaddr(ino_ni.blk_addr);
+
+ if (type == SEG_TYPE_CUR_DATA || type == SEG_TYPE_DATA) {
+ MSG(0, "FS Userdata Area: Data block from 0x%x\n", blk_addr);
+ MSG(0, " - Direct node block : id = 0x%x from 0x%x\n",
+ nid, ni.blk_addr);
+ MSG(0, " - Inode block : id = 0x%x from 0x%x\n",
+ ni.ino, ino_ni.blk_addr);
+ dump_node_from_blkaddr(ino_ni.blk_addr);
+ dump_data_offset(ni.blk_addr,
+ le16_to_cpu(sum_entry.ofs_in_node));
} else {
- get_node_info(sbi, ino, &ni);
- goto read_node_blk;
+ MSG(0, "FS Userdata Area: Node block from 0x%x\n", blk_addr);
+ if (ni.ino == ni.nid) {
+ MSG(0, " - Inode block : id = 0x%x from 0x%x\n",
+ ni.ino, ino_ni.blk_addr);
+ dump_node_from_blkaddr(ino_ni.blk_addr);
+ } else {
+ MSG(0, " - Node block : id = 0x%x from 0x%x\n",
+ nid, ni.blk_addr);
+ MSG(0, " - Inode block : id = 0x%x from 0x%x\n",
+ ni.ino, ino_ni.blk_addr);
+ dump_node_from_blkaddr(ino_ni.blk_addr);
+ dump_node_offset(ni.blk_addr);
+ }
}
- free(node_blk);
- return ino;
+ return 0;
}
diff --git a/fsck/f2fs.h b/fsck/f2fs.h
index 57bad9b..c268f15 100644
--- a/fsck/f2fs.h
+++ b/fsck/f2fs.h
@@ -197,6 +197,12 @@ static inline void *inline_data_addr(struct f2fs_node *node_blk)
return (void *)&(node_blk->i.i_addr[1]);
}
+static inline unsigned int ofs_of_node(struct f2fs_node *node_blk)
+{
+ unsigned flag = le32_to_cpu(node_blk->footer.flag);
+ return flag >> OFFSET_BIT_SHIFT;
+}
+
static inline unsigned long __bitmap_size(struct f2fs_sb_info *sbi, int flag)
{
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
@@ -255,6 +261,12 @@ static inline block_t __start_sum_addr(struct f2fs_sb_info *sbi)
return le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_start_sum);
}
+static inline block_t __end_block_addr(struct f2fs_sb_info *sbi)
+{
+ block_t end = SM_I(sbi)->main_blkaddr;
+ return end + le64_to_cpu(F2FS_RAW_SUPER(sbi)->block_count);
+}
+
#define GET_ZONENO_FROM_SEGNO(sbi, segno) \
((segno / sbi->segs_per_sec) / sbi->secs_per_zone)
diff --git a/fsck/fsck.h b/fsck/fsck.h
index 49d6d1d..9cad013 100644
--- a/fsck/fsck.h
+++ b/fsck/fsck.h
@@ -97,7 +97,7 @@ int fsck_chk_inline_dentries(struct f2fs_sb_info *, struct f2fs_node *,
u32 *, u32 *);
extern void print_node_info(struct f2fs_node *);
-extern void print_inode_info(struct f2fs_inode *);
+extern void print_inode_info(struct f2fs_inode *, int);
extern struct seg_entry *get_seg_entry(struct f2fs_sb_info *, unsigned int);
extern int get_sum_block(struct f2fs_sb_info *, unsigned int,
struct f2fs_summary_block *);
@@ -126,6 +126,6 @@ struct dump_option {
extern void sit_dump(struct f2fs_sb_info *, int, int);
extern void ssa_dump(struct f2fs_sb_info *, int, int);
extern void dump_node(struct f2fs_sb_info *, nid_t);
-extern int dump_inode_from_blkaddr(struct f2fs_sb_info *, u32);
+extern int dump_info_from_blkaddr(struct f2fs_sb_info *, u32);
#endif /* _FSCK_H_ */
diff --git a/fsck/main.c b/fsck/main.c
index 2af3daf..e05e528 100644
--- a/fsck/main.c
+++ b/fsck/main.c
@@ -169,7 +169,7 @@ static void do_dump(struct f2fs_sb_info *sbi)
if (opt->start_ssa != -1)
ssa_dump(sbi, opt->start_ssa, opt->end_ssa);
if (opt->blk_addr != -1) {
- dump_inode_from_blkaddr(sbi, opt->blk_addr);
+ dump_info_from_blkaddr(sbi, opt->blk_addr);
goto cleanup;
}
dump_node(sbi, opt->nid);
diff --git a/fsck/mount.c b/fsck/mount.c
index 0aca60b..73eba6b 100644
--- a/fsck/mount.c
+++ b/fsck/mount.c
@@ -9,12 +9,22 @@
* published by the Free Software Foundation.
*/
#include "fsck.h"
+#include <locale.h>
-void print_inode_info(struct f2fs_inode *inode)
+void print_inode_info(struct f2fs_inode *inode, int name)
{
unsigned int i = 0;
int namelen = le32_to_cpu(inode->i_namelen);
+ if (name && namelen) {
+ inode->i_name[namelen] = '\0';
+ MSG(0, " - File name : %s\n", inode->i_name);
+ setlocale(LC_ALL, "");
+ MSG(0, " - File size : %'llu (bytes)\n",
+ le64_to_cpu(inode->i_size));
+ return;
+ }
+
DISP_u32(inode, i_mode);
DISP_u32(inode, i_uid);
DISP_u32(inode, i_gid);
@@ -76,7 +86,7 @@ void print_node_info(struct f2fs_node *node_block)
/* Is this inode? */
if (ino == nid) {
DBG(0, "Node ID [0x%x:%u] is inode\n", nid, nid);
- print_inode_info(&node_block->i);
+ print_inode_info(&node_block->i, 0);
} else {
int i;
u32 *dump_blk = (u32 *)node_block;
--
2.1.1
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk
^ permalink raw reply related [flat|nested] 2+ messages in thread
* [PATCH 2/2] parse.f2fs: add a tool to parse IO traces made by runtime f2fs
2014-12-21 16:37 [PATCH 1/2] dump.f2fs: dump owner of data given block address Jaegeuk Kim
@ 2014-12-21 16:37 ` Jaegeuk Kim
0 siblings, 0 replies; 2+ messages in thread
From: Jaegeuk Kim @ 2014-12-21 16:37 UTC (permalink / raw)
To: linux-f2fs-devel; +Cc: Jaegeuk Kim
From: Jaegeuk Kim <jaegeuk@motorola.com>
This patch adds parse.f2fs to retrieve process information and an amount
of data reads and writes from given IO trace got by f2fs.
Signed-off-by: Jaegeuk Kim <jaegeuk@motorola.com>
---
tools/Makefile.am | 3 +-
tools/f2fs_io_parse.c | 322 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 324 insertions(+), 1 deletion(-)
create mode 100644 tools/f2fs_io_parse.c
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 1ead174..69a0bb1 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -2,6 +2,7 @@
AM_CPPFLAGS = ${libuuid_CFLAGS} -I$(top_srcdir)/include
AM_CFLAGS = -Wall
-sbin_PROGRAMS = f2fstat fibmap.f2fs
+sbin_PROGRAMS = f2fstat fibmap.f2fs parse.f2fs
f2fstat_SOURCES = f2fstat.c
fibmap_f2fs_SOURCES = fibmap.c
+parse_f2fs_SOURCES = f2fs_io_parse.c
diff --git a/tools/f2fs_io_parse.c b/tools/f2fs_io_parse.c
new file mode 100644
index 0000000..7f97270
--- /dev/null
+++ b/tools/f2fs_io_parse.c
@@ -0,0 +1,322 @@
+/*
+ * f2fs IO tracer
+ *
+ * Copyright (c) 2014 Motorola Mobility
+ * Copyright (c) 2014 Jaegeuk Kim <jaegeuk@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#define _LARGEFILE64_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <assert.h>
+#include <locale.h>
+
+#define P_NAMELEN 16
+
+/* For global trace methods */
+enum show_type {
+ SHOW_PID,
+ SHOW_FTYPE,
+ SHOW_ALL,
+};
+
+enum trace_types {
+ TP_PID,
+ TP_IOS,
+ TP_MAX,
+};
+
+struct tps {
+ enum trace_types type;
+ const char *name;
+};
+
+struct tps trace_points[] = {
+ { TP_PID, "f2fs_trace_pid" },
+ { TP_IOS, "f2fs_trace_ios" },
+};
+
+/* For f2fs_trace_pid and f2fs_trace_ios */
+enum rw_type {
+ READ,
+ WRITE,
+ MAX_RW,
+};
+
+enum file_type {
+ __NORMAL_FILE,
+ __DIR_FILE,
+ __NODE_FILE,
+ __META_FILE,
+ __ATOMIC_FILE,
+ __VOLATILE_FILE,
+ __MISC_FILE,
+ __NR_FILES,
+};
+
+char *file_type_string[] = {
+ "User ",
+ "Dir ",
+ "Node ",
+ "Meta ",
+ "Atomic ",
+ "Voltile ",
+ "Misc ",
+};
+
+struct pid_ent {
+ int pid;
+ char name[P_NAMELEN];
+ unsigned long long io[__NR_FILES][MAX_RW];
+ unsigned long long total_io[MAX_RW];
+ LIST_ENTRY(pid_ent) ptr;
+};
+
+/* global variables */
+int major = 0, minor = 0;
+int show_option = SHOW_ALL;
+unsigned long long total_io[__NR_FILES][MAX_RW];
+
+LIST_HEAD(plist, pid_ent) pid_info;
+
+/* Functions */
+static inline int atoh(char *str)
+{
+ int val;
+ sscanf(str, "%x", &val);
+ return val;
+}
+
+static void do_init()
+{
+ struct pid_ent *misc;
+
+ misc = calloc(1, sizeof(struct pid_ent));
+ assert(misc);
+
+ LIST_INIT(&pid_info);
+ LIST_INSERT_HEAD(&pid_info, misc, ptr);
+}
+
+void show_usage()
+{
+ printf("\nUsage: parse.f2fs [options] log_file\n");
+ printf("[options]:\n");
+ printf(" -a RW sorted by pid & file types\n");
+ printf(" -f RW sorted by file types\n");
+ printf(" -p RW sorted by pid\n");
+ printf(" -m major number\n");
+ printf(" -n minor number\n");
+ exit(1);
+}
+
+static int parse_options(int argc, char *argv[])
+{
+ const char *option_string = "fm:n:p";
+ int option = 0;
+
+ while ((option = getopt(argc, argv, option_string)) != EOF) {
+ switch (option) {
+ case 'f':
+ show_option = SHOW_FTYPE;
+ break;
+ case 'm':
+ major = atoh(optarg);
+ break;
+ case 'n':
+ minor = atoh(optarg);
+ break;
+ case 'p':
+ show_option = SHOW_PID;
+ break;
+ default:
+ printf("\tError: Unknown option %c\n", option);
+ show_usage();
+ break;
+ }
+ }
+ if ((optind + 1) != argc) {
+ printf("\tError: Log file is not specified.\n");
+ show_usage();
+ }
+ return optind;
+}
+
+struct pid_ent *get_pid_entry(int pid)
+{
+ struct pid_ent *entry;
+
+ LIST_FOREACH(entry, &pid_info, ptr) {
+ if (entry->pid == pid)
+ return entry;
+ }
+ return LIST_FIRST(&pid_info);
+}
+
+static void handle_tp_pid(char *ptr)
+{
+ struct pid_ent *pent;
+
+ pent = calloc(1, sizeof(struct pid_ent));
+ assert(pent);
+
+ ptr = strtok(NULL, " ");
+ pent->pid = atoh(ptr);
+
+ ptr = strtok(NULL, " ");
+ strcpy(pent->name, ptr);
+
+ LIST_INSERT_HEAD(&pid_info, pent, ptr);
+}
+
+static void handle_tp_ios(char *ptr)
+{
+ int pid, type, rw, len;
+ struct pid_ent *p;
+
+ ptr = strtok(NULL, " ");
+ pid = atoh(ptr);
+
+ ptr = strtok(NULL, " ");
+ ptr = strtok(NULL, " ");
+ type = atoh(ptr);
+
+ ptr = strtok(NULL, " ");
+ rw = atoh(ptr);
+
+ ptr = strtok(NULL, " ");
+ /* unsigned long long blkaddr = atoh(ptr); */
+
+ ptr = strtok(NULL, " ");
+ len = atoh(ptr);
+
+ /* update per-pid stat */
+ p = get_pid_entry(pid);
+ p->io[type][rw & 0x1] += len;
+ p->total_io[rw & 0x1] += len;
+
+ /* update total stat */
+ total_io[type][rw & 0x1] += len;
+}
+
+static void do_parse(FILE *file)
+{
+ char line[300];
+ char *ptr;
+ int i;
+
+ while (fgets(line, sizeof(line), file) != NULL) {
+ ptr = strtok(line, ":");
+
+ ptr = strtok(NULL, " :");
+
+ for (i = 0; i < TP_MAX; i++) {
+ if (!strcmp(ptr, trace_points[i].name))
+ break;
+ }
+ if (i == TP_MAX)
+ continue;
+ ptr = strtok(NULL, " :");
+ if (major && major != atoh(ptr))
+ continue;
+ ptr = strtok(NULL, " :");
+ if (minor && minor != atoh(ptr))
+ continue;
+
+ switch (i) {
+ case TP_PID:
+ handle_tp_pid(ptr);
+ break;
+ case TP_IOS:
+ handle_tp_ios(ptr);
+ break;
+ }
+ }
+}
+
+static void __print_pid()
+{
+ struct pid_ent *entry;
+ int i;
+
+ setlocale(LC_ALL, "");
+ printf("%8s %16s %17s ||", "PID", "NAME", "R/W in 4KB");
+ for (i = 0; i < __NR_FILES; i++)
+ printf(" %17s |", file_type_string[i]);
+ printf("\n");
+
+ LIST_FOREACH(entry, &pid_info, ptr) {
+ printf("%8x %16s %'8lld %'8lld ||",
+ entry->pid, entry->name,
+ entry->total_io[READ],
+ entry->total_io[WRITE]);
+ for (i = 0; i < __NR_FILES; i++)
+ printf(" %'8lld %'8lld |",
+ entry->io[i][READ],
+ entry->io[i][WRITE]);
+ printf("\n");
+ }
+}
+
+static void __print_ftype()
+{
+ int i;
+
+ setlocale(LC_ALL, "");
+ printf("\n===== Data R/W in 4KB accoring to File types =====\n");
+ for (i = 0; i < __NR_FILES; i++)
+ printf(" %17s |", file_type_string[i]);
+ printf("\n");
+
+ for (i = 0; i < __NR_FILES; i++)
+ printf(" %'8lld %'8lld |",
+ total_io[i][READ],
+ total_io[i][WRITE]);
+ printf("\n");
+}
+
+static void do_print()
+{
+ switch (show_option) {
+ case SHOW_PID:
+ __print_pid();
+ break;
+ case SHOW_FTYPE:
+ __print_ftype();
+ break;
+ case SHOW_ALL:
+ __print_pid();
+ printf("\n\n");
+ __print_ftype();
+ break;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ FILE *file;
+ int opt;
+
+ opt = parse_options(argc, argv);
+
+ file = fopen(argv[opt], "r");
+ if (!file) {
+ perror("open log file");
+ exit(EXIT_FAILURE);
+ }
+
+ do_init();
+
+ do_parse(file);
+
+ do_print();
+
+ fclose(file);
+ return 0;
+}
--
2.1.1
------------------------------------------------------------------------------
Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server
from Actuate! Instantly Supercharge Your Business Reports and Dashboards
with Interactivity, Sharing, Native Excel Exports, App Integration & more
Get technology previously reserved for billion-dollar corporations, FREE
http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2014-12-21 16:37 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-12-21 16:37 [PATCH 1/2] dump.f2fs: dump owner of data given block address Jaegeuk Kim
2014-12-21 16:37 ` [PATCH 2/2] parse.f2fs: add a tool to parse IO traces made by runtime f2fs Jaegeuk Kim
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).