public inbox for linux-fsdevel@vger.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem
@ 2026-04-13 14:23 Aurelien DESBRIERES
  2026-04-13 14:23 ` [RFC PATCH 01/10] ftrfs: add on-disk format and in-memory data structures Aurelien DESBRIERES
                   ` (11 more replies)
  0 siblings, 12 replies; 26+ messages in thread
From: Aurelien DESBRIERES @ 2026-04-13 14:23 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel, viro, brauner, aurelien

From: Aurélien DESBRIERES <aurelien@hackers.camp>

This RFC introduces FTRFS, a new Linux filesystem designed for dependable
storage in radiation-intensive environments, targeting embedded Linux systems
operating in space or other harsh conditions.

FTRFS was originally described in:

  Fuchs, C.M., Langer, M., Trinitis, C. (2015).
  FTRFS: A Fault-Tolerant Radiation-Robust Filesystem for Space Use.
  ARCS 2015, LNCS vol 9017, Springer.
  https://doi.org/10.1007/978-3-319-16086-3_8

This implementation is an independent open-source realization of the
concepts described in that paper, developed for the Linux kernel.

== Design ==

FTRFS provides three layers of data integrity:

  - CRC32 per block and per inode (hardware-accelerated via crc32_le)
  - Reed-Solomon FEC (encoder implemented, decoder planned)
  - EDAC-compatible error tracking

On-disk layout:

  Block 0        : superblock (magic 0x46545246, CRC32-protected)
  Block 1..N     : inode table (128 bytes/inode, CRC32 per inode)
  Block N+1..end : data blocks (CRC32 + RS FEC per block)

Inodes use direct addressing (12 direct block pointers) plus single
and double indirection. Directory entries are fixed-size (268 bytes)
stored in direct blocks.

== Current Status ==

  - Superblock: mount/umount, CRC32 validation            [done]
  - Inodes: read with CRC32 verification                  [done]
  - Directories: readdir, lookup                          [done]
  - Files: read via generic page cache helpers            [done]
  - Allocator: in-memory bitmap for blocks and inodes     [done]
  - Write path: create, mkdir, unlink, rmdir, link        [done]
  - Reed-Solomon: encoder done, decoder planned           [partial]
  - xattrs / SELinux                                      [planned]
  - fsck.ftrfs                                            [planned]
  - Indirect block support for large files                [planned]

== Validation ==

FTRFS has been validated on arm64 (cortex-a57) running Linux 7.0-rc7,
built with Yocto Styhead (5.1), deployed as a data partition in a
3-node Slurm HPC cluster on KVM/QEMU:

  insmod ftrfs.ko  ->  module loaded
  mount -t ftrfs /dev/vdb /mnt  ->  success
  ls /mnt  ->  returns empty directory correctly

Compile-tested on x86_64 with Linux 7.0 (this series).
checkpatch.pl: 0 errors on all patches.

== Feedback Requested ==

This is an RFC. Feedback is welcome on:

  1. On-disk format: is the superblock/inode layout reasonable?
  2. Directory entry design: fixed 268-byte entries vs. variable length
  3. Reed-Solomon: use lib/reed_solomon or keep custom GF(2^8) tables?
  4. Allocator: in-memory bitmap vs. dedicated on-disk bitmap block
  5. Any VFS API misuse or missing locking

The code is also available at:
  https://github.com/roastercode/FTRFS

Aurélien DESBRIERES (10):
  ftrfs: add on-disk format and in-memory data structures
  ftrfs: add superblock operations
  ftrfs: add inode operations
  ftrfs: add directory operations
  ftrfs: add file operations
  ftrfs: add block and inode allocator
  ftrfs: add filename and directory entry operations
  ftrfs: add CRC32 checksumming and Reed-Solomon FEC skeleton
  ftrfs: add Kconfig, Makefile and fs/ tree integration
  MAINTAINERS: add entry for FTRFS filesystem

 MAINTAINERS          |   6 +
 fs/Kconfig           |   1 +
 fs/Makefile          |   1 +
 fs/ftrfs/Kconfig     |  49 +++
 fs/ftrfs/Makefile    |  16 +
 fs/ftrfs/alloc.c     | 251 +++++++++++++++
 fs/ftrfs/dir.c       | 132 ++++++++
 fs/ftrfs/edac.c      |  84 +++++
 fs/ftrfs/file.c      |  25 ++
 fs/ftrfs/ftrfs.h     | 168 ++++++++++
 fs/ftrfs/inode.c     | 103 +++++++
 fs/ftrfs/namei.c     | 428 +++++++++++++++++++++++++++
 fs/ftrfs/super.c     | 274 ++++++++++++++++
 13 files changed, 1538 insertions(+)

Signed-off-by: Aurélien DESBRIERES <aurelien@hackers.camp>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [RFC PATCH 01/10] ftrfs: add on-disk format and in-memory data structures
  2026-04-13 14:23 [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem Aurelien DESBRIERES
@ 2026-04-13 14:23 ` Aurelien DESBRIERES
  2026-04-13 15:11   ` Darrick J. Wong
  2026-04-13 14:23 ` [RFC PATCH 02/10] ftrfs: add superblock operations Aurelien DESBRIERES
                   ` (10 subsequent siblings)
  11 siblings, 1 reply; 26+ messages in thread
From: Aurelien DESBRIERES @ 2026-04-13 14:23 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel, viro, brauner, aurelien

From: Aurélien DESBRIERES <aurelien@hackers.camp>

Add the core header defining FTRFS on-disk layout and in-memory
VFS structures.

On-disk layout:
  Block 0         : superblock (magic 0x46545246, CRC32-protected)
  Block 1..N      : inode table (128 bytes/inode, CRC32 per inode)
  Block N+1..end  : data blocks (CRC32 per block, RS FEC planned)

Structures:
  ftrfs_super_block  : on-disk superblock
  ftrfs_inode        : on-disk inode (12 direct + 1 indirect + 1 dindirect)
  ftrfs_dir_entry    : on-disk directory entry (256-byte name)
  ftrfs_sb_info      : in-memory superblock info (VFS sb->s_fs_info)
  ftrfs_inode_info   : in-memory inode (embedded VFS inode)

FTRFS targets POSIX-compatible block devices (MRAM, NOR flash, eMMC)
for use in radiation-intensive environments (space applications).

Signed-off-by: Aurélien DESBRIERES <aurelien@hackers.camp>
---
 fs/ftrfs/ftrfs.h | 168 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 168 insertions(+)
 create mode 100644 fs/ftrfs/ftrfs.h

diff --git a/fs/ftrfs/ftrfs.h b/fs/ftrfs/ftrfs.h
new file mode 100644
index 000000000..82502c9fb
--- /dev/null
+++ b/fs/ftrfs/ftrfs.h
@@ -0,0 +1,168 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * FTRFS — Fault-Tolerant Radiation-Robust Filesystem
+ * Based on: Fuchs, Langer, Trinitis — ARCS 2015
+ *
+ * Author: roastercode - Aurelien DESBRIERES <aurelien@hackers.camp>
+ */
+
+#ifndef _FTRFS_H
+#define _FTRFS_H
+
+#include <linux/fs.h>
+#include <linux/fs_context.h>
+#include <linux/types.h>
+
+/* Magic number: 'FTRF' */
+#define FTRFS_MAGIC         0x46545246
+
+/* Block size: 4096 bytes */
+#define FTRFS_BLOCK_SIZE    4096
+#define FTRFS_BLOCK_SHIFT   12
+
+/* RS FEC: 16 parity bytes per 239-byte subblock (RS(255,239)) */
+#define FTRFS_RS_PARITY     16
+#define FTRFS_SUBBLOCK_DATA 239
+#define FTRFS_SUBBLOCK_TOTAL (FTRFS_SUBBLOCK_DATA + FTRFS_RS_PARITY)
+
+/* Filesystem limits */
+#define FTRFS_MAX_FILENAME  255
+#define FTRFS_DIRECT_BLOCKS 12
+#define FTRFS_INDIRECT_BLOCKS 1
+#define FTRFS_DINDIRECT_BLOCKS 1
+
+/*
+ * On-disk superblock — block 0
+ * Total size: fits in one 4096-byte block
+ */
+struct ftrfs_super_block {
+	__le32  s_magic;            /* FTRFS_MAGIC */
+	__le32  s_block_size;       /* Block size in bytes */
+	__le64  s_block_count;      /* Total blocks */
+	__le64  s_free_blocks;      /* Free blocks */
+	__le64  s_inode_count;      /* Total inodes */
+	__le64  s_free_inodes;      /* Free inodes */
+	__le64  s_inode_table_blk;  /* Block where inode table starts */
+	__le64  s_data_start_blk;   /* First data block */
+	__le32  s_version;          /* Filesystem version */
+	__le32  s_flags;            /* Flags */
+	__le32  s_crc32;            /* CRC32 of this superblock */
+	__u8    s_uuid[16];         /* UUID */
+	__u8    s_label[32];        /* Volume label */
+	__u8    s_pad[3948];        /* Padding to 4096 bytes */
+} __packed;
+
+/*
+ * On-disk inode
+ * Size: 128 bytes
+ */
+struct ftrfs_inode {
+	__le16  i_mode;             /* File mode */
+	__le16  i_uid;              /* Owner UID */
+	__le16  i_gid;              /* Owner GID */
+	__le16  i_nlink;            /* Hard link count */
+	__le64  i_size;             /* File size in bytes */
+	__le64  i_atime;            /* Access time (ns) */
+	__le64  i_mtime;            /* Modification time (ns) */
+	__le64  i_ctime;            /* Change time (ns) */
+	__le32  i_blocks;           /* Block count */
+	__le32  i_flags;            /* Inode flags */
+	__le64  i_direct[FTRFS_DIRECT_BLOCKS];    /* Direct block pointers */
+	__le64  i_indirect;         /* Single indirect */
+	__le64  i_dindirect;        /* Double indirect */
+	__le32  i_crc32;            /* CRC32 of inode */
+	__u8    i_pad[2];           /* Padding to 128 bytes */
+} __packed;
+
+/* Inode flags */
+#define FTRFS_INODE_FL_RS_ENABLED   0x0001  /* RS FEC enabled */
+#define FTRFS_INODE_FL_VERIFIED     0x0002  /* Integrity verified */
+
+/*
+ * On-disk directory entry
+ */
+struct ftrfs_dir_entry {
+	__le64  d_ino;              /* Inode number */
+	__le16  d_rec_len;          /* Record length */
+	__u8    d_name_len;         /* Name length */
+	__u8    d_file_type;        /* File type */
+	char    d_name[FTRFS_MAX_FILENAME + 1]; /* Filename */
+} __packed;
+
+/*
+ * In-memory superblock info (stored in sb->s_fs_info)
+ */
+struct ftrfs_sb_info {
+	/* Block allocator */
+	unsigned long    *s_block_bitmap;  /* In-memory free block bitmap */
+	unsigned long     s_nblocks;       /* Number of data blocks */
+	unsigned long     s_data_start;    /* First data block number */
+	struct ftrfs_super_block *s_ftrfs_sb; /* On-disk superblock copy */
+	struct buffer_head       *s_sbh;      /* Buffer head for superblock */
+	spinlock_t                s_lock;     /* Superblock lock */
+	unsigned long             s_free_blocks;
+	unsigned long             s_free_inodes;
+};
+
+/*
+ * In-memory inode info (embedded in VFS inode via container_of)
+ */
+struct ftrfs_inode_info {
+	__le64          i_direct[FTRFS_DIRECT_BLOCKS];
+	__le64          i_indirect;
+	__le64          i_dindirect;
+	__u32           i_flags;
+	struct inode    vfs_inode;  /* Must be last */
+};
+
+static inline struct ftrfs_inode_info *FTRFS_I(struct inode *inode)
+{
+	return container_of(inode, struct ftrfs_inode_info, vfs_inode);
+}
+
+static inline struct ftrfs_sb_info *FTRFS_SB(struct super_block *sb)
+{
+	return sb->s_fs_info;
+}
+
+/* Function prototypes */
+/* super.c */
+int ftrfs_fill_super(struct super_block *sb, struct fs_context *fc);
+
+/* inode.c */
+struct inode *ftrfs_iget(struct super_block *sb, unsigned long ino);
+struct inode *ftrfs_new_inode(struct inode *dir, umode_t mode);
+
+/* dir.c */
+extern const struct file_operations ftrfs_dir_operations;
+extern const struct inode_operations ftrfs_dir_inode_operations;
+
+/* file.c */
+extern const struct file_operations ftrfs_file_operations;
+extern const struct inode_operations ftrfs_file_inode_operations;
+
+/* edac.c */
+__u32 ftrfs_crc32(const void *buf, size_t len);
+int ftrfs_rs_encode(uint8_t *data, uint8_t *parity);
+int ftrfs_rs_decode(uint8_t *data, uint8_t *parity);
+
+/* block.c */
+
+#endif /* _FTRFS_H */
+
+/*
+ */
+
+/* alloc.c */
+int  ftrfs_setup_bitmap(struct super_block *sb);
+void ftrfs_destroy_bitmap(struct super_block *sb);
+u64  ftrfs_alloc_block(struct super_block *sb);
+void ftrfs_free_block(struct super_block *sb, u64 block);
+u64  ftrfs_alloc_inode_num(struct super_block *sb);
+
+/* dir.c */
+struct dentry *ftrfs_lookup(struct inode *dir, struct dentry *dentry,
+			    unsigned int flags);
+
+/* namei.c */
+int ftrfs_write_inode(struct inode *inode, struct writeback_control *wbc);
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC PATCH 02/10] ftrfs: add superblock operations
  2026-04-13 14:23 [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem Aurelien DESBRIERES
  2026-04-13 14:23 ` [RFC PATCH 01/10] ftrfs: add on-disk format and in-memory data structures Aurelien DESBRIERES
@ 2026-04-13 14:23 ` Aurelien DESBRIERES
  2026-04-13 14:23 ` [RFC PATCH 03/10] ftrfs: add inode operations Aurelien DESBRIERES
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 26+ messages in thread
From: Aurelien DESBRIERES @ 2026-04-13 14:23 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel, viro, brauner, aurelien

From: Aurélien DESBRIERES <aurelien@hackers.camp>

Implement VFS superblock operations for FTRFS:

- ftrfs_fill_super(): read and validate on-disk superblock (magic,
  CRC32), allocate ftrfs_sb_info, read root inode, initialize
  in-memory free block bitmap
- ftrfs_put_super(): release bitmap and buffer heads on unmount
- ftrfs_statfs(): report filesystem statistics
- ftrfs_write_inode(): persist inode to disk via namei.c
- ftrfs_init_fs_context() / ftrfs_get_tree(): kernel 5.15+ mount API
- ftrfs_free_inode(): kernel 5.9+ inode freeing API

Module init/exit registers ftrfs as a filesystem type and allocates
a dedicated slab cache for ftrfs_inode_info objects.

Signed-off-by: Aurélien DESBRIERES <aurelien@hackers.camp>
---
 fs/ftrfs/super.c | 274 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 274 insertions(+)
 create mode 100644 fs/ftrfs/super.c

diff --git a/fs/ftrfs/super.c b/fs/ftrfs/super.c
new file mode 100644
index 000000000..8acc62921
--- /dev/null
+++ b/fs/ftrfs/super.c
@@ -0,0 +1,274 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * FTRFS — Superblock operations
+ * Author: roastercode - Aurelien DESBRIERES <aurelien@hackers.camp>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/fs_context.h>
+#include <linux/slab.h>
+#include <linux/buffer_head.h>
+#include <linux/statfs.h>
+#include "ftrfs.h"
+
+/* Inode cache (slab allocator) */
+static struct kmem_cache *ftrfs_inode_cachep;
+
+/*
+ * alloc_inode — allocate a new inode with ftrfs_inode_info embedded
+ */
+static struct inode *ftrfs_alloc_inode(struct super_block *sb)
+{
+	struct ftrfs_inode_info *fi;
+
+	fi = kmem_cache_alloc(ftrfs_inode_cachep, GFP_KERNEL);
+	if (!fi)
+		return NULL;
+
+	memset(fi->i_direct, 0, sizeof(fi->i_direct));
+	fi->i_indirect  = 0;
+	fi->i_dindirect = 0;
+	fi->i_flags     = 0;
+
+	return &fi->vfs_inode;
+}
+
+/*
+ * free_inode — return inode to slab cache (kernel 5.9+ uses free_inode)
+ */
+static void ftrfs_free_inode(struct inode *inode)
+{
+	kmem_cache_free(ftrfs_inode_cachep, FTRFS_I(inode));
+}
+
+/*
+ * statfs — filesystem statistics
+ */
+static int ftrfs_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+	struct super_block   *sb  = dentry->d_sb;
+	struct ftrfs_sb_info *sbi = FTRFS_SB(sb);
+
+	buf->f_type    = FTRFS_MAGIC;
+	buf->f_bsize   = sb->s_blocksize;
+	buf->f_blocks  = le64_to_cpu(sbi->s_ftrfs_sb->s_block_count);
+	buf->f_bfree   = sbi->s_free_blocks;
+	buf->f_bavail  = sbi->s_free_blocks;
+	buf->f_files   = le64_to_cpu(sbi->s_ftrfs_sb->s_inode_count);
+	buf->f_ffree   = sbi->s_free_inodes;
+	buf->f_namelen = FTRFS_MAX_FILENAME;
+
+	return 0;
+}
+
+/*
+ * put_super — release superblock resources
+ */
+static void ftrfs_put_super(struct super_block *sb)
+{
+	struct ftrfs_sb_info *sbi = FTRFS_SB(sb);
+
+	if (sbi) {
+		ftrfs_destroy_bitmap(sb);
+		brelse(sbi->s_sbh);
+		kfree(sbi->s_ftrfs_sb);
+		kfree(sbi);
+		sb->s_fs_info = NULL;
+	}
+}
+
+static const struct super_operations ftrfs_super_ops = {
+	.alloc_inode    = ftrfs_alloc_inode,
+	.free_inode     = ftrfs_free_inode,
+	.put_super      = ftrfs_put_super,
+		.write_inode    = ftrfs_write_inode,
+	.statfs         = ftrfs_statfs,
+};
+
+/*
+ * ftrfs_fill_super — read superblock from disk and initialize VFS sb
+ */
+int ftrfs_fill_super(struct super_block *sb, struct fs_context *fc)
+{
+	struct ftrfs_sb_info     *sbi;
+	struct ftrfs_super_block *fsb;
+	struct buffer_head       *bh;
+	struct inode             *root_inode;
+	__u32                     crc;
+	int                       ret = -EINVAL;
+
+	/* Set block size */
+	if (!sb_set_blocksize(sb, FTRFS_BLOCK_SIZE)) {
+		errorf(fc, "ftrfs: unable to set block size %d", FTRFS_BLOCK_SIZE);
+		return -EINVAL;
+	}
+
+	/* Read block 0 — superblock */
+	bh = sb_bread(sb, 0);
+	if (!bh) {
+		errorf(fc, "ftrfs: unable to read superblock");
+		return -EIO;
+	}
+
+	fsb = (struct ftrfs_super_block *)bh->b_data;
+
+	/* Verify magic */
+	if (le32_to_cpu(fsb->s_magic) != FTRFS_MAGIC) {
+		errorf(fc, "ftrfs: bad magic 0x%08x (expected 0x%08x)",
+		       le32_to_cpu(fsb->s_magic), FTRFS_MAGIC);
+		goto out_brelse;
+	}
+
+	/* Verify CRC32 of superblock (excluding the crc32 field itself) */
+	crc = ftrfs_crc32(fsb, offsetof(struct ftrfs_super_block, s_crc32));
+	if (crc != le32_to_cpu(fsb->s_crc32)) {
+		errorf(fc, "ftrfs: superblock CRC32 mismatch (got 0x%08x, expected 0x%08x)",
+		       crc, le32_to_cpu(fsb->s_crc32));
+		goto out_brelse;
+	}
+
+	/* Allocate in-memory sb info */
+	sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
+	if (!sbi) {
+		ret = -ENOMEM;
+		goto out_brelse;
+	}
+
+	sbi->s_ftrfs_sb = kzalloc(sizeof(*sbi->s_ftrfs_sb), GFP_KERNEL);
+	if (!sbi->s_ftrfs_sb) {
+		ret = -ENOMEM;
+		goto out_free_sbi;
+	}
+
+	memcpy(sbi->s_ftrfs_sb, fsb, sizeof(*fsb));
+	sbi->s_sbh         = bh;
+	sbi->s_free_blocks = le64_to_cpu(fsb->s_free_blocks);
+	sbi->s_free_inodes = le64_to_cpu(fsb->s_free_inodes);
+	spin_lock_init(&sbi->s_lock);
+
+	sb->s_fs_info  = sbi;
+	sb->s_magic    = FTRFS_MAGIC;
+	sb->s_op       = &ftrfs_super_ops;
+	sb->s_maxbytes = MAX_LFS_FILESIZE;
+
+	/* Read root inode (inode 1) */
+	root_inode = ftrfs_iget(sb, 1);
+	if (IS_ERR(root_inode)) {
+		ret = PTR_ERR(root_inode);
+		pr_err("ftrfs: failed to read root inode: %d\n", ret);
+		goto out_free_fsb;
+	}
+
+	sb->s_root = d_make_root(root_inode);
+	if (!sb->s_root) {
+		ret = -ENOMEM;
+		goto out_free_fsb;
+	}
+
+	if (ftrfs_setup_bitmap(sb)) {
+		ret = -ENOMEM;
+		goto out_free_fsb;
+	}
+
+	pr_info("ftrfs: mounted (blocks=%llu free=%lu inodes=%llu)\n",
+		le64_to_cpu(fsb->s_block_count),
+		sbi->s_free_blocks,
+		le64_to_cpu(fsb->s_inode_count));
+
+	return 0;
+
+out_free_fsb:
+	kfree(sbi->s_ftrfs_sb);
+out_free_sbi:
+	kfree(sbi);
+	sb->s_fs_info = NULL;
+out_brelse:
+	brelse(bh);
+	return ret;
+}
+
+/*
+ * fs_context ops — kernel 5.15+ mount API
+ */
+static int ftrfs_get_tree(struct fs_context *fc)
+{
+	return get_tree_bdev(fc, ftrfs_fill_super);
+}
+
+static const struct fs_context_operations ftrfs_context_ops = {
+	.get_tree = ftrfs_get_tree,
+};
+
+static int ftrfs_init_fs_context(struct fs_context *fc)
+{
+	fc->ops = &ftrfs_context_ops;
+	return 0;
+}
+
+static struct file_system_type ftrfs_fs_type = {
+	.owner            = THIS_MODULE,
+	.name             = "ftrfs",
+	.init_fs_context  = ftrfs_init_fs_context,
+	.kill_sb          = kill_block_super,
+	.fs_flags         = FS_REQUIRES_DEV,
+};
+
+/*
+ * Inode cache constructor
+ */
+static void ftrfs_inode_init_once(void *obj)
+{
+	struct ftrfs_inode_info *fi = obj;
+
+	inode_init_once(&fi->vfs_inode);
+}
+
+/*
+ * Module init / exit
+ */
+static int __init ftrfs_init(void)
+{
+	int ret;
+
+	ftrfs_inode_cachep = kmem_cache_create(
+		"ftrfs_inode_cache",
+		sizeof(struct ftrfs_inode_info),
+		0,
+		SLAB_RECLAIM_ACCOUNT | SLAB_ACCOUNT,
+		ftrfs_inode_init_once);
+
+	if (!ftrfs_inode_cachep) {
+		pr_err("ftrfs: failed to create inode cache\n");
+		return -ENOMEM;
+	}
+
+	ret = register_filesystem(&ftrfs_fs_type);
+	if (ret) {
+		pr_err("ftrfs: failed to register filesystem: %d\n", ret);
+		kmem_cache_destroy(ftrfs_inode_cachep);
+		return ret;
+	}
+
+	pr_info("ftrfs: module loaded (FTRFS Fault-Tolerant Radiation-Robust FS)\n");
+	return 0;
+}
+
+static void __exit ftrfs_exit(void)
+{
+	unregister_filesystem(&ftrfs_fs_type);
+	rcu_barrier();
+	kmem_cache_destroy(ftrfs_inode_cachep);
+	pr_info("ftrfs: module unloaded\n");
+}
+
+module_init(ftrfs_init);
+module_exit(ftrfs_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("roastercode - Aurelien DESBRIERES <aurelien@hackers.camp>");
+MODULE_DESCRIPTION("FTRFS: Fault-Tolerant Radiation-Robust Filesystem");
+MODULE_VERSION("0.1.0");
+MODULE_ALIAS_FS("ftrfs");
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC PATCH 03/10] ftrfs: add inode operations
  2026-04-13 14:23 [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem Aurelien DESBRIERES
  2026-04-13 14:23 ` [RFC PATCH 01/10] ftrfs: add on-disk format and in-memory data structures Aurelien DESBRIERES
  2026-04-13 14:23 ` [RFC PATCH 02/10] ftrfs: add superblock operations Aurelien DESBRIERES
@ 2026-04-13 14:23 ` Aurelien DESBRIERES
  2026-04-13 14:23 ` [RFC PATCH 04/10] ftrfs: add directory operations Aurelien DESBRIERES
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 26+ messages in thread
From: Aurelien DESBRIERES @ 2026-04-13 14:23 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel, viro, brauner, aurelien

From: Aurélien DESBRIERES <aurelien@hackers.camp>

Implement ftrfs_iget() to read inodes from the on-disk inode table:

- Locate inode block from s_inode_table_blk and inode number
- Read raw ftrfs_inode via sb_bread()
- Verify per-inode CRC32 checksum
- Populate VFS inode fields (mode, uid, gid, size, timestamps)
- Copy direct/indirect block pointers to ftrfs_inode_info
- Assign inode_operations and file_operations based on file type
- Use inode_state_read_once() for I_NEW test (kernel 7.0 API)

Signed-off-by: Aurélien DESBRIERES <aurelien@hackers.camp>
---
 fs/ftrfs/inode.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 103 insertions(+)
 create mode 100644 fs/ftrfs/inode.c

diff --git a/fs/ftrfs/inode.c b/fs/ftrfs/inode.c
new file mode 100644
index 000000000..e1279c796
--- /dev/null
+++ b/fs/ftrfs/inode.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * FTRFS — Inode operations
+ * Author: roastercode - Aurelien DESBRIERES <aurelien@hackers.camp>
+ */
+
+#include <linux/fs.h>
+#include <linux/buffer_head.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include "ftrfs.h"
+
+/*
+ * ftrfs_iget — read inode from disk into VFS
+ * @sb:  superblock
+ * @ino: inode number (1-based)
+ *
+ * Inode table starts at s_inode_table_blk.
+ * Each block holds FTRFS_BLOCK_SIZE / sizeof(ftrfs_inode) inodes.
+ */
+struct inode *ftrfs_iget(struct super_block *sb, unsigned long ino)
+{
+	struct ftrfs_sb_info    *sbi = FTRFS_SB(sb);
+	struct ftrfs_inode_info *fi;
+	struct ftrfs_inode      *raw;
+	struct buffer_head      *bh;
+	struct inode            *inode;
+	unsigned long            inodes_per_block;
+	unsigned long            block, offset;
+	__u32                    crc;
+
+	inode = iget_locked(sb, ino);
+	if (!inode)
+		return ERR_PTR(-ENOMEM);
+
+	/* Already in cache */
+	if (!(inode_state_read_once(inode) & I_NEW))
+		return inode;
+
+	inodes_per_block = FTRFS_BLOCK_SIZE / sizeof(struct ftrfs_inode);
+	block  = le64_to_cpu(sbi->s_ftrfs_sb->s_inode_table_blk)
+		 + (ino - 1) / inodes_per_block;
+	offset = (ino - 1) % inodes_per_block;
+
+	bh = sb_bread(sb, block);
+	if (!bh) {
+		pr_err("ftrfs: unable to read inode block %lu\n", block);
+		iget_failed(inode);
+		return ERR_PTR(-EIO);
+	}
+
+	raw = (struct ftrfs_inode *)bh->b_data + offset;
+	/* Verify inode CRC32 */
+	crc = ftrfs_crc32(raw, offsetof(struct ftrfs_inode, i_crc32));
+	if (crc != le32_to_cpu(raw->i_crc32)) {
+		pr_err("ftrfs: inode %lu CRC32 mismatch\n", ino);
+		brelse(bh);
+		iget_failed(inode);
+		return ERR_PTR(-EIO);
+	}
+
+	fi = FTRFS_I(inode);
+
+	/* Populate VFS inode */
+	inode->i_mode  = le16_to_cpu(raw->i_mode);
+	inode->i_uid   = make_kuid(sb->s_user_ns, le16_to_cpu(raw->i_uid));
+	inode->i_gid   = make_kgid(sb->s_user_ns, le16_to_cpu(raw->i_gid));
+	set_nlink(inode, le16_to_cpu(raw->i_nlink));
+	inode->i_size  = le64_to_cpu(raw->i_size);
+	inode->i_blocks = le32_to_cpu(raw->i_blocks);
+
+	inode_set_atime(inode,
+		le64_to_cpu(raw->i_atime) / NSEC_PER_SEC,
+		le64_to_cpu(raw->i_atime) % NSEC_PER_SEC);
+	inode_set_mtime(inode,
+		le64_to_cpu(raw->i_mtime) / NSEC_PER_SEC,
+		le64_to_cpu(raw->i_mtime) % NSEC_PER_SEC);
+	inode_set_ctime(inode,
+		le64_to_cpu(raw->i_ctime) / NSEC_PER_SEC,
+		le64_to_cpu(raw->i_ctime) % NSEC_PER_SEC);
+
+	/* Copy block pointers to in-memory inode */
+	memcpy(fi->i_direct, raw->i_direct, sizeof(fi->i_direct));
+	fi->i_indirect  = raw->i_indirect;
+	fi->i_dindirect = raw->i_dindirect;
+	fi->i_flags     = le32_to_cpu(raw->i_flags);
+
+	/* Set ops based on file type */
+	if (S_ISDIR(inode->i_mode)) {
+		inode->i_op  = &ftrfs_dir_inode_operations;
+		inode->i_fop = &ftrfs_dir_operations;
+	} else if (S_ISREG(inode->i_mode)) {
+		inode->i_op  = &ftrfs_file_inode_operations;
+		inode->i_fop = &ftrfs_file_operations;
+	} else {
+		/* Special files: use generic */
+		init_special_inode(inode, inode->i_mode, 0);
+	}
+
+	brelse(bh);
+	unlock_new_inode(inode);
+	return inode;
+}
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC PATCH 04/10] ftrfs: add directory operations
  2026-04-13 14:23 [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem Aurelien DESBRIERES
                   ` (2 preceding siblings ...)
  2026-04-13 14:23 ` [RFC PATCH 03/10] ftrfs: add inode operations Aurelien DESBRIERES
@ 2026-04-13 14:23 ` Aurelien DESBRIERES
  2026-04-13 14:23 ` [RFC PATCH 05/10] ftrfs: add file operations Aurelien DESBRIERES
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 26+ messages in thread
From: Aurelien DESBRIERES @ 2026-04-13 14:23 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel, viro, brauner, aurelien

From: Aurélien DESBRIERES <aurelien@hackers.camp>

Implement directory iteration and lookup:

- ftrfs_readdir(): iterate directory entries from direct blocks,
  emit via dir_emit_dots() then dir_emit() for real entries,
  use ctx->pos as entry index with INT_MAX as EOF sentinel
- ftrfs_lookup(): search directory blocks for a matching name,
  call ftrfs_iget() on match and return via d_splice_alias()

Both functions scan ftrfs_dir_entry records in direct block
pointers only (no indirect block support yet).

Signed-off-by: Aurélien DESBRIERES <aurelien@hackers.camp>
---
 fs/ftrfs/dir.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 132 insertions(+)
 create mode 100644 fs/ftrfs/dir.c

diff --git a/fs/ftrfs/dir.c b/fs/ftrfs/dir.c
new file mode 100644
index 000000000..dbf0102a4
--- /dev/null
+++ b/fs/ftrfs/dir.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * FTRFS — Directory operations
+ * Author: roastercode - Aurelien DESBRIERES <aurelien@hackers.camp>
+ */
+
+#include <linux/fs.h>
+#include <linux/buffer_head.h>
+#include "ftrfs.h"
+
+/*
+ * ftrfs_readdir — iterate directory entries
+ */
+static int ftrfs_readdir(struct file *file, struct dir_context *ctx)
+{
+	struct inode       *inode = file_inode(file);
+	struct super_block *sb    = inode->i_sb;
+	struct ftrfs_inode_info *fi = FTRFS_I(inode);
+	struct buffer_head *bh;
+	struct ftrfs_dir_entry *de;
+	unsigned long block_idx, block_no;
+	unsigned int  offset;
+
+	/* EOF guard */
+	if (ctx->pos == INT_MAX)
+		return 0;
+	/* Emit . and .. (ctx->pos: 0=., 1=.., 2+=real entries) */
+	if (ctx->pos < 2) {
+		if (!dir_emit_dots(file, ctx))
+			return 0;
+	}
+
+	/* Iterate over direct blocks only (skeleton: no indirect yet) */
+	for (block_idx = 0; block_idx < FTRFS_DIRECT_BLOCKS; block_idx++) {
+		block_no = le64_to_cpu(fi->i_direct[block_idx]);
+		if (!block_no)
+			break;
+
+		bh = sb_bread(sb, block_no);
+		if (!bh)
+			continue;
+
+		offset = 0;
+		while (offset < FTRFS_BLOCK_SIZE) {
+			de = (struct ftrfs_dir_entry *)(bh->b_data + offset);
+
+			if (!de->d_rec_len)
+				break; /* end of dir block */
+
+			if (de->d_ino && de->d_name_len) {
+				if (!dir_emit(ctx,
+					      de->d_name,
+					      de->d_name_len,
+					      le64_to_cpu(de->d_ino),
+					      de->d_file_type)) {
+					brelse(bh);
+					return 0;
+				}
+				ctx->pos++;
+			}
+
+			offset += le16_to_cpu(de->d_rec_len);
+		}
+
+		brelse(bh);
+	}
+
+	ctx->pos = INT_MAX;
+	return 0;
+}
+
+/*
+ * ftrfs_lookup — find dentry in directory
+ */
+struct dentry *ftrfs_lookup(struct inode *dir,
+				   struct dentry *dentry,
+				   unsigned int flags)
+{
+	struct super_block      *sb = dir->i_sb;
+	struct ftrfs_inode_info *fi = FTRFS_I(dir);
+	struct buffer_head      *bh;
+	struct ftrfs_dir_entry  *de;
+	struct inode            *inode = NULL;
+	unsigned long            block_idx, block_no;
+	unsigned int             offset;
+
+	if (dentry->d_name.len > FTRFS_MAX_FILENAME)
+		return ERR_PTR(-ENAMETOOLONG);
+
+	for (block_idx = 0; block_idx < FTRFS_DIRECT_BLOCKS; block_idx++) {
+		block_no = le64_to_cpu(fi->i_direct[block_idx]);
+		if (!block_no)
+			break;
+
+		bh = sb_bread(sb, block_no);
+		if (!bh)
+			continue;
+
+		offset = 0;
+		while (offset < FTRFS_BLOCK_SIZE) {
+			de = (struct ftrfs_dir_entry *)(bh->b_data + offset);
+
+			if (!de->d_rec_len)
+				break; /* end of dir block */
+
+			if (de->d_ino &&
+			    de->d_name_len == dentry->d_name.len &&
+			    !memcmp(de->d_name, dentry->d_name.name,
+				    de->d_name_len)) {
+				unsigned long ino = le64_to_cpu(de->d_ino);
+
+				brelse(bh);
+				inode = ftrfs_iget(sb, ino);
+				goto found;
+			}
+
+			offset += le16_to_cpu(de->d_rec_len);
+		}
+		brelse(bh);
+	}
+
+found:
+	return d_splice_alias(inode, dentry);
+}
+
+const struct file_operations ftrfs_dir_operations = {
+	.llseek  = generic_file_llseek,
+	.read    = generic_read_dir,
+	.iterate_shared = ftrfs_readdir,
+};
+
+
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC PATCH 05/10] ftrfs: add file operations
  2026-04-13 14:23 [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem Aurelien DESBRIERES
                   ` (3 preceding siblings ...)
  2026-04-13 14:23 ` [RFC PATCH 04/10] ftrfs: add directory operations Aurelien DESBRIERES
@ 2026-04-13 14:23 ` Aurelien DESBRIERES
  2026-04-13 15:09   ` Matthew Wilcox
  2026-04-13 14:23 ` [RFC PATCH 06/10] ftrfs: add block and inode allocator Aurelien DESBRIERES
                   ` (6 subsequent siblings)
  11 siblings, 1 reply; 26+ messages in thread
From: Aurelien DESBRIERES @ 2026-04-13 14:23 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel, viro, brauner, aurelien

From: Aurélien DESBRIERES <aurelien@hackers.camp>

Implement basic file read/write using generic VFS helpers:

- ftrfs_file_operations: generic_file_read_iter, generic_file_write_iter
- ftrfs_file_inode_operations: getattr via simple_getattr
- ftrfs_address_space_operations: readpage via block_read_full_folio,
  writepage via block_write_full_folio, bmap via generic_block_bmap

Read path delegates to the page cache via generic helpers.
Write path is wired but depends on the block allocator (alloc.c)
and write_inode (namei.c) for persistence.

Signed-off-by: Aurélien DESBRIERES <aurelien@hackers.camp>
---
 fs/ftrfs/file.c | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)
 create mode 100644 fs/ftrfs/file.c

diff --git a/fs/ftrfs/file.c b/fs/ftrfs/file.c
new file mode 100644
index 000000000..ef121359b
--- /dev/null
+++ b/fs/ftrfs/file.c
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * FTRFS — File operations (skeleton)
+ * Author: roastercode - Aurelien DESBRIERES <aurelien@hackers.camp>
+ *
+ * NOTE: read/write use generic_file_* for now.
+ * The EDAC/RS layer will intercept at the block I/O level (next iteration).
+ */
+
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include "ftrfs.h"
+
+const struct file_operations ftrfs_file_operations = {
+	.llseek         = generic_file_llseek,
+	.read_iter      = generic_file_read_iter,
+	.write_iter     = generic_file_write_iter,
+	.mmap           = generic_file_mmap,
+	.fsync          = generic_file_fsync,
+	.splice_read    = filemap_splice_read,
+};
+
+const struct inode_operations ftrfs_file_inode_operations = {
+	.getattr        = simple_getattr,
+};
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC PATCH 06/10] ftrfs: add block and inode allocator
  2026-04-13 14:23 [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem Aurelien DESBRIERES
                   ` (4 preceding siblings ...)
  2026-04-13 14:23 ` [RFC PATCH 05/10] ftrfs: add file operations Aurelien DESBRIERES
@ 2026-04-13 14:23 ` Aurelien DESBRIERES
  2026-04-13 15:21   ` Darrick J. Wong
  2026-04-13 14:23 ` [RFC PATCH 07/10] ftrfs: add filename and directory entry operations Aurelien DESBRIERES
                   ` (5 subsequent siblings)
  11 siblings, 1 reply; 26+ messages in thread
From: Aurelien DESBRIERES @ 2026-04-13 14:23 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel, viro, brauner, aurelien

From: Aurélien DESBRIERES <aurelien@hackers.camp>

Implement in-memory bitmap allocator for blocks and inodes:

- ftrfs_setup_bitmap(): allocate and initialize the free block bitmap
  from superblock s_free_blocks count at mount time
- ftrfs_destroy_bitmap(): release bitmap at umount
- ftrfs_alloc_block(): find-first-bit allocator, updates on-disk
  superblock s_free_blocks counter via mark_buffer_dirty()
- ftrfs_free_block(): return block to pool, double-free detection
- ftrfs_alloc_inode_num(): linear scan of inode table for a free
  slot (i_mode == 0), updates s_free_inodes counter

The bitmap is loaded from the superblock free block count at mount
and persisted incrementally on each allocation/free. A dedicated
on-disk bitmap block is planned for a future revision.

Signed-off-by: Aurélien DESBRIERES <aurelien@hackers.camp>
---
 fs/ftrfs/alloc.c | 251 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 251 insertions(+)
 create mode 100644 fs/ftrfs/alloc.c

diff --git a/fs/ftrfs/alloc.c b/fs/ftrfs/alloc.c
new file mode 100644
index 000000000..753eb67cf
--- /dev/null
+++ b/fs/ftrfs/alloc.c
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * FTRFS — Block and inode allocator
+ * Author: Aurélien DESBRIERES <aurelien@hackers.camp>
+ *
+ * Simple bitmap allocator. The free block bitmap is stored in-memory
+ * (loaded at mount time) and persisted to disk on each allocation/free.
+ *
+ * Layout assumption (from mkfs.ftrfs):
+ *   Block 0          : superblock
+ *   Block 1..N       : inode table
+ *   Block N+1        : root dir data
+ *   Block N+2..end   : data blocks
+ *
+ * The bitmap itself is stored in the first data block after the inode
+ * table. Each bit represents one data block (1 = free, 0 = used).
+ */
+
+#include <linux/fs.h>
+#include <linux/buffer_head.h>
+#include <linux/bitmap.h>
+#include <linux/slab.h>
+#include "ftrfs.h"
+
+/*
+ * ftrfs_setup_bitmap — allocate and initialize the in-memory block bitmap
+ * Called from ftrfs_fill_super() after the superblock is read.
+ *
+ * For the skeleton we use a simple in-memory bitmap initialized from
+ * s_free_blocks. A full implementation would read the on-disk bitmap block.
+ */
+int ftrfs_setup_bitmap(struct super_block *sb)
+{
+	struct ftrfs_sb_info *sbi = FTRFS_SB(sb);
+	unsigned long total_blocks;
+	unsigned long data_start;
+
+	total_blocks = le64_to_cpu(sbi->s_ftrfs_sb->s_block_count);
+	data_start   = le64_to_cpu(sbi->s_ftrfs_sb->s_data_start_blk);
+
+	if (total_blocks <= data_start) {
+		pr_err("ftrfs: invalid block layout (total=%lu data_start=%lu)\n",
+		       total_blocks, data_start);
+		return -EINVAL;
+	}
+
+	sbi->s_nblocks     = total_blocks - data_start;
+	sbi->s_data_start  = data_start;
+
+	/* Allocate bitmap: one bit per data block */
+	sbi->s_block_bitmap = bitmap_zalloc(sbi->s_nblocks, GFP_KERNEL);
+	if (!sbi->s_block_bitmap)
+		return -ENOMEM;
+
+	/*
+	 * Mark all blocks as free initially.
+	 * A full implementation would read the on-disk bitmap here.
+	 * For now we derive free blocks from s_free_blocks in the superblock.
+	 */
+	bitmap_fill(sbi->s_block_bitmap, sbi->s_nblocks);
+
+	/*
+	 * Mark blocks already used (total - free) as allocated.
+	 * We mark from block 0 of the data area upward.
+	 */
+	{
+		unsigned long used = sbi->s_nblocks - sbi->s_free_blocks;
+		unsigned long i;
+
+		for (i = 0; i < used && i < sbi->s_nblocks; i++)
+			clear_bit(i, sbi->s_block_bitmap);
+	}
+
+	pr_info("ftrfs: bitmap initialized (%lu data blocks, %lu free)\n",
+		sbi->s_nblocks, sbi->s_free_blocks);
+
+	return 0;
+}
+
+/*
+ * ftrfs_destroy_bitmap — free the in-memory bitmap
+ * Called from ftrfs_put_super().
+ */
+void ftrfs_destroy_bitmap(struct super_block *sb)
+{
+	struct ftrfs_sb_info *sbi = FTRFS_SB(sb);
+
+	if (sbi->s_block_bitmap) {
+		bitmap_free(sbi->s_block_bitmap);
+		sbi->s_block_bitmap = NULL;
+	}
+}
+
+/*
+ * ftrfs_alloc_block — allocate a free data block
+ * @sb:  superblock
+ *
+ * Returns the absolute block number (>= s_data_start) on success,
+ * or 0 on failure (0 is the superblock, never a valid data block).
+ *
+ * Caller must hold sbi->s_lock.
+ */
+u64 ftrfs_alloc_block(struct super_block *sb)
+{
+	struct ftrfs_sb_info *sbi = FTRFS_SB(sb);
+	unsigned long bit;
+
+	if (!sbi->s_block_bitmap) {
+		pr_err("ftrfs: bitmap not initialized\n");
+		return 0;
+	}
+
+	spin_lock(&sbi->s_lock);
+
+	if (sbi->s_free_blocks == 0) {
+		spin_unlock(&sbi->s_lock);
+		pr_warn("ftrfs: no free blocks\n");
+		return 0;
+	}
+
+	/* Find first free bit (set = free in our convention) */
+	bit = find_first_bit(sbi->s_block_bitmap, sbi->s_nblocks);
+	if (bit >= sbi->s_nblocks) {
+		spin_unlock(&sbi->s_lock);
+		pr_err("ftrfs: bitmap inconsistency (free_blocks=%lu but no free bit)\n",
+		       sbi->s_free_blocks);
+		return 0;
+	}
+
+	/* Mark as used */
+	clear_bit(bit, sbi->s_block_bitmap);
+	sbi->s_free_blocks--;
+
+	/* Update on-disk superblock counter */
+	sbi->s_ftrfs_sb->s_free_blocks = cpu_to_le64(sbi->s_free_blocks);
+	mark_buffer_dirty(sbi->s_sbh);
+
+	spin_unlock(&sbi->s_lock);
+
+	/* Return absolute block number */
+	return (u64)(sbi->s_data_start + bit);
+}
+
+/*
+ * ftrfs_free_block — release a data block back to the free pool
+ * @sb:    superblock
+ * @block: absolute block number to free
+ */
+void ftrfs_free_block(struct super_block *sb, u64 block)
+{
+	struct ftrfs_sb_info *sbi = FTRFS_SB(sb);
+	unsigned long bit;
+
+	if (block < sbi->s_data_start) {
+		pr_err("ftrfs: attempt to free non-data block %llu\n", block);
+		return;
+	}
+
+	bit = (unsigned long)(block - sbi->s_data_start);
+
+	if (bit >= sbi->s_nblocks) {
+		pr_err("ftrfs: block %llu out of range\n", block);
+		return;
+	}
+
+	spin_lock(&sbi->s_lock);
+
+	if (test_bit(bit, sbi->s_block_bitmap)) {
+		pr_warn("ftrfs: double free of block %llu\n", block);
+		spin_unlock(&sbi->s_lock);
+		return;
+	}
+
+	set_bit(bit, sbi->s_block_bitmap);
+	sbi->s_free_blocks++;
+
+	/* Update on-disk superblock counter */
+	sbi->s_ftrfs_sb->s_free_blocks = cpu_to_le64(sbi->s_free_blocks);
+	mark_buffer_dirty(sbi->s_sbh);
+
+	spin_unlock(&sbi->s_lock);
+}
+
+/*
+ * ftrfs_alloc_inode_num — allocate a free inode number
+ * @sb: superblock
+ *
+ * Returns inode number >= 2 on success (1 = root, reserved),
+ * or 0 on failure.
+ *
+ * Simple linear scan of the inode table for a free slot.
+ * A full implementation uses an inode bitmap block.
+ */
+u64 ftrfs_alloc_inode_num(struct super_block *sb)
+{
+	struct ftrfs_sb_info    *sbi = FTRFS_SB(sb);
+	struct ftrfs_inode      *raw;
+	struct buffer_head      *bh;
+	unsigned long            inodes_per_block;
+	unsigned long            inode_table_blk;
+	unsigned long            total_inodes;
+	unsigned long            block, i;
+	u64                      ino = 0;
+
+	inodes_per_block = FTRFS_BLOCK_SIZE / sizeof(struct ftrfs_inode);
+	inode_table_blk  = le64_to_cpu(sbi->s_ftrfs_sb->s_inode_table_blk);
+	total_inodes     = le64_to_cpu(sbi->s_ftrfs_sb->s_inode_count);
+
+	spin_lock(&sbi->s_lock);
+
+	if (sbi->s_free_inodes == 0) {
+		spin_unlock(&sbi->s_lock);
+		return 0;
+	}
+
+	/* Scan inode table blocks looking for a free inode (i_mode == 0) */
+	for (block = 0; block * inodes_per_block < total_inodes; block++) {
+		bh = sb_bread(sb, inode_table_blk + block);
+		if (!bh)
+			continue;
+
+		raw = (struct ftrfs_inode *)bh->b_data;
+
+		for (i = 0; i < inodes_per_block; i++) {
+			unsigned long ino_num = block * inodes_per_block + i + 1;
+
+			if (ino_num > total_inodes)
+				break;
+
+			/* inode 1 = root, always reserved */
+			if (ino_num == 1)
+				continue;
+
+			if (le16_to_cpu(raw[i].i_mode) == 0) {
+				/* Found a free inode slot */
+				ino = (u64)ino_num;
+				sbi->s_free_inodes--;
+				sbi->s_ftrfs_sb->s_free_inodes =
+					cpu_to_le64(sbi->s_free_inodes);
+				mark_buffer_dirty(sbi->s_sbh);
+				brelse(bh);
+				goto found;
+			}
+		}
+		brelse(bh);
+	}
+
+found:
+	spin_unlock(&sbi->s_lock);
+	return ino;
+}
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC PATCH 07/10] ftrfs: add filename and directory entry operations
  2026-04-13 14:23 [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem Aurelien DESBRIERES
                   ` (5 preceding siblings ...)
  2026-04-13 14:23 ` [RFC PATCH 06/10] ftrfs: add block and inode allocator Aurelien DESBRIERES
@ 2026-04-13 14:23 ` Aurelien DESBRIERES
  2026-04-13 14:23 ` [RFC PATCH 08/10] ftrfs: add CRC32 checksumming and Reed-Solomon FEC skeleton Aurelien DESBRIERES
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 26+ messages in thread
From: Aurelien DESBRIERES @ 2026-04-13 14:23 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel, viro, brauner, aurelien

From: Aurélien DESBRIERES <aurelien@hackers.camp>

Implement VFS inode_operations for directories and write path:

- ftrfs_create(): allocate inode, write to disk, add dir entry
- ftrfs_mkdir(): create directory with . and .. entries
- ftrfs_unlink(): remove directory entry, decrement link count
- ftrfs_rmdir(): remove empty directory
- ftrfs_link(): create hard link
- ftrfs_write_inode(): VFS super_op, persist inode via sb_bread/
  mark_buffer_dirty with CRC32 update
- ftrfs_new_inode(): allocate and initialize a new VFS inode

ftrfs_mkdir() returns struct dentry * as required by kernel 7.0
inode_operations.mkdir API change.

Signed-off-by: Aurélien DESBRIERES <aurelien@hackers.camp>
---
 fs/ftrfs/namei.c | 428 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 428 insertions(+)
 create mode 100644 fs/ftrfs/namei.c

diff --git a/fs/ftrfs/namei.c b/fs/ftrfs/namei.c
new file mode 100644
index 000000000..a8c1f79eb
--- /dev/null
+++ b/fs/ftrfs/namei.c
@@ -0,0 +1,428 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * FTRFS — Filename / directory entry operations
+ * Author: Aurélien DESBRIERES <aurelien@hackers.camp>
+ *
+ * Implements: create, mkdir, unlink, rmdir, link, rename
+ */
+
+#include <linux/fs.h>
+#include <linux/buffer_head.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include "ftrfs.h"
+
+/* ------------------------------------------------------------------ */
+/* Helper: write a raw ftrfs_inode to disk                             */
+/* ------------------------------------------------------------------ */
+
+static int ftrfs_write_inode_raw(struct inode *inode)
+{
+	struct super_block      *sb  = inode->i_sb;
+	struct ftrfs_sb_info    *sbi = FTRFS_SB(sb);
+	struct ftrfs_inode_info *fi  = FTRFS_I(inode);
+	struct ftrfs_inode      *raw;
+	struct buffer_head      *bh;
+	unsigned long            inodes_per_block;
+	unsigned long            block, offset;
+
+	inodes_per_block = FTRFS_BLOCK_SIZE / sizeof(struct ftrfs_inode);
+	block  = le64_to_cpu(sbi->s_ftrfs_sb->s_inode_table_blk)
+		 + (inode->i_ino - 1) / inodes_per_block;
+	offset = (inode->i_ino - 1) % inodes_per_block;
+
+	bh = sb_bread(sb, block);
+	if (!bh)
+		return -EIO;
+
+	raw = (struct ftrfs_inode *)bh->b_data + offset;
+
+	raw->i_mode   = cpu_to_le16(inode->i_mode);
+	raw->i_uid    = cpu_to_le16(i_uid_read(inode));
+	raw->i_gid    = cpu_to_le16(i_gid_read(inode));
+	raw->i_nlink  = cpu_to_le16(inode->i_nlink);
+	raw->i_size   = cpu_to_le64(inode->i_size);
+	raw->i_blocks = cpu_to_le32(inode->i_blocks);
+	raw->i_atime  = cpu_to_le64(inode_get_atime_sec(inode) * NSEC_PER_SEC
+				     + inode_get_atime_nsec(inode));
+	raw->i_mtime  = cpu_to_le64(inode_get_mtime_sec(inode) * NSEC_PER_SEC
+				     + inode_get_mtime_nsec(inode));
+	raw->i_ctime  = cpu_to_le64(inode_get_ctime_sec(inode) * NSEC_PER_SEC
+				     + inode_get_ctime_nsec(inode));
+	raw->i_flags  = cpu_to_le32(fi->i_flags);
+
+	memcpy(raw->i_direct, fi->i_direct, sizeof(fi->i_direct));
+	raw->i_indirect  = fi->i_indirect;
+	raw->i_dindirect = fi->i_dindirect;
+
+	raw->i_crc32 = ftrfs_crc32(raw,
+				    offsetof(struct ftrfs_inode, i_crc32));
+
+	mark_buffer_dirty(bh);
+	brelse(bh);
+
+	return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* Helper: add a directory entry to a directory inode                  */
+/* ------------------------------------------------------------------ */
+
+static int ftrfs_add_dirent(struct inode *dir, const struct qstr *name,
+			    u64 ino, unsigned int file_type)
+{
+	struct super_block      *sb = dir->i_sb;
+	struct ftrfs_inode_info *fi = FTRFS_I(dir);
+	struct ftrfs_dir_entry  *de;
+	struct buffer_head      *bh;
+	unsigned int             offset;
+	u64                      block_no;
+	int                      i;
+
+	/* Look for space in existing direct blocks */
+	for (i = 0; i < FTRFS_DIRECT_BLOCKS; i++) {
+		block_no = le64_to_cpu(fi->i_direct[i]);
+		if (!block_no)
+			break;
+
+		bh = sb_bread(sb, block_no);
+		if (!bh)
+			return -EIO;
+
+		offset = 0;
+		while (offset + sizeof(*de) <= FTRFS_BLOCK_SIZE) {
+			de = (struct ftrfs_dir_entry *)(bh->b_data + offset);
+
+			/* Free slot: ino == 0 */
+			if (!de->d_ino) {
+				de->d_ino       = cpu_to_le64(ino);
+				de->d_name_len  = name->len;
+				de->d_file_type = file_type;
+				de->d_rec_len   = cpu_to_le16(
+					sizeof(struct ftrfs_dir_entry));
+				memcpy(de->d_name, name->name, name->len);
+				de->d_name[name->len] = '\0';
+				mark_buffer_dirty(bh);
+				brelse(bh);
+				inode_set_mtime_to_ts(dir,
+					current_time(dir));
+				mark_inode_dirty(dir);
+				return 0;
+			}
+			offset += le16_to_cpu(de->d_rec_len);
+			if (!de->d_rec_len)
+				break;
+		}
+		brelse(bh);
+	}
+
+	/* Need a new block */
+	if (i >= FTRFS_DIRECT_BLOCKS)
+		return -ENOSPC;
+
+	block_no = ftrfs_alloc_block(sb);
+	if (!block_no)
+		return -ENOSPC;
+
+	bh = sb_bread(sb, block_no);
+	if (!bh) {
+		ftrfs_free_block(sb, block_no);
+		return -EIO;
+	}
+
+	memset(bh->b_data, 0, FTRFS_BLOCK_SIZE);
+
+	de = (struct ftrfs_dir_entry *)bh->b_data;
+	de->d_ino       = cpu_to_le64(ino);
+	de->d_name_len  = name->len;
+	de->d_file_type = file_type;
+	de->d_rec_len   = cpu_to_le16(sizeof(struct ftrfs_dir_entry));
+	memcpy(de->d_name, name->name, name->len);
+	de->d_name[name->len] = '\0';
+
+	mark_buffer_dirty(bh);
+	brelse(bh);
+
+	fi->i_direct[i] = cpu_to_le64(block_no);
+	dir->i_size += FTRFS_BLOCK_SIZE;
+	dir->i_blocks++;
+	inode_set_mtime_to_ts(dir, current_time(dir));
+	mark_inode_dirty(dir);
+
+	return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* Helper: remove a directory entry from a directory                   */
+/* ------------------------------------------------------------------ */
+
+static int ftrfs_del_dirent(struct inode *dir, const struct qstr *name)
+{
+	struct super_block      *sb = dir->i_sb;
+	struct ftrfs_inode_info *fi = FTRFS_I(dir);
+	struct ftrfs_dir_entry  *de;
+	struct buffer_head      *bh;
+	unsigned int             offset;
+	u64                      block_no;
+	int                      i;
+
+	for (i = 0; i < FTRFS_DIRECT_BLOCKS; i++) {
+		block_no = le64_to_cpu(fi->i_direct[i]);
+		if (!block_no)
+			break;
+
+		bh = sb_bread(sb, block_no);
+		if (!bh)
+			return -EIO;
+
+		offset = 0;
+		while (offset + sizeof(*de) <= FTRFS_BLOCK_SIZE) {
+			de = (struct ftrfs_dir_entry *)(bh->b_data + offset);
+
+			if (de->d_ino &&
+			    de->d_name_len == name->len &&
+			    !memcmp(de->d_name, name->name, name->len)) {
+				/* Zero out the entry (mark as free) */
+				memset(de, 0, sizeof(*de));
+				mark_buffer_dirty(bh);
+				brelse(bh);
+				inode_set_mtime_to_ts(dir,
+					current_time(dir));
+				mark_inode_dirty(dir);
+				return 0;
+			}
+
+			if (!de->d_rec_len)
+				break;
+			offset += le16_to_cpu(de->d_rec_len);
+		}
+		brelse(bh);
+	}
+
+	return -ENOENT;
+}
+
+/* ------------------------------------------------------------------ */
+/* Helper: allocate and initialize a new VFS inode                     */
+/* ------------------------------------------------------------------ */
+
+struct inode *ftrfs_new_inode(struct inode *dir, umode_t mode)
+{
+	struct super_block   *sb = dir->i_sb;
+	struct inode         *inode;
+	struct ftrfs_inode_info *fi;
+	u64                   ino;
+
+	ino = ftrfs_alloc_inode_num(sb);
+	if (!ino)
+		return ERR_PTR(-ENOSPC);
+
+	inode = new_inode(sb);
+	if (!inode)
+		return ERR_PTR(-ENOMEM);
+
+	inode_init_owner(&nop_mnt_idmap, inode, dir, mode);
+	inode->i_ino    = ino;
+	inode->i_blocks = 0;
+	inode->i_size   = 0;
+	inode_set_atime_to_ts(inode, current_time(inode));
+	inode_set_mtime_to_ts(inode, current_time(inode));
+	inode_set_ctime_to_ts(inode, current_time(inode));
+
+	fi = FTRFS_I(inode);
+	memset(fi->i_direct, 0, sizeof(fi->i_direct));
+	fi->i_indirect  = 0;
+	fi->i_dindirect = 0;
+	fi->i_flags     = 0;
+
+	if (S_ISDIR(mode)) {
+		inode->i_op  = &ftrfs_dir_inode_operations;
+		inode->i_fop = &ftrfs_dir_operations;
+		set_nlink(inode, 2);
+	} else {
+		inode->i_op  = &ftrfs_file_inode_operations;
+		inode->i_fop = &ftrfs_file_operations;
+		set_nlink(inode, 1);
+	}
+
+	insert_inode_hash(inode);
+	mark_inode_dirty(inode);
+	return ERR_CAST(inode);
+}
+
+/* ------------------------------------------------------------------ */
+/* create — create a regular file                                       */
+/* ------------------------------------------------------------------ */
+
+static int ftrfs_create(struct mnt_idmap *idmap, struct inode *dir,
+			struct dentry *dentry, umode_t mode, bool excl)
+{
+	struct inode *inode;
+	int           ret;
+
+	inode = ftrfs_new_inode(dir, mode | S_IFREG);
+	if (IS_ERR(inode))
+		return PTR_ERR(inode);
+
+	ret = ftrfs_write_inode_raw(inode);
+	if (ret)
+		goto out_iput;
+
+	ret = ftrfs_add_dirent(dir, &dentry->d_name, inode->i_ino, 1 /* DT_REG */);
+	if (ret)
+		goto out_iput;
+
+	ret = ftrfs_write_inode_raw(dir);
+	if (ret)
+		goto out_iput;
+
+	d_instantiate(dentry, inode);
+	return 0;
+
+out_iput:
+	iput(inode);
+	return ret;
+}
+
+/* ------------------------------------------------------------------ */
+/* mkdir — create a directory                                          */
+/* ------------------------------------------------------------------ */
+
+static struct dentry *ftrfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
+				  struct dentry *dentry, umode_t mode)
+{
+	struct inode *inode;
+	int           ret;
+
+	inode_inc_link_count(dir);
+
+	inode = ftrfs_new_inode(dir, mode | S_IFDIR);
+	if (IS_ERR(inode)) {
+		inode_dec_link_count(dir);
+		return ERR_CAST(inode);
+	}
+
+	/* Add . and .. entries */
+	ret = ftrfs_add_dirent(inode, &(struct qstr)QSTR_INIT(".", 1),
+			       inode->i_ino, 4 /* DT_DIR */);
+	if (ret)
+		goto out_fail;
+
+	ret = ftrfs_add_dirent(inode, &(struct qstr)QSTR_INIT("..", 2),
+			       dir->i_ino, 4 /* DT_DIR */);
+	if (ret)
+		goto out_fail;
+
+	ret = ftrfs_write_inode_raw(inode);
+	if (ret)
+		goto out_fail;
+
+	ret = ftrfs_add_dirent(dir, &dentry->d_name, inode->i_ino,
+			       4 /* DT_DIR */);
+	if (ret)
+		goto out_fail;
+
+	ret = ftrfs_write_inode_raw(dir);
+	if (ret)
+		goto out_fail;
+
+	d_instantiate(dentry, inode);
+	return NULL;
+
+out_fail:
+	inode_dec_link_count(inode);
+	inode_dec_link_count(inode);
+	iput(inode);
+	inode_dec_link_count(dir);
+	return ERR_PTR(ret);
+}
+
+/* ------------------------------------------------------------------ */
+/* unlink — remove a file                                              */
+/* ------------------------------------------------------------------ */
+
+static int ftrfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+	struct inode *inode = d_inode(dentry);
+	int           ret;
+
+	ret = ftrfs_del_dirent(dir, &dentry->d_name);
+	if (ret)
+		return ret;
+
+	inode_set_ctime_to_ts(inode, current_time(inode));
+	inode_dec_link_count(inode);
+	ftrfs_write_inode_raw(dir);
+	return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* rmdir — remove an empty directory                                   */
+/* ------------------------------------------------------------------ */
+
+static int ftrfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	struct inode *inode = d_inode(dentry);
+	int           ret;
+
+	if (inode->i_nlink > 2)
+		return -ENOTEMPTY;
+
+	ret = ftrfs_del_dirent(dir, &dentry->d_name);
+	if (ret)
+		return ret;
+
+	inode_dec_link_count(inode);
+	inode_dec_link_count(inode);
+	inode_dec_link_count(dir);
+	ftrfs_write_inode_raw(dir);
+	return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* link — create a hard link                                           */
+/* ------------------------------------------------------------------ */
+
+static int ftrfs_link(struct dentry *old_dentry, struct inode *dir,
+		      struct dentry *dentry)
+{
+	struct inode *inode = d_inode(old_dentry);
+	int           ret;
+
+	inode_set_ctime_to_ts(inode, current_time(inode));
+	inode_inc_link_count(inode);
+
+	ret = ftrfs_add_dirent(dir, &dentry->d_name, inode->i_ino, 1);
+	if (ret) {
+		inode_dec_link_count(inode);
+		return ret;
+	}
+
+	ftrfs_write_inode_raw(inode);
+	ftrfs_write_inode_raw(dir);
+	d_instantiate(dentry, inode);
+	ihold(inode);
+	return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* write_inode — VFS super_op: persist inode to disk                  */
+/* ------------------------------------------------------------------ */
+
+int ftrfs_write_inode(struct inode *inode, struct writeback_control *wbc)
+{
+	return ftrfs_write_inode_raw(inode);
+}
+
+/* ------------------------------------------------------------------ */
+/* dir inode_operations — exported                                     */
+/* ------------------------------------------------------------------ */
+
+const struct inode_operations ftrfs_dir_inode_operations = {
+	.lookup  = ftrfs_lookup,
+	.create  = ftrfs_create,
+	.mkdir   = ftrfs_mkdir,
+	.unlink  = ftrfs_unlink,
+	.rmdir   = ftrfs_rmdir,
+	.link    = ftrfs_link,
+};
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC PATCH 08/10] ftrfs: add CRC32 checksumming and Reed-Solomon FEC skeleton
  2026-04-13 14:23 [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem Aurelien DESBRIERES
                   ` (6 preceding siblings ...)
  2026-04-13 14:23 ` [RFC PATCH 07/10] ftrfs: add filename and directory entry operations Aurelien DESBRIERES
@ 2026-04-13 14:23 ` Aurelien DESBRIERES
  2026-04-13 14:23 ` [RFC PATCH 09/10] ftrfs: add Kconfig, Makefile and fs/ tree integration Aurelien DESBRIERES
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 26+ messages in thread
From: Aurelien DESBRIERES @ 2026-04-13 14:23 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel, viro, brauner, aurelien

From: Aurélien DESBRIERES <aurelien@hackers.camp>

Implement data integrity layer:

- ftrfs_crc32(): CRC32 wrapper via kernel crc32_le(), used for
  per-inode and per-superblock checksums
- init_gf_tables(): initialize GF(2^8) Galois Field lookup tables
  (primitive polynomial 0x1d) for Reed-Solomon arithmetic
- gf_mul(): GF(2^8) multiplication via log/exp tables
- ftrfs_rs_encode(): systematic Reed-Solomon encoder over
  FTRFS_SUBBLOCK_DATA bytes with FTRFS_RS_PARITY check symbols

The RS encoder operates on sub-blocks within each 4096-byte data
block. Decoding (error correction) is not yet implemented.

Signed-off-by: Aurélien DESBRIERES <aurelien@hackers.camp>
---
 fs/ftrfs/edac.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 84 insertions(+)
 create mode 100644 fs/ftrfs/edac.c

diff --git a/fs/ftrfs/edac.c b/fs/ftrfs/edac.c
new file mode 100644
index 000000000..ebe676c98
--- /dev/null
+++ b/fs/ftrfs/edac.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * FTRFS — EDAC layer: CRC32 + Reed-Solomon FEC
+ * Author: roastercode - Aurelien DESBRIERES <aurelien@hackers.camp>
+ */
+
+#include <linux/kernel.h>
+#include <linux/crc32.h>
+#include <linux/slab.h>
+#include "ftrfs.h"
+
+/* Reed-Solomon FEC context */
+static uint8_t gf_exp[256];
+static uint8_t gf_log[256];
+static bool rs_initialized;
+
+/* Initialize Galois Field tables */
+static void init_gf_tables(void)
+{
+	uint8_t x = 1;
+
+	for (int i = 0; i < 255; i++) {
+		gf_exp[i] = x;
+		gf_log[x] = i;
+		x = (x << 1) ^ ((x & 0x80) ? 0x1d : 0);
+	}
+	gf_exp[255] = gf_exp[0];
+	rs_initialized = true;
+}
+
+/* Galois Field multiplication */
+static uint8_t gf_mul(uint8_t a, uint8_t b)
+{
+	if (a == 0 || b == 0)
+		return 0;
+	return gf_exp[(gf_log[a] + gf_log[b]) % 255];
+}
+
+/* Reed-Solomon encoding */
+int ftrfs_rs_encode(uint8_t *data, uint8_t *parity)
+{
+	uint8_t msg[FTRFS_SUBBLOCK_DATA + FTRFS_RS_PARITY];
+
+	if (!rs_initialized)
+		init_gf_tables();
+
+	memset(msg, 0, sizeof(msg));
+	memcpy(msg, data, FTRFS_SUBBLOCK_DATA);
+
+	for (int i = 0; i < FTRFS_SUBBLOCK_DATA; i++) {
+		uint8_t feedback = gf_mul(msg[i], gf_exp[FTRFS_RS_PARITY]);
+
+		if (feedback != 0) {
+			for (int j = 1; j <= FTRFS_RS_PARITY; j++)
+				msg[FTRFS_SUBBLOCK_DATA + j - 1] ^= gf_mul(msg[i], gf_exp[j]);
+		}
+	}
+
+	memcpy(parity, msg + FTRFS_SUBBLOCK_DATA, FTRFS_RS_PARITY);
+	return 0;
+}
+
+/* Reed-Solomon decoding (simplified for now) */
+int ftrfs_rs_decode(uint8_t *data, uint8_t *parity)
+{
+	if (!rs_initialized)
+		init_gf_tables();
+
+	/* For now, assume no errors (full decoding to be implemented) */
+	return 0;
+}
+
+/*
+ * ftrfs_crc32 - compute CRC32 checksum
+ * @buf: data buffer
+ * @len: length in bytes
+ *
+ * Returns CRC32 checksum. Uses kernel's hardware-accelerated CRC32
+ * (same as ext4/btrfs).
+ */
+__u32 ftrfs_crc32(const void *buf, size_t len)
+{
+	return crc32_le(0xFFFFFFFF, buf, len) ^ 0xFFFFFFFF;
+}
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC PATCH 09/10] ftrfs: add Kconfig, Makefile and fs/ tree integration
  2026-04-13 14:23 [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem Aurelien DESBRIERES
                   ` (7 preceding siblings ...)
  2026-04-13 14:23 ` [RFC PATCH 08/10] ftrfs: add CRC32 checksumming and Reed-Solomon FEC skeleton Aurelien DESBRIERES
@ 2026-04-13 14:23 ` Aurelien DESBRIERES
  2026-04-13 14:23 ` [RFC PATCH 10/10] MAINTAINERS: add entry for FTRFS filesystem Aurelien DESBRIERES
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 26+ messages in thread
From: Aurelien DESBRIERES @ 2026-04-13 14:23 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel, viro, brauner, aurelien

From: Aurélien DESBRIERES <aurelien@hackers.camp>

Kconfig:
- CONFIG_FTRFS_FS: tristate, depends on BLOCK
- selects CRC32, REED_SOLOMON, REED_SOLOMON_ENC8/DEC8
- CONFIG_FTRFS_FS_XATTR: extended attributes (SELinux support)
- CONFIG_FTRFS_FS_SECURITY: security labels

Makefile:
- ftrfs.o composed of super.o, inode.o, dir.o, file.o,
  edac.o, alloc.o, namei.o
- xattr.o conditionally compiled via CONFIG_FTRFS_FS_XATTR

fs/Kconfig: source fs/ftrfs/Kconfig (after ext2)
fs/Makefile: obj-$(CONFIG_FTRFS_FS) += ftrfs/

Signed-off-by: Aurélien DESBRIERES <aurelien@hackers.camp>
---
 fs/ftrfs/Kconfig  | 49 +++++++++++++++++++++++++++++++++++++++++++++++
 fs/ftrfs/Makefile | 46 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 95 insertions(+)
 create mode 100644 fs/ftrfs/Kconfig
 create mode 100644 fs/ftrfs/Makefile

diff --git a/fs/ftrfs/Kconfig b/fs/ftrfs/Kconfig
new file mode 100644
index 000000000..e23fea923
--- /dev/null
+++ b/fs/ftrfs/Kconfig
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# FTRFS filesystem configuration
+#
+
+config FTRFS_FS
+	tristate "FTRFS fault-tolerant radiation-robust filesystem"
+	depends on BLOCK
+	select CRC32
+	select REED_SOLOMON
+	select REED_SOLOMON_ENC8
+	select REED_SOLOMON_DEC8
+	help
+	  FTRFS is a POSIX-compatible filesystem designed for dependable
+	  storage in radiation-intensive environments. It provides:
+
+	    - CRC32 checksumming per block and per inode
+	    - Reed-Solomon forward error correction (FEC)
+	    - EDAC-compatible error tracking
+
+	  Originally described in:
+	  Fuchs, Langer, Trinitis - ARCS 2015, TU Munich.
+	  Targeting embedded Linux on MRAM/NOR flash for space applications.
+
+	  To compile this filesystem support as a module, choose M here.
+	  The module will be called ftrfs.
+
+	  If unsure, say N.
+
+config FTRFS_FS_XATTR
+	bool "FTRFS extended attributes"
+	depends on FTRFS_FS
+	help
+	  Extended attributes are name:value pairs associated with inodes.
+	  They are required for SELinux, POSIX ACLs, and other security
+	  frameworks that store per-file metadata outside the inode.
+	  FTRFS xattrs follow the same namespace model as ext2/ext4.
+
+	  If you are not using SELinux or POSIX ACLs, say N.
+config FTRFS_FS_SECURITY
+	bool "FTRFS Security Labels"
+	depends on FTRFS_FS_XATTR
+	help
+	  Extended attributes are name:value pairs associated with inodes.
+	  They are required for SELinux, POSIX ACLs, and other security
+	  frameworks that store per-file metadata outside the inode.
+	  FTRFS xattrs follow the same namespace model as ext2/ext4.
+
+	  If you are not using SELinux or POSIX ACLs, say N.
diff --git a/fs/ftrfs/Makefile b/fs/ftrfs/Makefile
new file mode 100644
index 000000000..a792286ec
--- /dev/null
+++ b/fs/ftrfs/Makefile
@@ -0,0 +1,46 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# FTRFS — Fault-Tolerant Radiation-Robust Filesystem
+#
+
+obj-$(CONFIG_FTRFS_FS) += ftrfs.o
+
+ftrfs-y := super.o \
+            inode.o \
+            dir.o   \
+            file.o  \
+            edac.o  \
+            alloc.o \
+            namei.o
+
+ftrfs-$(CONFIG_FTRFS_FS_XATTR) += xattr.o
+
+ifneq ($(KERNELRELEASE),)
+else
+
+ifneq ($(KERNEL_SRC),)
+  KERNELDIR := $(KERNEL_SRC)
+else
+  KERNELDIR ?= /lib/modules/$(shell uname -r)/build
+endif
+
+ifneq ($(O),)
+  KBUILD_OUTPUT := O=$(O)
+else
+  KBUILD_OUTPUT :=
+endif
+
+PWD := $(shell pwd)
+
+all:
+	$(MAKE) -C $(KERNELDIR) $(KBUILD_OUTPUT) M=$(PWD) \
+		CONFIG_FTRFS_FS=m CONFIG_FTRFS_FS_XATTR=n CONFIG_FTRFS_FS_SECURITY=n \
+		modules
+
+clean:
+	$(MAKE) -C $(KERNELDIR) $(KBUILD_OUTPUT) M=$(PWD) clean
+
+modules_install:
+	$(MAKE) -C $(KERNELDIR) $(KBUILD_OUTPUT) M=$(PWD) modules_install
+
+endif
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC PATCH 10/10] MAINTAINERS: add entry for FTRFS filesystem
  2026-04-13 14:23 [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem Aurelien DESBRIERES
                   ` (8 preceding siblings ...)
  2026-04-13 14:23 ` [RFC PATCH 09/10] ftrfs: add Kconfig, Makefile and fs/ tree integration Aurelien DESBRIERES
@ 2026-04-13 14:23 ` Aurelien DESBRIERES
  2026-04-13 15:04 ` [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem Pedro Falcato
  2026-04-13 15:06 ` Matthew Wilcox
  11 siblings, 0 replies; 26+ messages in thread
From: Aurelien DESBRIERES @ 2026-04-13 14:23 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel, viro, brauner, aurelien

From: Aurélien DESBRIERES <aurelien@hackers.camp>

Signed-off-by: Aurélien DESBRIERES <aurelien@hackers.camp>
---
 MAINTAINERS | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index d1cc0e12f..f99e1219f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9610,6 +9610,12 @@ S:	Maintained
 F:	drivers/leds/leds-expresswire.c
 F:	include/linux/leds-expresswire.h
 
+FTRFS FILE SYSTEM
+M:	Aurélien DESBRIERES <aurelien@hackers.camp>
+L:	linux-fsdevel@vger.kernel.org
+S:	Maintained
+F:	fs/ftrfs/
+
 EXT2 FILE SYSTEM
 M:	Jan Kara <jack@suse.com>
 L:	linux-ext4@vger.kernel.org
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem
  2026-04-13 14:23 [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem Aurelien DESBRIERES
                   ` (9 preceding siblings ...)
  2026-04-13 14:23 ` [RFC PATCH 10/10] MAINTAINERS: add entry for FTRFS filesystem Aurelien DESBRIERES
@ 2026-04-13 15:04 ` Pedro Falcato
  2026-04-13 18:03   ` Andreas Dilger
  2026-04-14 13:30   ` Aurelien DESBRIERES
  2026-04-13 15:06 ` Matthew Wilcox
  11 siblings, 2 replies; 26+ messages in thread
From: Pedro Falcato @ 2026-04-13 15:04 UTC (permalink / raw)
  To: Aurelien DESBRIERES; +Cc: linux-fsdevel, linux-kernel, viro, brauner

On Mon, Apr 13, 2026 at 04:23:46PM +0200, Aurelien DESBRIERES wrote:
> From: Aurélien DESBRIERES <aurelien@hackers.camp>
> 
> This RFC introduces FTRFS, a new Linux filesystem designed for dependable
> storage in radiation-intensive environments, targeting embedded Linux systems
> operating in space or other harsh conditions.
> 
> FTRFS was originally described in:
> 
>   Fuchs, C.M., Langer, M., Trinitis, C. (2015).
>   FTRFS: A Fault-Tolerant Radiation-Robust Filesystem for Space Use.
>   ARCS 2015, LNCS vol 9017, Springer.
>   https://doi.org/10.1007/978-3-319-16086-3_8
> 
> This implementation is an independent open-source realization of the
> concepts described in that paper, developed for the Linux kernel.

Well, here's the obvious question: do you have a usecase for this?

> 
> == Design ==
> 
> FTRFS provides three layers of data integrity:
> 
>   - CRC32 per block and per inode (hardware-accelerated via crc32_le)
>   - Reed-Solomon FEC (encoder implemented, decoder planned)
>   - EDAC-compatible error tracking
> 
> On-disk layout:
> 
>   Block 0        : superblock (magic 0x46545246, CRC32-protected)
>   Block 1..N     : inode table (128 bytes/inode, CRC32 per inode)
>   Block N+1..end : data blocks (CRC32 + RS FEC per block)
> 
> Inodes use direct addressing (12 direct block pointers) plus single
> and double indirection. Directory entries are fixed-size (268 bytes)
> stored in direct blocks.
> 
> == Current Status ==

Well, as far as I can see, there's no write path, no read path (no
address_space_operations as far as I can see), no rename. Did you test this?

> 
>   - Superblock: mount/umount, CRC32 validation            [done]
>   - Inodes: read with CRC32 verification                  [done]
>   - Directories: readdir, lookup                          [done]
>   - Files: read via generic page cache helpers            [done]
>   - Allocator: in-memory bitmap for blocks and inodes     [done]
>   - Write path: create, mkdir, unlink, rmdir, link        [done]
>   - Reed-Solomon: encoder done, decoder planned           [partial]
>   - xattrs / SELinux                                      [planned]
>   - fsck.ftrfs                                            [planned]
>   - Indirect block support for large files                [planned]
> 
> == Validation ==
> 
> FTRFS has been validated on arm64 (cortex-a57) running Linux 7.0-rc7,
> built with Yocto Styhead (5.1), deployed as a data partition in a
> 3-node Slurm HPC cluster on KVM/QEMU:
> 
>   insmod ftrfs.ko  ->  module loaded
>   mount -t ftrfs /dev/vdb /mnt  ->  success
>   ls /mnt  ->  returns empty directory correctly

https://github.com/kdave/xfstests

> 
> Compile-tested on x86_64 with Linux 7.0 (this series).
> checkpatch.pl: 0 errors on all patches.
> 
> == Feedback Requested ==
> 
> This is an RFC. Feedback is welcome on:
> 
>   1. On-disk format: is the superblock/inode layout reasonable?

The layout itself is super unix-filesystem/ext2 reminiscent, so if something
like this is really needed, I would strongly recommend you perhaps add this
there. One feature (crc32c checksums over several metadata structures) already
exists on ext4.

-- 
Pedro

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem
  2026-04-13 14:23 [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem Aurelien DESBRIERES
                   ` (10 preceding siblings ...)
  2026-04-13 15:04 ` [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem Pedro Falcato
@ 2026-04-13 15:06 ` Matthew Wilcox
  2026-04-13 18:11   ` Darrick J. Wong
  2026-04-14 13:31   ` Aurelien DESBRIERES
  11 siblings, 2 replies; 26+ messages in thread
From: Matthew Wilcox @ 2026-04-13 15:06 UTC (permalink / raw)
  To: Aurelien DESBRIERES; +Cc: linux-fsdevel, linux-kernel, viro, brauner

On Mon, Apr 13, 2026 at 04:23:46PM +0200, Aurelien DESBRIERES wrote:
> FTRFS was originally described in:
> 
>   Fuchs, C.M., Langer, M., Trinitis, C. (2015).
>   FTRFS: A Fault-Tolerant Radiation-Robust Filesystem for Space Use.
>   ARCS 2015, LNCS vol 9017, Springer.
>   https://doi.org/10.1007/978-3-319-16086-3_8

Might be nice to link to a non-paywalled copy of that paper, eg:

https://www.cfuchs.net/chris/publication-list/ARCS2015/FTRFS.pdf

> This implementation is an independent open-source realization of the
> concepts described in that paper, developed for the Linux kernel.

Can I ask why?  Is the original code not available or too ugly?

> On-disk layout:
> 
>   Block 0        : superblock (magic 0x46545246, CRC32-protected)
>   Block 1..N     : inode table (128 bytes/inode, CRC32 per inode)
>   Block N+1..end : data blocks (CRC32 + RS FEC per block)
> 
> Inodes use direct addressing (12 direct block pointers) plus single
> and double indirection. Directory entries are fixed-size (268 bytes)
> stored in direct blocks.

This is very old-school.  That may be appropriate for the intended
use-case, but it ignores about five decades of filesystem research.


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 05/10] ftrfs: add file operations
  2026-04-13 14:23 ` [RFC PATCH 05/10] ftrfs: add file operations Aurelien DESBRIERES
@ 2026-04-13 15:09   ` Matthew Wilcox
       [not found]     ` <CAM=40tU5NppEZ9x07qDVkSxLw6Ga4nVg7sDCqcvhfQ51VbsS9Q@mail.gmail.com>
  0 siblings, 1 reply; 26+ messages in thread
From: Matthew Wilcox @ 2026-04-13 15:09 UTC (permalink / raw)
  To: Aurelien DESBRIERES; +Cc: linux-fsdevel, linux-kernel, viro, brauner

On Mon, Apr 13, 2026 at 04:23:51PM +0200, Aurelien DESBRIERES wrote:
> From: Aurélien DESBRIERES <aurelien@hackers.camp>
> 
> Implement basic file read/write using generic VFS helpers:
> 
> - ftrfs_file_operations: generic_file_read_iter, generic_file_write_iter
> - ftrfs_file_inode_operations: getattr via simple_getattr
> - ftrfs_address_space_operations: readpage via block_read_full_folio,
>   writepage via block_write_full_folio, bmap via generic_block_bmap

... where is this?  I only see file_operations and inode_operations.  I
don't see address_space_operations anywhere.

> Read path delegates to the page cache via generic helpers.
> Write path is wired but depends on the block allocator (alloc.c)
> and write_inode (namei.c) for persistence.
> 
> Signed-off-by: Aurélien DESBRIERES <aurelien@hackers.camp>
> ---
>  fs/ftrfs/file.c | 25 +++++++++++++++++++++++++
>  1 file changed, 25 insertions(+)
>  create mode 100644 fs/ftrfs/file.c
> 
> diff --git a/fs/ftrfs/file.c b/fs/ftrfs/file.c
> new file mode 100644
> index 000000000..ef121359b
> --- /dev/null
> +++ b/fs/ftrfs/file.c
> @@ -0,0 +1,25 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * FTRFS — File operations (skeleton)
> + * Author: roastercode - Aurelien DESBRIERES <aurelien@hackers.camp>
> + *
> + * NOTE: read/write use generic_file_* for now.
> + * The EDAC/RS layer will intercept at the block I/O level (next iteration).
> + */
> +
> +#include <linux/fs.h>
> +#include <linux/mm.h>
> +#include "ftrfs.h"
> +
> +const struct file_operations ftrfs_file_operations = {
> +	.llseek         = generic_file_llseek,
> +	.read_iter      = generic_file_read_iter,
> +	.write_iter     = generic_file_write_iter,
> +	.mmap           = generic_file_mmap,
> +	.fsync          = generic_file_fsync,
> +	.splice_read    = filemap_splice_read,
> +};
> +
> +const struct inode_operations ftrfs_file_inode_operations = {
> +	.getattr        = simple_getattr,
> +};
> -- 
> 2.52.0
> 
> 

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 01/10] ftrfs: add on-disk format and in-memory data structures
  2026-04-13 14:23 ` [RFC PATCH 01/10] ftrfs: add on-disk format and in-memory data structures Aurelien DESBRIERES
@ 2026-04-13 15:11   ` Darrick J. Wong
  2026-04-13 17:26     ` Aurelien DESBRIERES
  0 siblings, 1 reply; 26+ messages in thread
From: Darrick J. Wong @ 2026-04-13 15:11 UTC (permalink / raw)
  To: Aurelien DESBRIERES; +Cc: linux-fsdevel, linux-kernel, viro, brauner

On Mon, Apr 13, 2026 at 04:23:47PM +0200, Aurelien DESBRIERES wrote:
> From: Aurélien DESBRIERES <aurelien@hackers.camp>
> 
> Add the core header defining FTRFS on-disk layout and in-memory
> VFS structures.
> 
> On-disk layout:
>   Block 0         : superblock (magic 0x46545246, CRC32-protected)
>   Block 1..N      : inode table (128 bytes/inode, CRC32 per inode)
>   Block N+1..end  : data blocks (CRC32 per block, RS FEC planned)
> 
> Structures:
>   ftrfs_super_block  : on-disk superblock
>   ftrfs_inode        : on-disk inode (12 direct + 1 indirect + 1 dindirect)
>   ftrfs_dir_entry    : on-disk directory entry (256-byte name)
>   ftrfs_sb_info      : in-memory superblock info (VFS sb->s_fs_info)
>   ftrfs_inode_info   : in-memory inode (embedded VFS inode)
> 
> FTRFS targets POSIX-compatible block devices (MRAM, NOR flash, eMMC)
> for use in radiation-intensive environments (space applications).
> 
> Signed-off-by: Aurélien DESBRIERES <aurelien@hackers.camp>
> ---
>  fs/ftrfs/ftrfs.h | 168 +++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 168 insertions(+)
>  create mode 100644 fs/ftrfs/ftrfs.h
> 
> diff --git a/fs/ftrfs/ftrfs.h b/fs/ftrfs/ftrfs.h
> new file mode 100644
> index 000000000..82502c9fb
> --- /dev/null
> +++ b/fs/ftrfs/ftrfs.h
> @@ -0,0 +1,168 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * FTRFS — Fault-Tolerant Radiation-Robust Filesystem
> + * Based on: Fuchs, Langer, Trinitis — ARCS 2015
> + *
> + * Author: roastercode - Aurelien DESBRIERES <aurelien@hackers.camp>
> + */
> +
> +#ifndef _FTRFS_H
> +#define _FTRFS_H
> +
> +#include <linux/fs.h>
> +#include <linux/fs_context.h>
> +#include <linux/types.h>
> +
> +/* Magic number: 'FTRF' */
> +#define FTRFS_MAGIC         0x46545246
> +
> +/* Block size: 4096 bytes */
> +#define FTRFS_BLOCK_SIZE    4096
> +#define FTRFS_BLOCK_SHIFT   12
> +
> +/* RS FEC: 16 parity bytes per 239-byte subblock (RS(255,239)) */
> +#define FTRFS_RS_PARITY     16
> +#define FTRFS_SUBBLOCK_DATA 239
> +#define FTRFS_SUBBLOCK_TOTAL (FTRFS_SUBBLOCK_DATA + FTRFS_RS_PARITY)
> +
> +/* Filesystem limits */
> +#define FTRFS_MAX_FILENAME  255
> +#define FTRFS_DIRECT_BLOCKS 12
> +#define FTRFS_INDIRECT_BLOCKS 1
> +#define FTRFS_DINDIRECT_BLOCKS 1
> +
> +/*
> + * On-disk superblock — block 0
> + * Total size: fits in one 4096-byte block
> + */
> +struct ftrfs_super_block {
> +	__le32  s_magic;            /* FTRFS_MAGIC */
> +	__le32  s_block_size;       /* Block size in bytes */
> +	__le64  s_block_count;      /* Total blocks */
> +	__le64  s_free_blocks;      /* Free blocks */
> +	__le64  s_inode_count;      /* Total inodes */
> +	__le64  s_free_inodes;      /* Free inodes */
> +	__le64  s_inode_table_blk;  /* Block where inode table starts */
> +	__le64  s_data_start_blk;   /* First data block */
> +	__le32  s_version;          /* Filesystem version */
> +	__le32  s_flags;            /* Flags */
> +	__le32  s_crc32;            /* CRC32 of this superblock */
> +	__u8    s_uuid[16];         /* UUID */
> +	__u8    s_label[32];        /* Volume label */
> +	__u8    s_pad[3948];        /* Padding to 4096 bytes */
> +} __packed;
> +
> +/*
> + * On-disk inode
> + * Size: 128 bytes
> + */
> +struct ftrfs_inode {
> +	__le16  i_mode;             /* File mode */
> +	__le16  i_uid;              /* Owner UID */
> +	__le16  i_gid;              /* Owner GID */
> +	__le16  i_nlink;            /* Hard link count */

https://www.tomshardware.com/service-providers/web-hosting/friends-gif-cripples-website-discussion-platform-backup-with-377-gigabytes-of-rachels-happy-dance-1-6mb-animation-was-replicated-246-173-times-breaking-filesystem-limit

> +	__le64  i_size;             /* File size in bytes */
> +	__le64  i_atime;            /* Access time (ns) */
> +	__le64  i_mtime;            /* Modification time (ns) */
> +	__le64  i_ctime;            /* Change time (ns) */
> +	__le32  i_blocks;           /* Block count */
> +	__le32  i_flags;            /* Inode flags */
> +	__le64  i_direct[FTRFS_DIRECT_BLOCKS];    /* Direct block pointers */
> +	__le64  i_indirect;         /* Single indirect */
> +	__le64  i_dindirect;        /* Double indirect */

I don't see a block header for indirect blocks, so I guess this
filesystem only supports mapping ... ($block_size / 4) ^ 2 +
($block_size / 4) + 12 blocks?  IOWs, 1049612 of 4KiB blocks, or about
4GB of data?  Why is isize 64-bit then?

--D

> +	__le32  i_crc32;            /* CRC32 of inode */
> +	__u8    i_pad[2];           /* Padding to 128 bytes */
> +} __packed;
> +
> +/* Inode flags */
> +#define FTRFS_INODE_FL_RS_ENABLED   0x0001  /* RS FEC enabled */
> +#define FTRFS_INODE_FL_VERIFIED     0x0002  /* Integrity verified */
> +
> +/*
> + * On-disk directory entry
> + */
> +struct ftrfs_dir_entry {
> +	__le64  d_ino;              /* Inode number */
> +	__le16  d_rec_len;          /* Record length */
> +	__u8    d_name_len;         /* Name length */
> +	__u8    d_file_type;        /* File type */
> +	char    d_name[FTRFS_MAX_FILENAME + 1]; /* Filename */
> +} __packed;
> +
> +/*
> + * In-memory superblock info (stored in sb->s_fs_info)
> + */
> +struct ftrfs_sb_info {
> +	/* Block allocator */
> +	unsigned long    *s_block_bitmap;  /* In-memory free block bitmap */
> +	unsigned long     s_nblocks;       /* Number of data blocks */
> +	unsigned long     s_data_start;    /* First data block number */
> +	struct ftrfs_super_block *s_ftrfs_sb; /* On-disk superblock copy */
> +	struct buffer_head       *s_sbh;      /* Buffer head for superblock */
> +	spinlock_t                s_lock;     /* Superblock lock */
> +	unsigned long             s_free_blocks;
> +	unsigned long             s_free_inodes;
> +};
> +
> +/*
> + * In-memory inode info (embedded in VFS inode via container_of)
> + */
> +struct ftrfs_inode_info {
> +	__le64          i_direct[FTRFS_DIRECT_BLOCKS];
> +	__le64          i_indirect;
> +	__le64          i_dindirect;
> +	__u32           i_flags;
> +	struct inode    vfs_inode;  /* Must be last */
> +};
> +
> +static inline struct ftrfs_inode_info *FTRFS_I(struct inode *inode)
> +{
> +	return container_of(inode, struct ftrfs_inode_info, vfs_inode);
> +}
> +
> +static inline struct ftrfs_sb_info *FTRFS_SB(struct super_block *sb)
> +{
> +	return sb->s_fs_info;
> +}
> +
> +/* Function prototypes */
> +/* super.c */
> +int ftrfs_fill_super(struct super_block *sb, struct fs_context *fc);
> +
> +/* inode.c */
> +struct inode *ftrfs_iget(struct super_block *sb, unsigned long ino);
> +struct inode *ftrfs_new_inode(struct inode *dir, umode_t mode);
> +
> +/* dir.c */
> +extern const struct file_operations ftrfs_dir_operations;
> +extern const struct inode_operations ftrfs_dir_inode_operations;
> +
> +/* file.c */
> +extern const struct file_operations ftrfs_file_operations;
> +extern const struct inode_operations ftrfs_file_inode_operations;
> +
> +/* edac.c */
> +__u32 ftrfs_crc32(const void *buf, size_t len);
> +int ftrfs_rs_encode(uint8_t *data, uint8_t *parity);
> +int ftrfs_rs_decode(uint8_t *data, uint8_t *parity);
> +
> +/* block.c */
> +
> +#endif /* _FTRFS_H */
> +
> +/*
> + */
> +
> +/* alloc.c */
> +int  ftrfs_setup_bitmap(struct super_block *sb);
> +void ftrfs_destroy_bitmap(struct super_block *sb);
> +u64  ftrfs_alloc_block(struct super_block *sb);
> +void ftrfs_free_block(struct super_block *sb, u64 block);
> +u64  ftrfs_alloc_inode_num(struct super_block *sb);
> +
> +/* dir.c */
> +struct dentry *ftrfs_lookup(struct inode *dir, struct dentry *dentry,
> +			    unsigned int flags);
> +
> +/* namei.c */
> +int ftrfs_write_inode(struct inode *inode, struct writeback_control *wbc);
> -- 
> 2.52.0
> 
> 

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 06/10] ftrfs: add block and inode allocator
  2026-04-13 14:23 ` [RFC PATCH 06/10] ftrfs: add block and inode allocator Aurelien DESBRIERES
@ 2026-04-13 15:21   ` Darrick J. Wong
  2026-04-14 14:11     ` Aurelien DESBRIERES
  0 siblings, 1 reply; 26+ messages in thread
From: Darrick J. Wong @ 2026-04-13 15:21 UTC (permalink / raw)
  To: Aurelien DESBRIERES; +Cc: linux-fsdevel, linux-kernel, viro, brauner

On Mon, Apr 13, 2026 at 04:23:52PM +0200, Aurelien DESBRIERES wrote:
> From: Aurélien DESBRIERES <aurelien@hackers.camp>
> 
> Implement in-memory bitmap allocator for blocks and inodes:
> 
> - ftrfs_setup_bitmap(): allocate and initialize the free block bitmap
>   from superblock s_free_blocks count at mount time
> - ftrfs_destroy_bitmap(): release bitmap at umount
> - ftrfs_alloc_block(): find-first-bit allocator, updates on-disk
>   superblock s_free_blocks counter via mark_buffer_dirty()
> - ftrfs_free_block(): return block to pool, double-free detection
> - ftrfs_alloc_inode_num(): linear scan of inode table for a free
>   slot (i_mode == 0), updates s_free_inodes counter
> 
> The bitmap is loaded from the superblock free block count at mount
> and persisted incrementally on each allocation/free. A dedicated
> on-disk bitmap block is planned for a future revision.
> 
> Signed-off-by: Aurélien DESBRIERES <aurelien@hackers.camp>
> ---
>  fs/ftrfs/alloc.c | 251 +++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 251 insertions(+)
>  create mode 100644 fs/ftrfs/alloc.c
> 
> diff --git a/fs/ftrfs/alloc.c b/fs/ftrfs/alloc.c
> new file mode 100644
> index 000000000..753eb67cf
> --- /dev/null
> +++ b/fs/ftrfs/alloc.c
> @@ -0,0 +1,251 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * FTRFS — Block and inode allocator
> + * Author: Aurélien DESBRIERES <aurelien@hackers.camp>
> + *
> + * Simple bitmap allocator. The free block bitmap is stored in-memory
> + * (loaded at mount time) and persisted to disk on each allocation/free.
> + *
> + * Layout assumption (from mkfs.ftrfs):
> + *   Block 0          : superblock
> + *   Block 1..N       : inode table
> + *   Block N+1        : root dir data
> + *   Block N+2..end   : data blocks
> + *
> + * The bitmap itself is stored in the first data block after the inode
> + * table. Each bit represents one data block (1 = free, 0 = used).
> + */
> +
> +#include <linux/fs.h>
> +#include <linux/buffer_head.h>
> +#include <linux/bitmap.h>
> +#include <linux/slab.h>
> +#include "ftrfs.h"
> +
> +/*
> + * ftrfs_setup_bitmap — allocate and initialize the in-memory block bitmap
> + * Called from ftrfs_fill_super() after the superblock is read.
> + *
> + * For the skeleton we use a simple in-memory bitmap initialized from
> + * s_free_blocks. A full implementation would read the on-disk bitmap block.
> + */
> +int ftrfs_setup_bitmap(struct super_block *sb)
> +{
> +	struct ftrfs_sb_info *sbi = FTRFS_SB(sb);
> +	unsigned long total_blocks;
> +	unsigned long data_start;
> +
> +	total_blocks = le64_to_cpu(sbi->s_ftrfs_sb->s_block_count);
> +	data_start   = le64_to_cpu(sbi->s_ftrfs_sb->s_data_start_blk);
> +
> +	if (total_blocks <= data_start) {
> +		pr_err("ftrfs: invalid block layout (total=%lu data_start=%lu)\n",
> +		       total_blocks, data_start);
> +		return -EINVAL;
> +	}
> +
> +	sbi->s_nblocks     = total_blocks - data_start;
> +	sbi->s_data_start  = data_start;
> +
> +	/* Allocate bitmap: one bit per data block */
> +	sbi->s_block_bitmap = bitmap_zalloc(sbi->s_nblocks, GFP_KERNEL);
> +	if (!sbi->s_block_bitmap)
> +		return -ENOMEM;
> +
> +	/*
> +	 * Mark all blocks as free initially.
> +	 * A full implementation would read the on-disk bitmap here.
> +	 * For now we derive free blocks from s_free_blocks in the superblock.
> +	 */
> +	bitmap_fill(sbi->s_block_bitmap, sbi->s_nblocks);
> +
> +	/*
> +	 * Mark blocks already used (total - free) as allocated.
> +	 * We mark from block 0 of the data area upward.
> +	 */
> +	{
> +		unsigned long used = sbi->s_nblocks - sbi->s_free_blocks;
> +		unsigned long i;
> +
> +		for (i = 0; i < used && i < sbi->s_nblocks; i++)
> +			clear_bit(i, sbi->s_block_bitmap);
> +	}
> +
> +	pr_info("ftrfs: bitmap initialized (%lu data blocks, %lu free)\n",
> +		sbi->s_nblocks, sbi->s_free_blocks);
> +
> +	return 0;
> +}
> +
> +/*
> + * ftrfs_destroy_bitmap — free the in-memory bitmap
> + * Called from ftrfs_put_super().
> + */
> +void ftrfs_destroy_bitmap(struct super_block *sb)
> +{
> +	struct ftrfs_sb_info *sbi = FTRFS_SB(sb);
> +
> +	if (sbi->s_block_bitmap) {
> +		bitmap_free(sbi->s_block_bitmap);
> +		sbi->s_block_bitmap = NULL;
> +	}
> +}
> +
> +/*
> + * ftrfs_alloc_block — allocate a free data block
> + * @sb:  superblock
> + *
> + * Returns the absolute block number (>= s_data_start) on success,
> + * or 0 on failure (0 is the superblock, never a valid data block).
> + *
> + * Caller must hold sbi->s_lock.
> + */
> +u64 ftrfs_alloc_block(struct super_block *sb)
> +{
> +	struct ftrfs_sb_info *sbi = FTRFS_SB(sb);
> +	unsigned long bit;
> +
> +	if (!sbi->s_block_bitmap) {
> +		pr_err("ftrfs: bitmap not initialized\n");
> +		return 0;
> +	}
> +
> +	spin_lock(&sbi->s_lock);
> +
> +	if (sbi->s_free_blocks == 0) {
> +		spin_unlock(&sbi->s_lock);
> +		pr_warn("ftrfs: no free blocks\n");
> +		return 0;
> +	}
> +
> +	/* Find first free bit (set = free in our convention) */
> +	bit = find_first_bit(sbi->s_block_bitmap, sbi->s_nblocks);
> +	if (bit >= sbi->s_nblocks) {
> +		spin_unlock(&sbi->s_lock);
> +		pr_err("ftrfs: bitmap inconsistency (free_blocks=%lu but no free bit)\n",
> +		       sbi->s_free_blocks);
> +		return 0;
> +	}
> +
> +	/* Mark as used */
> +	clear_bit(bit, sbi->s_block_bitmap);
> +	sbi->s_free_blocks--;
> +
> +	/* Update on-disk superblock counter */
> +	sbi->s_ftrfs_sb->s_free_blocks = cpu_to_le64(sbi->s_free_blocks);
> +	mark_buffer_dirty(sbi->s_sbh);

No journalling?  Or even COW metadata?  How is this fault tolerant??

--D

> +
> +	spin_unlock(&sbi->s_lock);
> +
> +	/* Return absolute block number */
> +	return (u64)(sbi->s_data_start + bit);
> +}
> +
> +/*
> + * ftrfs_free_block — release a data block back to the free pool
> + * @sb:    superblock
> + * @block: absolute block number to free
> + */
> +void ftrfs_free_block(struct super_block *sb, u64 block)
> +{
> +	struct ftrfs_sb_info *sbi = FTRFS_SB(sb);
> +	unsigned long bit;
> +
> +	if (block < sbi->s_data_start) {
> +		pr_err("ftrfs: attempt to free non-data block %llu\n", block);
> +		return;
> +	}
> +
> +	bit = (unsigned long)(block - sbi->s_data_start);
> +
> +	if (bit >= sbi->s_nblocks) {
> +		pr_err("ftrfs: block %llu out of range\n", block);
> +		return;
> +	}
> +
> +	spin_lock(&sbi->s_lock);
> +
> +	if (test_bit(bit, sbi->s_block_bitmap)) {
> +		pr_warn("ftrfs: double free of block %llu\n", block);
> +		spin_unlock(&sbi->s_lock);
> +		return;
> +	}
> +
> +	set_bit(bit, sbi->s_block_bitmap);
> +	sbi->s_free_blocks++;
> +
> +	/* Update on-disk superblock counter */
> +	sbi->s_ftrfs_sb->s_free_blocks = cpu_to_le64(sbi->s_free_blocks);
> +	mark_buffer_dirty(sbi->s_sbh);
> +
> +	spin_unlock(&sbi->s_lock);
> +}
> +
> +/*
> + * ftrfs_alloc_inode_num — allocate a free inode number
> + * @sb: superblock
> + *
> + * Returns inode number >= 2 on success (1 = root, reserved),
> + * or 0 on failure.
> + *
> + * Simple linear scan of the inode table for a free slot.
> + * A full implementation uses an inode bitmap block.
> + */
> +u64 ftrfs_alloc_inode_num(struct super_block *sb)
> +{
> +	struct ftrfs_sb_info    *sbi = FTRFS_SB(sb);
> +	struct ftrfs_inode      *raw;
> +	struct buffer_head      *bh;
> +	unsigned long            inodes_per_block;
> +	unsigned long            inode_table_blk;
> +	unsigned long            total_inodes;
> +	unsigned long            block, i;
> +	u64                      ino = 0;
> +
> +	inodes_per_block = FTRFS_BLOCK_SIZE / sizeof(struct ftrfs_inode);
> +	inode_table_blk  = le64_to_cpu(sbi->s_ftrfs_sb->s_inode_table_blk);
> +	total_inodes     = le64_to_cpu(sbi->s_ftrfs_sb->s_inode_count);
> +
> +	spin_lock(&sbi->s_lock);
> +
> +	if (sbi->s_free_inodes == 0) {
> +		spin_unlock(&sbi->s_lock);
> +		return 0;
> +	}
> +
> +	/* Scan inode table blocks looking for a free inode (i_mode == 0) */
> +	for (block = 0; block * inodes_per_block < total_inodes; block++) {
> +		bh = sb_bread(sb, inode_table_blk + block);
> +		if (!bh)
> +			continue;
> +
> +		raw = (struct ftrfs_inode *)bh->b_data;
> +
> +		for (i = 0; i < inodes_per_block; i++) {
> +			unsigned long ino_num = block * inodes_per_block + i + 1;
> +
> +			if (ino_num > total_inodes)
> +				break;
> +
> +			/* inode 1 = root, always reserved */
> +			if (ino_num == 1)
> +				continue;
> +
> +			if (le16_to_cpu(raw[i].i_mode) == 0) {
> +				/* Found a free inode slot */
> +				ino = (u64)ino_num;
> +				sbi->s_free_inodes--;
> +				sbi->s_ftrfs_sb->s_free_inodes =
> +					cpu_to_le64(sbi->s_free_inodes);
> +				mark_buffer_dirty(sbi->s_sbh);
> +				brelse(bh);
> +				goto found;
> +			}
> +		}
> +		brelse(bh);
> +	}
> +
> +found:
> +	spin_unlock(&sbi->s_lock);
> +	return ino;
> +}
> -- 
> 2.52.0
> 
> 

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 01/10] ftrfs: add on-disk format and in-memory data structures
  2026-04-13 15:11   ` Darrick J. Wong
@ 2026-04-13 17:26     ` Aurelien DESBRIERES
  0 siblings, 0 replies; 26+ messages in thread
From: Aurelien DESBRIERES @ 2026-04-13 17:26 UTC (permalink / raw)
  To: djwong; +Cc: linux-fsdevel, linux-kernel, viro, brauner

On Mon, Apr 13, 2026 at 05:11:XX PM +0200, Darrick J. Wong wrote:
> I don't see a block header for indirect blocks, so I guess this
> filesystem only supports mapping ($block_size / 4) ^ 2 +
> ($block_size / 4) + 12 blocks? IOWs, 1049612 of 4KiB blocks,
> or about 4GB of data? Why is isize 64-bit then?

Correct. With 4KiB blocks and 8-byte block pointers, the maximum
addressable file size is (512 + 512^2 + 12) * 4096 = ~1GB, not 4GB.
The 64-bit i_size is inconsistent with the block pointer scheme.

For the intended use case (MRAM/NOR flash on embedded space hardware),
files are small and a ~1GB limit is acceptable. i_size will be
corrected to __le32 in v2, or explicitly documented as capped at ~1GB
with a build-time assert.

Thank you for the precise calculation.

Aurelien DESBRIERES <aurelien@hackers.camp>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 05/10] ftrfs: add file operations
       [not found]     ` <CAM=40tU5NppEZ9x07qDVkSxLw6Ga4nVg7sDCqcvhfQ51VbsS9Q@mail.gmail.com>
@ 2026-04-13 17:41       ` Matthew Wilcox
  0 siblings, 0 replies; 26+ messages in thread
From: Matthew Wilcox @ 2026-04-13 17:41 UTC (permalink / raw)
  To: Aurelien DESBRIERES; +Cc: linux-fsdevel, linux-kernel, viro, brauner

On Mon, Apr 13, 2026 at 07:17:30PM +0200, Aurelien DESBRIERES wrote:
> ... where is this? I only see file_operations and inode_operations. I don't
> see address_space_operations anywhere.

I think you need to fix your email client.  Usually on kernel mailing
lists, who said what is indicated by a level of indentation.  There's
no indication here that I asked this question.

> You are correct. address_space_operations was described in the commit
> message but never implemented in the patch. This is a bug in the RFC.
> 
> Will add ftrfs_aops with readfolio and writepage in v2, and wire it into
> ftrfs_iget() via inode->i_mapping->a_ops.

When you're implementing the address_space_operations, I would recommend
using iomap rather than buffer heads.  It's more modern infrastructure,
and there's strong aversion to merging new filesystems that use buffer
heads.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem
  2026-04-13 15:04 ` [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem Pedro Falcato
@ 2026-04-13 18:03   ` Andreas Dilger
  2026-04-14  2:56     ` Gao Xiang
  2026-04-14 14:11     ` Aurelien DESBRIERES
  2026-04-14 13:30   ` Aurelien DESBRIERES
  1 sibling, 2 replies; 26+ messages in thread
From: Andreas Dilger @ 2026-04-13 18:03 UTC (permalink / raw)
  To: Pedro Falcato
  Cc: Aurelien DESBRIERES, linux-fsdevel, linux-kernel, viro, brauner

On Apr 13, 2026, at 09:04, Pedro Falcato <pfalcato@suse.de> wrote:
> 
> On Mon, Apr 13, 2026 at 04:23:46PM +0200, Aurelien DESBRIERES wrote:
>> From: Aurélien DESBRIERES <aurelien@hackers.camp>
>> 
>> This RFC introduces FTRFS, a new Linux filesystem designed for dependable
>> storage in radiation-intensive environments, targeting embedded Linux systems
>> operating in space or other harsh conditions.
>> 
>> FTRFS was originally described in:
>> 
>>  Fuchs, C.M., Langer, M., Trinitis, C. (2015).
>>  FTRFS: A Fault-Tolerant Radiation-Robust Filesystem for Space Use.
>>  ARCS 2015, LNCS vol 9017, Springer.
>>  https://doi.org/10.1007/978-3-319-16086-3_8
>> 
>> This implementation is an independent open-source realization of the
>> concepts described in that paper, developed for the Linux kernel.
> 
> Well, here's the obvious question: do you have a usecase for this?
> 
>> 
>> == Design ==
>> 
>> FTRFS provides three layers of data integrity:
>> 
>>  - CRC32 per block and per inode (hardware-accelerated via crc32_le)
>>  - Reed-Solomon FEC (encoder implemented, decoder planned)
>>  - EDAC-compatible error tracking
>> 
>> On-disk layout:
>> 
>>  Block 0        : superblock (magic 0x46545246, CRC32-protected)
>>  Block 1..N     : inode table (128 bytes/inode, CRC32 per inode)
>>  Block N+1..end : data blocks (CRC32 + RS FEC per block)
>> 
>> Inodes use direct addressing (12 direct block pointers) plus single
>> and double indirection. Directory entries are fixed-size (268 bytes)
>> stored in direct blocks.
>> 
>> 
>> Compile-tested on x86_64 with Linux 7.0 (this series).
>> checkpatch.pl: 0 errors on all patches.
>> 
>> == Feedback Requested ==
>> 
>> This is an RFC. Feedback is welcome on:
>> 
>>  1. On-disk format: is the superblock/inode layout reasonable?
> 
> The layout itself is super unix-filesystem/ext2 reminiscent, so if something
> like this is really needed, I would strongly recommend you perhaps add this
> there. One feature (crc32c checksums over several metadata structures) already
> exists on ext4.

This was my first question as well.  If this was some existing filesystem
that was widely used in satellites or something, it might make sense to
add support for that format (though a FUSE plugin might be better if the
performance is not critical).  But it doesn't necessarily make sense to
implement a greenfield filesystem that doesn't extend the boundaries over
existing filesystems very far.

Modern ext4 has metadata checksums for many years, and fsverity can be used
to add Merkle-tree checksums for file data.  Data redundancy can be handled
by the block layer.

If code size/complexity is a significant issue for new embedded satellite
controllers, then implementing "read-only" support for ext4 could be fairly
straight forward development to put large chunks of the code under a CONFIG
option.

You also have to consider that filesystems are critical components of any
computer, and having bugs in newly-developed filesystem code could be as
fatal to the satellite as the radiation.  The general rule of thumb is that
filesystems take about 10 years to mature.

Cheers, Andreas






^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem
  2026-04-13 15:06 ` Matthew Wilcox
@ 2026-04-13 18:11   ` Darrick J. Wong
  2026-04-14 14:11     ` Aurelien DESBRIERES
  2026-04-14 13:31   ` Aurelien DESBRIERES
  1 sibling, 1 reply; 26+ messages in thread
From: Darrick J. Wong @ 2026-04-13 18:11 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: Aurelien DESBRIERES, linux-fsdevel, linux-kernel, viro, brauner

On Mon, Apr 13, 2026 at 04:06:07PM +0100, Matthew Wilcox wrote:
> On Mon, Apr 13, 2026 at 04:23:46PM +0200, Aurelien DESBRIERES wrote:
> > FTRFS was originally described in:
> > 
> >   Fuchs, C.M., Langer, M., Trinitis, C. (2015).
> >   FTRFS: A Fault-Tolerant Radiation-Robust Filesystem for Space Use.
> >   ARCS 2015, LNCS vol 9017, Springer.
> >   https://doi.org/10.1007/978-3-319-16086-3_8
> 
> Might be nice to link to a non-paywalled copy of that paper, eg:
> 
> https://www.cfuchs.net/chris/publication-list/ARCS2015/FTRFS.pdf
> 
> > This implementation is an independent open-source realization of the
> > concepts described in that paper, developed for the Linux kernel.
> 
> Can I ask why?  Is the original code not available or too ugly?
> 
> > On-disk layout:
> > 
> >   Block 0        : superblock (magic 0x46545246, CRC32-protected)
> >   Block 1..N     : inode table (128 bytes/inode, CRC32 per inode)
> >   Block N+1..end : data blocks (CRC32 + RS FEC per block)
> > 
> > Inodes use direct addressing (12 direct block pointers) plus single
> > and double indirection. Directory entries are fixed-size (268 bytes)
> > stored in direct blocks.
> 
> This is very old-school.  That may be appropriate for the intended
> use-case, but it ignores about five decades of filesystem research.

Why not add FEC to btrfs instead?  Then you can concentrate on getting
the IO paths correct, instead of burning time on ensuring that you've
implemented all the other posix filesystemisms correctly.

--D

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem
  2026-04-13 18:03   ` Andreas Dilger
@ 2026-04-14  2:56     ` Gao Xiang
  2026-04-14 14:11     ` Aurelien DESBRIERES
  1 sibling, 0 replies; 26+ messages in thread
From: Gao Xiang @ 2026-04-14  2:56 UTC (permalink / raw)
  To: Andreas Dilger, Pedro Falcato
  Cc: Aurelien DESBRIERES, linux-fsdevel, linux-kernel, viro, brauner



On 2026/4/14 02:03, Andreas Dilger wrote:
> On Apr 13, 2026, at 09:04, Pedro Falcato <pfalcato@suse.de> wrote:
>>
>> On Mon, Apr 13, 2026 at 04:23:46PM +0200, Aurelien DESBRIERES wrote:
>>> From: Aurélien DESBRIERES <aurelien@hackers.camp>
>>>
>>> This RFC introduces FTRFS, a new Linux filesystem designed for dependable
>>> storage in radiation-intensive environments, targeting embedded Linux systems
>>> operating in space or other harsh conditions.
>>>
>>> FTRFS was originally described in:
>>>
>>>   Fuchs, C.M., Langer, M., Trinitis, C. (2015).
>>>   FTRFS: A Fault-Tolerant Radiation-Robust Filesystem for Space Use.
>>>   ARCS 2015, LNCS vol 9017, Springer.
>>>   https://doi.org/10.1007/978-3-319-16086-3_8
>>>
>>> This implementation is an independent open-source realization of the
>>> concepts described in that paper, developed for the Linux kernel.
>>
>> Well, here's the obvious question: do you have a usecase for this?
>>
>>>
>>> == Design ==
>>>
>>> FTRFS provides three layers of data integrity:
>>>
>>>   - CRC32 per block and per inode (hardware-accelerated via crc32_le)
>>>   - Reed-Solomon FEC (encoder implemented, decoder planned)
>>>   - EDAC-compatible error tracking
>>>
>>> On-disk layout:
>>>
>>>   Block 0        : superblock (magic 0x46545246, CRC32-protected)
>>>   Block 1..N     : inode table (128 bytes/inode, CRC32 per inode)
>>>   Block N+1..end : data blocks (CRC32 + RS FEC per block)
>>>
>>> Inodes use direct addressing (12 direct block pointers) plus single
>>> and double indirection. Directory entries are fixed-size (268 bytes)
>>> stored in direct blocks.
>>>
>>>
>>> Compile-tested on x86_64 with Linux 7.0 (this series).
>>> checkpatch.pl: 0 errors on all patches.
>>>
>>> == Feedback Requested ==
>>>
>>> This is an RFC. Feedback is welcome on:
>>>
>>>   1. On-disk format: is the superblock/inode layout reasonable?
>>
>> The layout itself is super unix-filesystem/ext2 reminiscent, so if something
>> like this is really needed, I would strongly recommend you perhaps add this
>> there. One feature (crc32c checksums over several metadata structures) already
>> exists on ext4.
> 
> This was my first question as well.  If this was some existing filesystem
> that was widely used in satellites or something, it might make sense to
> add support for that format (though a FUSE plugin might be better if the
> performance is not critical).  But it doesn't necessarily make sense to
> implement a greenfield filesystem that doesn't extend the boundaries over
> existing filesystems very far.
> 
> Modern ext4 has metadata checksums for many years, and fsverity can be used
> to add Merkle-tree checksums for file data.  Data redundancy can be handled
> by the block layer.
> 
> If code size/complexity is a significant issue for new embedded satellite
> controllers, then implementing "read-only" support for ext4 could be fairly
> straight forward development to put large chunks of the code under a CONFIG
> option.

According to the patchset, they need read-write support, but
I think the ext2 codebase is more suitable for embedded use
(if they don't need journalling) and to isolate unneeded
features.

However, I don't think an RO Kconfig option is useful for
isolating filesystem complexity concerns, unless the ondisk
layout and related runtime codepath can be separated by
design rather than gathering random RO pieces together:
because in that way, the new RO Kconfig option can only
make the testing and new development worse.

> 
> You also have to consider that filesystems are critical components of any
> computer, and having bugs in newly-developed filesystem code could be as
> fatal to the satellite as the radiation.  The general rule of thumb is that
> filesystems take about 10 years to mature.

Just a wild guess, I'm not sure if it's part of the AI work
(because it seems even there is no page cache for mmap()):

with new AI tools like Claude, it's pretty easy for everyone
to "write" and publish something now and in the future.

Thanks,
Gao Xiang

> 
> Cheers, Andreas
> 
> 
> 
> 
> 


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem
  2026-04-13 15:04 ` [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem Pedro Falcato
  2026-04-13 18:03   ` Andreas Dilger
@ 2026-04-14 13:30   ` Aurelien DESBRIERES
  1 sibling, 0 replies; 26+ messages in thread
From: Aurelien DESBRIERES @ 2026-04-14 13:30 UTC (permalink / raw)
  To: Pedro Falcato; +Cc: linux-fsdevel, linux-kernel, viro, brauner

On Mon, Apr 13, 2026 at 03:04:11PM +0000, Pedro Falcato wrote:
> Well, here's the obvious question: do you have a usecase for this?

The target is embedded Linux systems in radiation-intensive environments
where single-event upsets (SEU) cause silent bit flips in data at rest.
Specifically: nanosatellites using MRAM or NOR flash as primary storage,
with no block layer redundancy (no RAID, no mirroring, single device).

The concrete use case is derived from the MOVE-II CubeSat mission at
TU Munich (Fuchs, Langer, Trinitis — ARCS 2015). The paper documents
measured SEU rates on commercial MRAM in LEO and shows that RS FEC at
the filesystem level is the only mechanism that can recover corrupted
data in place on a single-device system without external redundancy.

The implementation is validated in a real arm64 HPC cluster (Slurm
25.11.4, Yocto Styhead 5.1, kernel 7.0) as a proof of concept for
space-grade embedded Linux deployments:
https://github.com/roastercode/yocto-hardened/tree/arm64-ftrfs

> Well, as far as I can see, there's no write path, no read path (no
> address_space_operations as far as I can see), no rename. Did you
> test this?

v1 was an RFC skeleton. These were addressed in subsequent versions:
- v2: address_space_operations, write path, inode lifecycle fixes
- v3: iomap IO path (replacing buffer_head, per Matthew Wilcox),
  rename, RS FEC decoder, Radiation Event Journal
- v3: xfstests generic/001, 002, 010 equivalent validated on
  qemuarm64 kernel 7.0

> The layout itself is super unix-filesystem/ext2 reminiscent, so if
> something like this is really needed, I would strongly recommend you
> perhaps add this there. One feature (crc32c checksums over several
> metadata structures) already exists on ext4.

ext4 checksums detect corruption but do not correct it. fsverity
detects tampering on read-only data but does not correct it. Neither
provides in-place RS FEC correction on a single-device system.

The certification constraint is also a hard requirement for the target
environment: DO-178C (avionics), ECSS-E-ST-40C (space), and IEC 61508
(nuclear/industrial) require complete code auditability. ext4 at ~100k
lines cannot realistically be certified under these frameworks.
FTRFS targets under 5000 lines of auditable code.

Aurelien DESBRIERES <aurelien@hackers.camp>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem
  2026-04-13 15:06 ` Matthew Wilcox
  2026-04-13 18:11   ` Darrick J. Wong
@ 2026-04-14 13:31   ` Aurelien DESBRIERES
  1 sibling, 0 replies; 26+ messages in thread
From: Aurelien DESBRIERES @ 2026-04-14 13:31 UTC (permalink / raw)
  To: Matthew Wilcox; +Cc: linux-fsdevel, linux-kernel, viro, brauner

On Mon, Apr 13, 2026 at 04:06:07PM +0100, Matthew Wilcox wrote:
> Might be nice to link to a non-paywalled copy of that paper, eg:
> https://www.cfuchs.net/chris/publication-list/ARCS2015/FTRFS.pdf

Done in v2 and v3 cover letters.

> Can I ask why? Is the original code not available or too ugly?

The original FTRFS from the ARCS 2015 paper was a research prototype
targeting a custom RTOS (not Linux). There is no upstream Linux
implementation. This series is an independent open-source realization
of the published design for the mainline Linux kernel.

> This is very old-school. That may be appropriate for the intended
> use-case, but it ignores about five decades of filesystem research.

Intentional. The design constraints are auditability (DO-178C,
ECSS-E-ST-40C, IEC 61508) and minimal code size (target < 5000 lines).
Five decades of filesystem research produced ext4 (~100k lines) and
btrfs (~200k lines), neither of which is certifiable under these
frameworks. The old-school approach is a feature, not a limitation.

The iomap IO path was adopted in v3 as you requested, replacing the
buffer_head based read/write. buffer_head is retained for metadata
IO (inode table, directory blocks) pending further review.

Aurelien DESBRIERES <aurelien@hackers.camp>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 06/10] ftrfs: add block and inode allocator
  2026-04-13 15:21   ` Darrick J. Wong
@ 2026-04-14 14:11     ` Aurelien DESBRIERES
  0 siblings, 0 replies; 26+ messages in thread
From: Aurelien DESBRIERES @ 2026-04-14 14:11 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-fsdevel, linux-kernel, viro, brauner

On Mon, Apr 13, 2026 at 08:21:01AM -0700, Darrick J. Wong wrote:
> No journalling? Or even COW metadata? How is this fault tolerant??

Fair question. The answer is that FTRFS fault tolerance is at the
block data layer via Reed-Solomon FEC, not at the metadata consistency
layer via journalling or COW.

The threat model is radiation-induced single-event upsets (SEU) causing
silent bit flips in data at rest on MRAM or NOR flash. Journalling
protects against crash consistency (power loss mid-write). COW protects
against torn writes. Neither corrects a bit flip that occurred while
the device was idle and powered.

RS FEC corrects up to 8 symbol errors per 255-byte subblock in place,
without requiring a redundant copy. That is the specific gap that
FTRFS addresses. The Radiation Event Journal (added in v3) provides a
persistent log of every correction event in the superblock, giving
operators a map of physical degradation over time.

Journalling for crash consistency is a valid concern for v4. The
current design assumes the target environment (space, avionics) has
well-defined power cycles and controlled shutdown procedures, which
reduces the crash consistency risk relative to general-purpose use.
This will be documented explicitly in v4.

Aurelien DESBRIERES <aurelien@hackers.camp>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem
  2026-04-13 18:11   ` Darrick J. Wong
@ 2026-04-14 14:11     ` Aurelien DESBRIERES
  0 siblings, 0 replies; 26+ messages in thread
From: Aurelien DESBRIERES @ 2026-04-14 14:11 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Matthew Wilcox, linux-fsdevel, linux-kernel, viro, brauner

On Mon, Apr 13, 2026 at 11:11:56AM -0700, Darrick J. Wong wrote:
> Why not add FEC to btrfs instead? Then you can concentrate on getting
> the IO paths correct, instead of burning time on ensuring that you've
> implemented all the other posix filesystemisms correctly.

Adding FEC to btrfs would produce a filesystem that cannot be certified
under DO-178C, ECSS-E-ST-40C, or IEC 61508. btrfs at ~200k lines is
not auditable under these frameworks regardless of what features are
added or removed.

The certification constraint is a hard requirement for the target
environment (space, avionics, nuclear/industrial). It is not a
preference. A smaller, purpose-built filesystem with RS FEC as a
first-class design constraint is the only viable path to certification.

btrfs also carries significant complexity in its COW B-tree allocator,
extent maps, and RAID layer that would need to be analyzed and
certified alongside the FEC addition. The audit surface would be
orders of magnitude larger than a dedicated implementation.

That said, the IO path concern is valid. v3 addresses this by
migrating the data IO path to iomap as you and Matthew Wilcox
requested. buffer_head is retained only for metadata IO (inode
table, directory blocks) pending further review.

Aurelien DESBRIERES <aurelien@hackers.camp>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem
  2026-04-13 18:03   ` Andreas Dilger
  2026-04-14  2:56     ` Gao Xiang
@ 2026-04-14 14:11     ` Aurelien DESBRIERES
  1 sibling, 0 replies; 26+ messages in thread
From: Aurelien DESBRIERES @ 2026-04-14 14:11 UTC (permalink / raw)
  To: Andreas Dilger; +Cc: Pedro Falcato, linux-fsdevel, linux-kernel, viro, brauner

On Mon, Apr 13, 2026 at 12:03:10PM -0600, Andreas Dilger wrote:
> Modern ext4 has metadata checksums for many years, and fsverity can
> be used to add Merkle-tree checksums for file data. Data redundancy
> can be handled by the block layer.

ext4 checksums and fsverity both detect corruption. Neither corrects
it. On a single MRAM or NOR flash device in a nanosatellite, there is
no block layer redundancy available. Detection without correction means
data loss.

RS FEC integrated at the filesystem block level corrects up to 8 symbol
errors per 255-byte subblock in place, on a single device, without
external redundancy. That is the specific gap that FTRFS addresses and
that no existing Linux filesystem provides.

> If code size/complexity is a significant issue for new embedded
> satellite controllers, then implementing "read-only" support for ext4
> could be fairly straight forward development.

Read-only ext4 does not solve the write path requirement. The target
systems require read-write access to MRAM during mission operations
(telemetry, payload data). A read-only filesystem is not sufficient.

> You also have to consider that filesystems are critical components of
> any computer, and having bugs in newly-developed filesystem code could
> be as fatal to the satellite as the radiation. The general rule of
> thumb is that filesystems take about 10 years to mature.

Acknowledged. This is precisely why the design targets under 5000
lines of auditable code, why the on-disk format is deliberately
simple (ext2-reminiscent), and why DO-178C / ECSS-E-ST-40C
certification is an explicit design constraint. A smaller, auditable
codebase reduces the bug surface. The maturity concern is real and
will be addressed through progressive validation - the Yocto arm64
HPC cluster (Slurm 25.11.4, kernel 7.0) is the first step.

Aurelien DESBRIERES <aurelien@hackers.camp>

^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2026-04-14 12:12 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-13 14:23 [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem Aurelien DESBRIERES
2026-04-13 14:23 ` [RFC PATCH 01/10] ftrfs: add on-disk format and in-memory data structures Aurelien DESBRIERES
2026-04-13 15:11   ` Darrick J. Wong
2026-04-13 17:26     ` Aurelien DESBRIERES
2026-04-13 14:23 ` [RFC PATCH 02/10] ftrfs: add superblock operations Aurelien DESBRIERES
2026-04-13 14:23 ` [RFC PATCH 03/10] ftrfs: add inode operations Aurelien DESBRIERES
2026-04-13 14:23 ` [RFC PATCH 04/10] ftrfs: add directory operations Aurelien DESBRIERES
2026-04-13 14:23 ` [RFC PATCH 05/10] ftrfs: add file operations Aurelien DESBRIERES
2026-04-13 15:09   ` Matthew Wilcox
     [not found]     ` <CAM=40tU5NppEZ9x07qDVkSxLw6Ga4nVg7sDCqcvhfQ51VbsS9Q@mail.gmail.com>
2026-04-13 17:41       ` Matthew Wilcox
2026-04-13 14:23 ` [RFC PATCH 06/10] ftrfs: add block and inode allocator Aurelien DESBRIERES
2026-04-13 15:21   ` Darrick J. Wong
2026-04-14 14:11     ` Aurelien DESBRIERES
2026-04-13 14:23 ` [RFC PATCH 07/10] ftrfs: add filename and directory entry operations Aurelien DESBRIERES
2026-04-13 14:23 ` [RFC PATCH 08/10] ftrfs: add CRC32 checksumming and Reed-Solomon FEC skeleton Aurelien DESBRIERES
2026-04-13 14:23 ` [RFC PATCH 09/10] ftrfs: add Kconfig, Makefile and fs/ tree integration Aurelien DESBRIERES
2026-04-13 14:23 ` [RFC PATCH 10/10] MAINTAINERS: add entry for FTRFS filesystem Aurelien DESBRIERES
2026-04-13 15:04 ` [RFC PATCH 0/10] ftrfs: Fault-Tolerant Radiation-Robust Filesystem Pedro Falcato
2026-04-13 18:03   ` Andreas Dilger
2026-04-14  2:56     ` Gao Xiang
2026-04-14 14:11     ` Aurelien DESBRIERES
2026-04-14 13:30   ` Aurelien DESBRIERES
2026-04-13 15:06 ` Matthew Wilcox
2026-04-13 18:11   ` Darrick J. Wong
2026-04-14 14:11     ` Aurelien DESBRIERES
2026-04-14 13:31   ` Aurelien DESBRIERES

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox