From nobody Sat Jun 20 15:19:29 2026 Received: from mail-wm1-f42.google.com (mail-wm1-f42.google.com [209.85.128.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7721B3859D9 for ; Mon, 13 Apr 2026 21:06:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.42 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776114377; cv=none; b=EeybW7cP6FYEYB7/ew3iljldqXd0f2PzRswhYjfaGt9gzob74CzdcCMOq+6i4MOQSt6BpSerZxhIJMavNmgaUEELO6Mdicqa1uCagaE142LrXpLpV6KQDinIG62kq7bk8Oo/Npyke9K/abMpr7VMnuVJU6AxwAIyQLC4ypHbn6I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776114377; c=relaxed/simple; bh=VhjFbMSlOshu11yavoVTfVmUUwRlwLSHPzn80gEY9fo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=ZR4nihKj2iabFIKXj+g+SMr0mbWbk+Bueuh9jO7lDUal8fy066kd5ht5ig5DF6gz7V/Ru+ZMGqI92A452CkXsz92HkEPyGyyfhwE6QW7g7x2J2VxuICrXmVJ6oWzDhMa0uY2K+ABx6ipzm5PcjvoPwUgz/3sS9H6TZrmvfCjiTM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hackers.camp; spf=pass smtp.mailfrom=gmail.com; arc=none smtp.client-ip=209.85.128.42 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hackers.camp Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-wm1-f42.google.com with SMTP id 5b1f17b1804b1-48371104ffdso6494485e9.1 for ; Mon, 13 Apr 2026 14:06:15 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776114374; x=1776719174; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=ky4MuvaYCkkHfYmDAn86zRZn9P/nobrWNgfD4GBihFk=; b=MNQdElzPq0LblBuvweKgD8zANEwtYqd5L9czPQ9U4pYoro/8/OHeHRkVUnOOJ+UB7Y nN6Il5TiPjhqJwvX535JuTcUsbeszgFaZzfSgsu5dsVNTugOZ9stZmfKfTVlTagZkmwo 6/q6knwz/cdpg1AuqX/SXdRqH73bKjVYulwGOO+ctI0CQTjUFqUqE24N87zwYehabU+W mtACbzyAMFyLTmoj3iWpYQO98UjcKpFpnBmKeIil5QRAhoQ+Yoks6B4+LLByJf0Gjcbv jivE/IesTvfZMRNl5grpcw5w9mHzlxAcJEtdG3Gj8GNsqlc5SV/sqApxw4hmxc1Fc891 FZTw== X-Forwarded-Encrypted: i=1; AFNElJ8v5h60RIC1kdnLC8a5NpEKpolx0kk5ZnN6Ghvwo07sTm2jndxXjHzg6OONDaeD83VFdTTbDmZfl5XNSMU=@vger.kernel.org X-Gm-Message-State: AOJu0YzKThvfllhEeOq2Arod8KoSwOTeuakF3JWKStMa8+fGqbTG0n7L +5CU/Gpv2XURgqb2pxelb2esm6FmlB1IuYchfJIJRefKNiqZkYJQKe82 X-Gm-Gg: AeBDiev6wa9CM8ApyXpq8BKAbwhacRb7eVC+dst4ZeOPx4glA4CBcmYLgBiB6ujxj/y H9YBiwY+yqcsYhSkQX21JSIj6zUhL63U8VXF5Zq/ubESAasFCgkgTmwRQUILbBeDHD8hNwXaDFy X5AHie1fJaGQZE6ZtvMEXP29qKFJqPc/xvvuC9yFjeaScnWPVGEJuhNZ4G9IdCRmpJ7K0q56FGd MW62aqSAVSw+2qYfHbWVErLoD+6Tm8OU1e3Wqri1N8qRkw+Ku7x2sGeZ9Yhwqd9I9FXMyLC9SZO jnS9hDmoKXVR5Bj14ObOq/JVr8ZpLaJX6lQa6jVMxTznrHflWWgVF49VWaPAonNljJaah75u27k c8ZNHlSLWZgxZ3nLFD4LveHgrecPp2ZXSRTlkoy6GTkPeOOlSSjh0LjAQlUDnF5yrD63EeRL5If SnrFycgViq8i/M7HQLco46vCRNXSXvnRNxt3PdGE4RQjwNzJRIW40RlUvtbDhEtpgMtVjRo3AYC NOa X-Received: by 2002:a05:600c:4fca:b0:485:f1d6:2b1d with SMTP id 5b1f17b1804b1-488d6658dafmr121717735e9.0.1776114373746; Mon, 13 Apr 2026 14:06:13 -0700 (PDT) Received: from spartian-1.home ([2a01:cb1c:784:2f00:708:2805:7128:7a75]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43d63e5c98fsm35130020f8f.35.2026.04.13.14.06.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 13 Apr 2026 14:06:13 -0700 (PDT) From: Aurelien DESBRIERES To: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Aurelien DESBRIERES , viro@zeniv.linux.org.uk, brauner@kernel.org, willy@infradead.org, djwong@kernel.org, adilger@dilger.ca, pfalcato@suse.de Subject: [PATCH v2 01/11] ftrfs: add on-disk format and in-memory data structures Date: Tue, 14 Apr 2026 01:05:42 +0200 Message-ID: <20260413230601.525400-2-aurelien@hackers.camp> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260413230601.525400-1-aurelien@hackers.camp> References: <20260413230601.525400-1-aurelien@hackers.camp> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable 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: Aurelien DESBRIERES --- 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 =E2=80=94 Fault-Tolerant Radiation-Robust Filesystem + * Based on: Fuchs, Langer, Trinitis =E2=80=94 ARCS 2015 + * + * Author: roastercode - Aurelien DESBRIERES + */ + +#ifndef _FTRFS_H +#define _FTRFS_H + +#include +#include +#include + +/* 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 =E2=80=94 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); --=20 2.52.0 From nobody Sat Jun 20 15:19:29 2026 Received: from mail-wm1-f49.google.com (mail-wm1-f49.google.com [209.85.128.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 548A5392822 for ; Mon, 13 Apr 2026 21:06:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776114384; cv=none; b=hunQlik0cdK8eDSS6dZH+1KhcxqoHaFmGjtoKNB+eWgmDlt7bFIhUk7DAzTlKDerMyWrH5xuWLhR2HrJ21g5hmmpu1NfCgSSrVkBUA6FkCydejntEJJG8QCOJFREutmvq+5jxre46d5U3JvdlODCmKlY2lz4q8lTBZKfrSBvU5c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776114384; c=relaxed/simple; bh=k3ZcmerPCaSP5l5VqDhCeas4xqbTxaeRMl4kGnOUlA8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=coIycTjNuZHlRi0e2U6nMrO0FKudYLIAThZESzEkOrtzZu1HRyVadEEkpBS3IjaMEGUT5T6SxpmD+3xyyDzbI5H0UW5AaIO8XDZI/XJInRHxfILa2Pf/CLVB4os9MEJUzKq8TFM/hi9AVFuagmFD8654hAQyNZfFZfAHwq2IvXQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hackers.camp; spf=pass smtp.mailfrom=gmail.com; arc=none smtp.client-ip=209.85.128.49 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hackers.camp Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-wm1-f49.google.com with SMTP id 5b1f17b1804b1-488965a9ca1so5872775e9.2 for ; Mon, 13 Apr 2026 14:06:22 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776114381; x=1776719181; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=zCysZkMCVzz4vFWVIOblJf4TyrdfU5COK82iM1u8HRI=; b=WwoaAnvNJEcqHQD0dq2UGA4oTHRfgqdGlYPLcS7zatosJZd7gzfKzriI1qPLLrzqLd iKj++v3a7DK++7kzUe6sSDBY49GArePYTSbPQBG8kZ3eMsSQosRmqnCM9wJOuIyQICz2 yBMTmExxruBqAOc4J/bNtkWGJTNjbrXw9RYqS44SPdYXvwotH3/drlp1ynQLoixoOFZH 1aC6sEk6d4igCH4Y5+DSkvnO+DW4EW/HB3xBHrQ8TA8CqisE5bDVt2zkC+SNF8i5VS5X +9UBGs4PZ31xej5cWOVvEvLUN9ge8VFznoo+Q/dJI8KslGaK7YyCnNmsJ1iNpcPnSPLr PHDg== X-Forwarded-Encrypted: i=1; AFNElJ83jb6Dvvx30GllChiQuPiAHq3WnToD58AIUb/0TRoKagWikdPmer4Dzyte5gUF21srWLKO6HKNv1PPhU0=@vger.kernel.org X-Gm-Message-State: AOJu0Yzku9k/fBGLeKruwsQPman6FPYJq2EZl0g3UafzZE39w6JqDfh7 SFnKSuoaT8cNQpnaIxqhTGfDGL2g7uSgkOXXX+U1O574Ih5N1A3nedb3STKxO0JKv8I= X-Gm-Gg: AeBDievBuCOBZT60ZhPe303juvt06M8cP94JP6IczYEiZZValRRYus2M1FFW9mFBoRo 2VGsTULi+cmj1P5GILGxUHSJuDy+8lcprWb7wFHDQ1ZISXGT65qOMkMdmEQ4OKpTQVkeHv5+gTV DNn09xfaOeN/5F1qVi9bfRi0ZI3aNxtKtSuNYGh8jbuiLYvZhe6Qs4yIo15QtSxOhgiRytm/9g0 +LV04LHOUR8vnlBA3p3YWyW3ATee+JfxZ2Z3tXSZZmKI43og3aRxeySxku3RKzduQDSQ4ETwZfX 3T3efXp5B6HYi+WrZ/2V+28Y5utPJdtEJVaG9m5EHpJH9ZjO/fmcOIuvNKVVbkk1W44w7PqFp+z iK1+pBZ8YC9tG4wd9LZhZ2wavz7dH7AlruZGYTJf4hysYt+grNShF2Dl/ZL9VSq7FM0tRP0UiJ4 BYnFFCI9K1ed6l/USIUxPM2MfUgnw0W4oU5w363GWWQ4BUX614/xiFwtGlohu9W+3s8hzyQdyiT TTE X-Received: by 2002:a05:600c:524a:b0:488:926e:3ccd with SMTP id 5b1f17b1804b1-488d7f24014mr106140105e9.4.1776114380582; Mon, 13 Apr 2026 14:06:20 -0700 (PDT) Received: from spartian-1.home ([2a01:cb1c:784:2f00:708:2805:7128:7a75]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43d63e5c98fsm35130020f8f.35.2026.04.13.14.06.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 13 Apr 2026 14:06:20 -0700 (PDT) From: Aurelien DESBRIERES To: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Aurelien DESBRIERES , viro@zeniv.linux.org.uk, brauner@kernel.org, willy@infradead.org, djwong@kernel.org, adilger@dilger.ca, pfalcato@suse.de Subject: [PATCH v2 02/11] ftrfs: add superblock operations Date: Tue, 14 Apr 2026 01:05:43 +0200 Message-ID: <20260413230601.525400-3-aurelien@hackers.camp> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260413230601.525400-1-aurelien@hackers.camp> References: <20260413230601.525400-1-aurelien@hackers.camp> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable 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: Aurelien DESBRIERES --- 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 =E2=80=94 Superblock operations + * Author: roastercode - Aurelien DESBRIERES + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ftrfs.h" + +/* Inode cache (slab allocator) */ +static struct kmem_cache *ftrfs_inode_cachep; + +/* + * alloc_inode =E2=80=94 allocate a new inode with ftrfs_inode_info embedd= ed + */ +static struct inode *ftrfs_alloc_inode(struct super_block *sb) +{ + struct ftrfs_inode_info *fi; + + fi =3D kmem_cache_alloc(ftrfs_inode_cachep, GFP_KERNEL); + if (!fi) + return NULL; + + memset(fi->i_direct, 0, sizeof(fi->i_direct)); + fi->i_indirect =3D 0; + fi->i_dindirect =3D 0; + fi->i_flags =3D 0; + + return &fi->vfs_inode; +} + +/* + * free_inode =E2=80=94 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 =E2=80=94 filesystem statistics + */ +static int ftrfs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + struct super_block *sb =3D dentry->d_sb; + struct ftrfs_sb_info *sbi =3D FTRFS_SB(sb); + + buf->f_type =3D FTRFS_MAGIC; + buf->f_bsize =3D sb->s_blocksize; + buf->f_blocks =3D le64_to_cpu(sbi->s_ftrfs_sb->s_block_count); + buf->f_bfree =3D sbi->s_free_blocks; + buf->f_bavail =3D sbi->s_free_blocks; + buf->f_files =3D le64_to_cpu(sbi->s_ftrfs_sb->s_inode_count); + buf->f_ffree =3D sbi->s_free_inodes; + buf->f_namelen =3D FTRFS_MAX_FILENAME; + + return 0; +} + +/* + * put_super =E2=80=94 release superblock resources + */ +static void ftrfs_put_super(struct super_block *sb) +{ + struct ftrfs_sb_info *sbi =3D FTRFS_SB(sb); + + if (sbi) { + ftrfs_destroy_bitmap(sb); + brelse(sbi->s_sbh); + kfree(sbi->s_ftrfs_sb); + kfree(sbi); + sb->s_fs_info =3D NULL; + } +} + +static const struct super_operations ftrfs_super_ops =3D { + .alloc_inode =3D ftrfs_alloc_inode, + .free_inode =3D ftrfs_free_inode, + .put_super =3D ftrfs_put_super, + .write_inode =3D ftrfs_write_inode, + .statfs =3D ftrfs_statfs, +}; + +/* + * ftrfs_fill_super =E2=80=94 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 =3D -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 =E2=80=94 superblock */ + bh =3D sb_bread(sb, 0); + if (!bh) { + errorf(fc, "ftrfs: unable to read superblock"); + return -EIO; + } + + fsb =3D (struct ftrfs_super_block *)bh->b_data; + + /* Verify magic */ + if (le32_to_cpu(fsb->s_magic) !=3D 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 =3D ftrfs_crc32(fsb, offsetof(struct ftrfs_super_block, s_crc32)); + if (crc !=3D le32_to_cpu(fsb->s_crc32)) { + errorf(fc, "ftrfs: superblock CRC32 mismatch (got 0x%08x, expected 0x%08= x)", + crc, le32_to_cpu(fsb->s_crc32)); + goto out_brelse; + } + + /* Allocate in-memory sb info */ + sbi =3D kzalloc(sizeof(*sbi), GFP_KERNEL); + if (!sbi) { + ret =3D -ENOMEM; + goto out_brelse; + } + + sbi->s_ftrfs_sb =3D kzalloc(sizeof(*sbi->s_ftrfs_sb), GFP_KERNEL); + if (!sbi->s_ftrfs_sb) { + ret =3D -ENOMEM; + goto out_free_sbi; + } + + memcpy(sbi->s_ftrfs_sb, fsb, sizeof(*fsb)); + sbi->s_sbh =3D bh; + sbi->s_free_blocks =3D le64_to_cpu(fsb->s_free_blocks); + sbi->s_free_inodes =3D le64_to_cpu(fsb->s_free_inodes); + spin_lock_init(&sbi->s_lock); + + sb->s_fs_info =3D sbi; + sb->s_magic =3D FTRFS_MAGIC; + sb->s_op =3D &ftrfs_super_ops; + sb->s_maxbytes =3D MAX_LFS_FILESIZE; + + /* Read root inode (inode 1) */ + root_inode =3D ftrfs_iget(sb, 1); + if (IS_ERR(root_inode)) { + ret =3D PTR_ERR(root_inode); + pr_err("ftrfs: failed to read root inode: %d\n", ret); + goto out_free_fsb; + } + + sb->s_root =3D d_make_root(root_inode); + if (!sb->s_root) { + ret =3D -ENOMEM; + goto out_free_fsb; + } + + if (ftrfs_setup_bitmap(sb)) { + ret =3D -ENOMEM; + goto out_free_fsb; + } + + pr_info("ftrfs: mounted (blocks=3D%llu free=3D%lu inodes=3D%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 =3D NULL; +out_brelse: + brelse(bh); + return ret; +} + +/* + * fs_context ops =E2=80=94 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 =3D { + .get_tree =3D ftrfs_get_tree, +}; + +static int ftrfs_init_fs_context(struct fs_context *fc) +{ + fc->ops =3D &ftrfs_context_ops; + return 0; +} + +static struct file_system_type ftrfs_fs_type =3D { + .owner =3D THIS_MODULE, + .name =3D "ftrfs", + .init_fs_context =3D ftrfs_init_fs_context, + .kill_sb =3D kill_block_super, + .fs_flags =3D FS_REQUIRES_DEV, +}; + +/* + * Inode cache constructor + */ +static void ftrfs_inode_init_once(void *obj) +{ + struct ftrfs_inode_info *fi =3D obj; + + inode_init_once(&fi->vfs_inode); +} + +/* + * Module init / exit + */ +static int __init ftrfs_init(void) +{ + int ret; + + ftrfs_inode_cachep =3D 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 =3D 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 "); +MODULE_DESCRIPTION("FTRFS: Fault-Tolerant Radiation-Robust Filesystem"); +MODULE_VERSION("0.1.0"); +MODULE_ALIAS_FS("ftrfs"); --=20 2.52.0 From nobody Sat Jun 20 15:19:29 2026 Received: from mail-wm1-f48.google.com (mail-wm1-f48.google.com [209.85.128.48]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 757773947BD for ; Mon, 13 Apr 2026 21:06:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.48 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776114385; cv=none; b=VzAy1nmBEXYFaX6LY72Yhezo78Ftc/7byris36ATAt2Bs05nL/IDshusIVGEXD7Yygnz5FMi8GIR8yyi23DjPkuTpQeEZBzS1kdkCneZ6IQN7IhPGMJcKDdU53BZu+iK2NDR2rbERsFvJ6UOXZjnICgYwEUxmhDjm3584oDXM4g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776114385; c=relaxed/simple; bh=v32LiiFvmmIm2TLfU9CJlRKpWxy/76tGuhfVX78Pjms=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=OyUUiB7QGmMzjqPY+N6i/p6GMZimR4z+1Zqq+C1UG+QiNny8sESfC4jkIT9bh3bkkPeyGXUo+R2jYiET6fhTIa/5hbq/LtDYf7sezueSKMcc2c125X2Dw33OQxBzyFaul6bELCgsZlSVzrVxOtWSzoA+3lNGtrrR+8yNoCKL8Yo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hackers.camp; spf=pass smtp.mailfrom=gmail.com; arc=none smtp.client-ip=209.85.128.48 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hackers.camp Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-wm1-f48.google.com with SMTP id 5b1f17b1804b1-4836fc075d2so6383605e9.0 for ; Mon, 13 Apr 2026 14:06:23 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776114382; x=1776719182; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=DbD7D+uc0+23/XzD49X5LHz5RWaS8JM3kH9aHiCiWR4=; b=fbkAMf0nWcd4iO8DkKvxgunRiWlR1/xV2KhBMt/sxMOM5X8/bBkeFtuBGHoiJ6fg4G es8YwcG0mb+sOXkFlwzl+lO+K1e5sfStEHTO+ZEaMxrv6JkPy7UDl4EVCr8Q4W98yD6L k5TEoHvVyDry5YsSvdoiCnFvn6Stkl4/j3rvE2qGNP2Tijz4K+7+Vs6QgGqtFdztxIvl 1sW052OfZiXrhmzFLeppueZYe/OP4ulgsR2xm+hCr2r6WWImu1wTv/tjKF8GFGL9qSMl o7qGFldW/K/3HRtiotTA4WXyIcsd4jjcRuXD4UWXy2Hap3HG6RuoywSfiSFVOqytcCMg ns/Q== X-Forwarded-Encrypted: i=1; AFNElJ8YQTw5MaHjSIDXsxY37cBEr/dzcvP0ysVp+8xUNwr48mN1BjUzrmhKGrFqZ905xYP8pya9DH01d5jBeko=@vger.kernel.org X-Gm-Message-State: AOJu0YzL5KjBK17V5Vw5WhhcmWXyfjltv+Rw2/YAR/RI1+bZqBE9rKMu NFCFRXzYztqmJmkX0INF/KFmt0UthmsVkmuTBeijRi7gej7qPhDgVPgj X-Gm-Gg: AeBDievRCibyOt3FzyTv/zz16Fvt3F7C7HWLpHYI6kUaUIfI+YPsMJHxJSAFTrEceIs s5Rhy6FNAqps2EdQQ/t3uAThsNmP4a1Oj915/AfKcY90XI7vGd8fymX/Z+P77QiLRwhkV4qtmlE rh7einUpk+lCCla7TxtoyNjconarIoV7NnFr5SYI3tmBcliG0RKfGOlXxiBbZjmPgcDbN0u7vd7 2q/iI/fyo6+3EkBUJpKjtIeFvrwXTPpH5ngtGump1QZgNVxSMpoIqbxkPcFpoALVAGN2XToEwSj RiOo5G3+FBNH8r6ted9vSgBJggFaC+ecXx6Z/T4Xt1aJsk8xO2YmSYPkfVmCRvy/JeksacqF4Jp 2elN4i67kT20QQxFa4MttU47UbXi99RzoCshVixYY0gC/kC8asisVdpSub5xZI+k+wAmlJBTXA/ KVEQRIdjkPLKMen0me5OCgJgSZm6/krvBqjN8YSYuA4lG9nUTav/v5ShaIbA/Tm7evrjXd8ulzI C0C X-Received: by 2002:a05:6000:2f84:b0:43d:5584:9077 with SMTP id ffacd0b85a97d-43d64289ccbmr11795787f8f.2.1776114381608; Mon, 13 Apr 2026 14:06:21 -0700 (PDT) Received: from spartian-1.home ([2a01:cb1c:784:2f00:708:2805:7128:7a75]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43d63e5c98fsm35130020f8f.35.2026.04.13.14.06.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 13 Apr 2026 14:06:21 -0700 (PDT) From: Aurelien DESBRIERES To: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Aurelien DESBRIERES , viro@zeniv.linux.org.uk, brauner@kernel.org, willy@infradead.org, djwong@kernel.org, adilger@dilger.ca, pfalcato@suse.de Subject: [PATCH v2 03/11] ftrfs: add inode operations Date: Tue, 14 Apr 2026 01:05:44 +0200 Message-ID: <20260413230601.525400-4-aurelien@hackers.camp> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260413230601.525400-1-aurelien@hackers.camp> References: <20260413230601.525400-1-aurelien@hackers.camp> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable 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: Aurelien DESBRIERES --- 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 =E2=80=94 Inode operations + * Author: roastercode - Aurelien DESBRIERES + */ + +#include +#include +#include +#include +#include "ftrfs.h" + +/* + * ftrfs_iget =E2=80=94 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 =3D 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 =3D 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 =3D FTRFS_BLOCK_SIZE / sizeof(struct ftrfs_inode); + block =3D le64_to_cpu(sbi->s_ftrfs_sb->s_inode_table_blk) + + (ino - 1) / inodes_per_block; + offset =3D (ino - 1) % inodes_per_block; + + bh =3D 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 =3D (struct ftrfs_inode *)bh->b_data + offset; + /* Verify inode CRC32 */ + crc =3D ftrfs_crc32(raw, offsetof(struct ftrfs_inode, i_crc32)); + if (crc !=3D 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 =3D FTRFS_I(inode); + + /* Populate VFS inode */ + inode->i_mode =3D le16_to_cpu(raw->i_mode); + inode->i_uid =3D make_kuid(sb->s_user_ns, le16_to_cpu(raw->i_uid)); + inode->i_gid =3D make_kgid(sb->s_user_ns, le16_to_cpu(raw->i_gid)); + set_nlink(inode, le16_to_cpu(raw->i_nlink)); + inode->i_size =3D le64_to_cpu(raw->i_size); + inode->i_blocks =3D 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 =3D raw->i_indirect; + fi->i_dindirect =3D raw->i_dindirect; + fi->i_flags =3D le32_to_cpu(raw->i_flags); + + /* Set ops based on file type */ + if (S_ISDIR(inode->i_mode)) { + inode->i_op =3D &ftrfs_dir_inode_operations; + inode->i_fop =3D &ftrfs_dir_operations; + } else if (S_ISREG(inode->i_mode)) { + inode->i_op =3D &ftrfs_file_inode_operations; + inode->i_fop =3D &ftrfs_file_operations; + } else { + /* Special files: use generic */ + init_special_inode(inode, inode->i_mode, 0); + } + + brelse(bh); + unlock_new_inode(inode); + return inode; +} --=20 2.52.0 From nobody Sat Jun 20 15:19:29 2026 Received: from mail-wm1-f51.google.com (mail-wm1-f51.google.com [209.85.128.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1D008395240 for ; Mon, 13 Apr 2026 21:06:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776114386; cv=none; b=h32gJ97sAcQc00m0wjnzg+h4p3DWGHZpQJkNGjxLLiDOG68gO2FK5a4XUVgSZ4UO/9GlGF9REx3oA/C3hqP7215HgxHxT3U426ybCJJDpjChuT64TMW/7e5OijYy+QxcyYotTZbCcdDiv4V/VuXdIjXtELOi+CaD42LrCnJTQQ8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776114386; c=relaxed/simple; bh=+r1Y3kHnY4xi3Jk1Q7GHUslpyQv7/s2wi8tCMEA0jz4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=BLqzuPPUgXJDs3NBgwjc4oYyETp7UTOLzPt+ALqxCfsuVIoumxACHoAGxOcWAryExE6pJL53boWH2y8bFTc5ffX3Qk6DqPH2y/P3K6p4fpNiMyhT189OdG33cjuwFmo2EqrUAKktpii1t9jyb9nT0Ih0tWe7G44hJxQH0VWNmC8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hackers.camp; spf=pass smtp.mailfrom=gmail.com; arc=none smtp.client-ip=209.85.128.51 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hackers.camp Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-wm1-f51.google.com with SMTP id 5b1f17b1804b1-488965a9ca1so5872815e9.2 for ; Mon, 13 Apr 2026 14:06:24 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776114383; x=1776719183; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=jLhHtjSxaas4v+h5EWtZjmwyeBl0aB7UUhjqllpkU1I=; b=Kjek65YSwHgcVkQw/jIVNUIPEIiL7fubZVz/euFZp+n+vPhw1s4OJyXyDkd33k1r4z Xtr6fAy2FNcud7/KCuvj8tEWtcQsVT7G3Q7nKpiYDz6G0BSsyfNHzMkCrZODBefqIUyk VcUN/Q/rhq2F0vdfqwxNoHaY/Zr1JHx9u96WPHQ0BP60WwTzgoT35BAlhONPyiGkU0VD jdratsPQq7tNdvBPYv3l3bfNgmRwpynKh8kduLLvsXSXlCrsasca5Mkpj/lF2OTB5xpg 4UmUb6dxrKnCJ6FrJg2ZZJ3VU/JQHPQK6mX/I1IAA46m+ILcgFQ53jlnllWs60cuM1l+ +JYA== X-Forwarded-Encrypted: i=1; AFNElJ+CkiHhW1rbe6tOfJL5vaTTP0jicoo6YHSCCMTVs0tPGchIN4WzOrr1X+F053npFzbH3vSO0tcOnN/rIms=@vger.kernel.org X-Gm-Message-State: AOJu0YwgG8BltLqdp3ub0uVwyCmCZcG8euKwi76AhkS84n9Jad24LvGv 7G8p9c6e+qRODE5FePbUoZPOpmWplSC0InTt02P5FG1UYnMxUEt06dvS X-Gm-Gg: AeBDietrW6XNNzdADi+DnUCQTmcDUS5vK8UHCNgaIscTWuw0u2wfY05ERGDwmGT2Reg 8NC6ssf+xC/ltlUjmPkoodmqZUBs5iBkYv8u5gZDdoL/eKe3ecT/RXAFH/ptf5GyQqw/XJYLb+/ 8fRZtdeAVpdb10tmmPTbAzamexaCve7VIU/VjrD4NzsQpCDloDnmff/j6W+s4g9TfQBX7nbf2G6 l7YfGXUXBi7629dtQXGOv8OOGer7R20/9DtCa7kAxjm6fnur4HuD8yma4GCWRftKjy/v+R/XbkI bXX7QMeHNuq4zaFYtsTKV9Z/tKwcLgMbiAskN6UsLTedgbZzGbPeSMuLny2ga26lCnPy5xEs0fe H43+zkmzuj7hI/xDYSwpau/SMUehB9fzLsEKjiQwEK911MRVXJWGnjXdpDNL/9HHdXhG5oR4tlp 0j4BRScyTp1yhXbYmhEaaeFBUZkEnb2iAlwYh6Yr03nLgqvksaLc7JLIN4oPCMoQdYrKCfEFfb6 nVE X-Received: by 2002:a05:600c:4689:b0:488:93c5:4dc with SMTP id 5b1f17b1804b1-488d7f76192mr120984355e9.7.1776114383341; Mon, 13 Apr 2026 14:06:23 -0700 (PDT) Received: from spartian-1.home ([2a01:cb1c:784:2f00:708:2805:7128:7a75]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43d63e5c98fsm35130020f8f.35.2026.04.13.14.06.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 13 Apr 2026 14:06:22 -0700 (PDT) From: Aurelien DESBRIERES To: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Aurelien DESBRIERES , viro@zeniv.linux.org.uk, brauner@kernel.org, willy@infradead.org, djwong@kernel.org, adilger@dilger.ca, pfalcato@suse.de Subject: [PATCH v2 04/11] ftrfs: add directory operations Date: Tue, 14 Apr 2026 01:05:45 +0200 Message-ID: <20260413230601.525400-5-aurelien@hackers.camp> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260413230601.525400-1-aurelien@hackers.camp> References: <20260413230601.525400-1-aurelien@hackers.camp> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable 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: Aurelien DESBRIERES --- 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 =E2=80=94 Directory operations + * Author: roastercode - Aurelien DESBRIERES + */ + +#include +#include +#include "ftrfs.h" + +/* + * ftrfs_readdir =E2=80=94 iterate directory entries + */ +static int ftrfs_readdir(struct file *file, struct dir_context *ctx) +{ + struct inode *inode =3D file_inode(file); + struct super_block *sb =3D inode->i_sb; + struct ftrfs_inode_info *fi =3D 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 =3D=3D INT_MAX) + return 0; + /* Emit . and .. (ctx->pos: 0=3D., 1=3D.., 2+=3Dreal 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 =3D 0; block_idx < FTRFS_DIRECT_BLOCKS; block_idx++) { + block_no =3D le64_to_cpu(fi->i_direct[block_idx]); + if (!block_no) + break; + + bh =3D sb_bread(sb, block_no); + if (!bh) + continue; + + offset =3D 0; + while (offset < FTRFS_BLOCK_SIZE) { + de =3D (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 +=3D le16_to_cpu(de->d_rec_len); + } + + brelse(bh); + } + + ctx->pos =3D INT_MAX; + return 0; +} + +/* + * ftrfs_lookup =E2=80=94 find dentry in directory + */ +struct dentry *ftrfs_lookup(struct inode *dir, + struct dentry *dentry, + unsigned int flags) +{ + struct super_block *sb =3D dir->i_sb; + struct ftrfs_inode_info *fi =3D FTRFS_I(dir); + struct buffer_head *bh; + struct ftrfs_dir_entry *de; + struct inode *inode =3D 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 =3D 0; block_idx < FTRFS_DIRECT_BLOCKS; block_idx++) { + block_no =3D le64_to_cpu(fi->i_direct[block_idx]); + if (!block_no) + break; + + bh =3D sb_bread(sb, block_no); + if (!bh) + continue; + + offset =3D 0; + while (offset < FTRFS_BLOCK_SIZE) { + de =3D (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 =3D=3D dentry->d_name.len && + !memcmp(de->d_name, dentry->d_name.name, + de->d_name_len)) { + unsigned long ino =3D le64_to_cpu(de->d_ino); + + brelse(bh); + inode =3D ftrfs_iget(sb, ino); + goto found; + } + + offset +=3D le16_to_cpu(de->d_rec_len); + } + brelse(bh); + } + +found: + return d_splice_alias(inode, dentry); +} + +const struct file_operations ftrfs_dir_operations =3D { + .llseek =3D generic_file_llseek, + .read =3D generic_read_dir, + .iterate_shared =3D ftrfs_readdir, +}; + + --=20 2.52.0 From nobody Sat Jun 20 15:19:29 2026 Received: from mail-wm1-f54.google.com (mail-wm1-f54.google.com [209.85.128.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EC97C396591 for ; Mon, 13 Apr 2026 21:06:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776114387; cv=none; b=WeLHMSZ7erAM7Gta4LcPOOp2aYiYXGoej6h1LJ/q8WGS7ro3Ou11NMKR2r77akNFrmpJeW4PKNA7F07BwzjY91eTlVA36otagFX3rFUWK09w8aVz4LWcwWpVxqtZYE+xogG6CUpBbzjVftJpg/z4f8zCC76A8hTHvnnHN+ETwe0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776114387; c=relaxed/simple; bh=ESRzkVvOWER4qlZIiaT0hCchCEOjI9Koyz6YvBEQsqI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=bOvjbz/6brLmRDzYiPwXfuT8fboy55uc9X0UeJRviTaJRDACZyDNpe11QgpFuV+YLnP3EVhedcJG/z+1S2oR1Ffrwfdi/5xdc4V79/fOGiZtQlRXl0maqBB/AzlUyde060OiRpW+n6vk/QS+DaGJhtirO5rCY3hEjQat7Aa9mOU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hackers.camp; spf=pass smtp.mailfrom=gmail.com; arc=none smtp.client-ip=209.85.128.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hackers.camp Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-wm1-f54.google.com with SMTP id 5b1f17b1804b1-488965a9ca1so5872855e9.2 for ; Mon, 13 Apr 2026 14:06:25 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776114384; x=1776719184; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=efycFkVlY38OPi/zLUsHHPWsJjhRjzXk7nam0qEsQhs=; b=msMWfYxUpR6Ig4OJJlye0J79frXj58qt5d/zFWevnDJ9/QHEBFGcZG2g1FPs+llnWj qo6BuG7DkpYJMpI9UA/GheJwXrzCi0AGfZ7hdIkToiwlKFjWsjWIaWLx1L/iP2q4G2Th dtNzA3RoT051VmHQEGo5K2tFraz53LzsLS5yokhjxNnIPw0TM9o5/Bd8DbtXMpX0S7I+ D95lKpukjjNCAl8gimB4dvpu3qrMXjp1ABdhSLaLRum/rySSu5BiWf5lp3yeYg3F4cQY LG1w8OTRH9DDAtQe/i49P7C4WV8g0W3b050HkFNnQsRORqV9MTJEeELT5M12xhdmE8Nu FW9Q== X-Forwarded-Encrypted: i=1; AFNElJ850gNaEjGEmYGWjAiKNDb5v6ez5r37Us3IAjaF50Hjbvc0BH7FYdzPki5tDYVs7rbTHsq+pIWpJPbycSE=@vger.kernel.org X-Gm-Message-State: AOJu0YxFDkPxoZWwN0xu1SfIIEgw45HNKsXdcas6kGd0mmCyM120jwxH F0lqjJ8TkUff6f7XWbf+BljoCei1TiwtS1QJE47JGy0i0DmhsPjAT7rD X-Gm-Gg: AeBDieu3UeEOMMDneOoLCtsYOU2kI+i/eEaV1AV0PHR79IN9VDsdhvsyR+IoGX6ydrO mqMuwpZT6y6NfEhPRHN1c0PwR/hhFGBpWic7MO36oMKWofC7scCJ16OkYn5HUnf+HQfa+JSS9I4 cAIJzlNZuqwoxfXe1aSb1lk6G83WlnGn627vU5vuiD/SPlXlY6RVgOrixAu4QxBGtgleTPodhKU 8mYSYuIAjz6XqlxEQahKbJ0nnnPqFZQNf7F9NHi433kB2KYuiVrYiK7J1QhYo1aFy+ig+Cw9bvs isHoPu4jys0XDj92VZNj6EajkMx39Dd7d3w/gDkAxxHDt7WQ7V/hJOyxVUT2zj4QBLDZPjeCDyQ eqTFfDTx2a7GkAPtdv7XCbKa5xQ3PpOs4nSfVCAJC5V/kqODKueBbOWRF3UfbvbqCjT1TzySj5C GeQI4egRmqQTmGCR5d8o4XHijEgy3kRx8cuEW6EVuLt1DEWIBPI+R5ei5tCG4dnCcB+0eIWdUqe oI6 X-Received: by 2002:a05:6000:481e:b0:43d:64c6:1765 with SMTP id ffacd0b85a97d-43d65267229mr9435381f8f.2.1776114384316; Mon, 13 Apr 2026 14:06:24 -0700 (PDT) Received: from spartian-1.home ([2a01:cb1c:784:2f00:708:2805:7128:7a75]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43d63e5c98fsm35130020f8f.35.2026.04.13.14.06.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 13 Apr 2026 14:06:23 -0700 (PDT) From: Aurelien DESBRIERES To: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Aurelien DESBRIERES , viro@zeniv.linux.org.uk, brauner@kernel.org, willy@infradead.org, djwong@kernel.org, adilger@dilger.ca, pfalcato@suse.de Subject: [PATCH v2 05/11] ftrfs: add file operations Date: Tue, 14 Apr 2026 01:05:46 +0200 Message-ID: <20260413230601.525400-6-aurelien@hackers.camp> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260413230601.525400-1-aurelien@hackers.camp> References: <20260413230601.525400-1-aurelien@hackers.camp> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable 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: Aurelien DESBRIERES --- 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 =E2=80=94 File operations (skeleton) + * Author: roastercode - Aurelien DESBRIERES + * + * NOTE: read/write use generic_file_* for now. + * The EDAC/RS layer will intercept at the block I/O level (next iteration= ). + */ + +#include +#include +#include "ftrfs.h" + +const struct file_operations ftrfs_file_operations =3D { + .llseek =3D generic_file_llseek, + .read_iter =3D generic_file_read_iter, + .write_iter =3D generic_file_write_iter, + .mmap =3D generic_file_mmap, + .fsync =3D generic_file_fsync, + .splice_read =3D filemap_splice_read, +}; + +const struct inode_operations ftrfs_file_inode_operations =3D { + .getattr =3D simple_getattr, +}; --=20 2.52.0 From nobody Sat Jun 20 15:19:29 2026 Received: from mail-wm1-f41.google.com (mail-wm1-f41.google.com [209.85.128.41]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 50816396D0F for ; Mon, 13 Apr 2026 21:06:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.41 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776114391; cv=none; b=pBRgM70JtSsi1lTc6EAnaTTr5QZZzLLdSuZlA5us1G2tQszknWbidi70d6z0CI+4KjT2orYgQDJEaSV8/GKyMsJGHQgZUhyD8ZkDv8igXj3XLArkV5sDO08OShJ3CiMH6kq4dslnEAe7oS2G1AhPWqGmKe/7ejlarTkFD5PwCK8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776114391; c=relaxed/simple; bh=/ZdlCM8sH1mbF4GXl4o9bel+3INcvruSdLH7gacwiwM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=sCMlrqk9A+3NpGGIXBdurhgRLm6fGho9QC1Arz1W1pgf0Y5w1x+Mk0uc885fEAgu1jQsp0fow3DlhU3gEFH8C59vy+yNHbalx/JJ1n4wU0XKDHVxU0XHeYTIf1LxuuEjDsLMtosO03fB55PMAGKFiEnMZUPHaGjn+zwlRlytJAg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hackers.camp; spf=pass smtp.mailfrom=gmail.com; arc=none smtp.client-ip=209.85.128.41 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hackers.camp Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-wm1-f41.google.com with SMTP id 5b1f17b1804b1-4836fc075d2so6383695e9.0 for ; Mon, 13 Apr 2026 14:06:27 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776114386; x=1776719186; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=gl4pTDrAKIvexuR34sysFgQEYkX8+FoCtcApw2eOqGQ=; b=QzAZU1M3N+AuiRNZzMbOxt0Gw5cICy+7ODraEbYxsLONtsjst9QeRO4rILAdITR2As 65EP7ZdqSS7eYBq329OVDOid2AJ5KYbEBaHKsT+/ti0LiblV3DVzxkxY7GSQEKUWR/Go IKVx10nVjvxPM4BiWNgczQGoW64gBeQNErMRh5zljrklORrrOWH2U8UZWooqOIDwVjIH ZtHhgICIyHKswB0U4GZpkRhmPepv0XaoxN+4FedeXM9TQr0C3OB62TGkGKTCGOamejj+ R3yudZqSEUDnrVWQ3uw5Gu7UgDnE3yOgjnpJ7VFuGnMBfsdJlFYqt4mTihcdRN5lGnfr 5dyQ== X-Forwarded-Encrypted: i=1; AFNElJ9XXJZ46Ru7yaFLu3XryHn1g1X/qM5xoQMSgBiCeBQ+lZoBGxe6bLbi77gksyUP2e/OZS1tXNvjmCKmTUg=@vger.kernel.org X-Gm-Message-State: AOJu0YzbtbPQYAD/kCz4I2NaiV66+igdrEF5FZpwCYP/QwbZgy6lW2sX v+uNqsRRJ7uAhm/GHRpr1kySsDdBZz/0qoih+Qr5PT+FmovrNBlypCD6 X-Gm-Gg: AeBDieuUF7jZ3hQXGzfLBKAWTtpk7ewG78ecsBCg8ZJfXbyyCnihMH/h3yUqV29jtCV eP4O+Z+qpe/0p24mPBVTanp0FXSEf297J74peCnUOqfZY4j0nBu7n/IkCqmJv9vRmNqrNE0J/Ax XQYlTurztbCSrqBcdrTuCG34RM3utV5LBB0Jpv7Ab6FLb53+SH2d3/TbEMjLBJ7t0lrpcjGBPeT VOXtKaykQ9G7yBfnPvbS7fbrxdw8UZB81WHh1HWfttTWDZMskmVDCTnScRpXDhqxNh+u6Kke8Ay kfQzkMm5n5pSGbOtZ5Yfs3+MaBz1Vj6pcJUo41QiXQES91GQvPND8ErChHvstL4snPb7dDIZAQd 0zGTOfJEYCHfKRCX60U2TGuTkfg7xJQFMyhsn6dUWjHNH2QngBz1/UDS6gKzlvqGJS0ShtzfPal QrnpUw+xv2lzok8HIrV+0fMWIOX/4U97L3dsy3VqycrWijlDUAWaJ3LYsIZpmPF9gPsmALycmrf Fek X-Received: by 2002:a05:600c:b85:b0:488:afb4:b98c with SMTP id 5b1f17b1804b1-488d68b3827mr112566155e9.3.1776114385545; Mon, 13 Apr 2026 14:06:25 -0700 (PDT) Received: from spartian-1.home ([2a01:cb1c:784:2f00:708:2805:7128:7a75]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43d63e5c98fsm35130020f8f.35.2026.04.13.14.06.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 13 Apr 2026 14:06:24 -0700 (PDT) From: Aurelien DESBRIERES To: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Aurelien DESBRIERES , viro@zeniv.linux.org.uk, brauner@kernel.org, willy@infradead.org, djwong@kernel.org, adilger@dilger.ca, pfalcato@suse.de Subject: [PATCH v2 06/11] ftrfs: add block and inode allocator Date: Tue, 14 Apr 2026 01:05:47 +0200 Message-ID: <20260413230601.525400-7-aurelien@hackers.camp> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260413230601.525400-1-aurelien@hackers.camp> References: <20260413230601.525400-1-aurelien@hackers.camp> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable 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 =3D=3D 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: Aurelien DESBRIERES --- 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 =E2=80=94 Block and inode allocator + * Author: Aur=C3=A9lien DESBRIERES + * + * 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 =3D free, 0 =3D used). + */ + +#include +#include +#include +#include +#include "ftrfs.h" + +/* + * ftrfs_setup_bitmap =E2=80=94 allocate and initialize the in-memory bloc= k 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 bloc= k. + */ +int ftrfs_setup_bitmap(struct super_block *sb) +{ + struct ftrfs_sb_info *sbi =3D FTRFS_SB(sb); + unsigned long total_blocks; + unsigned long data_start; + + total_blocks =3D le64_to_cpu(sbi->s_ftrfs_sb->s_block_count); + data_start =3D le64_to_cpu(sbi->s_ftrfs_sb->s_data_start_blk); + + if (total_blocks <=3D data_start) { + pr_err("ftrfs: invalid block layout (total=3D%lu data_start=3D%lu)\n", + total_blocks, data_start); + return -EINVAL; + } + + sbi->s_nblocks =3D total_blocks - data_start; + sbi->s_data_start =3D data_start; + + /* Allocate bitmap: one bit per data block */ + sbi->s_block_bitmap =3D 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 =3D sbi->s_nblocks - sbi->s_free_blocks; + unsigned long i; + + for (i =3D 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 =E2=80=94 free the in-memory bitmap + * Called from ftrfs_put_super(). + */ +void ftrfs_destroy_bitmap(struct super_block *sb) +{ + struct ftrfs_sb_info *sbi =3D FTRFS_SB(sb); + + if (sbi->s_block_bitmap) { + bitmap_free(sbi->s_block_bitmap); + sbi->s_block_bitmap =3D NULL; + } +} + +/* + * ftrfs_alloc_block =E2=80=94 allocate a free data block + * @sb: superblock + * + * Returns the absolute block number (>=3D 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 =3D 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 =3D=3D 0) { + spin_unlock(&sbi->s_lock); + pr_warn("ftrfs: no free blocks\n"); + return 0; + } + + /* Find first free bit (set =3D free in our convention) */ + bit =3D find_first_bit(sbi->s_block_bitmap, sbi->s_nblocks); + if (bit >=3D sbi->s_nblocks) { + spin_unlock(&sbi->s_lock); + pr_err("ftrfs: bitmap inconsistency (free_blocks=3D%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 =3D 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 =E2=80=94 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 =3D 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 =3D (unsigned long)(block - sbi->s_data_start); + + if (bit >=3D 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 =3D cpu_to_le64(sbi->s_free_blocks); + mark_buffer_dirty(sbi->s_sbh); + + spin_unlock(&sbi->s_lock); +} + +/* + * ftrfs_alloc_inode_num =E2=80=94 allocate a free inode number + * @sb: superblock + * + * Returns inode number >=3D 2 on success (1 =3D 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 =3D 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 =3D 0; + + inodes_per_block =3D FTRFS_BLOCK_SIZE / sizeof(struct ftrfs_inode); + inode_table_blk =3D le64_to_cpu(sbi->s_ftrfs_sb->s_inode_table_blk); + total_inodes =3D le64_to_cpu(sbi->s_ftrfs_sb->s_inode_count); + + spin_lock(&sbi->s_lock); + + if (sbi->s_free_inodes =3D=3D 0) { + spin_unlock(&sbi->s_lock); + return 0; + } + + /* Scan inode table blocks looking for a free inode (i_mode =3D=3D 0) */ + for (block =3D 0; block * inodes_per_block < total_inodes; block++) { + bh =3D sb_bread(sb, inode_table_blk + block); + if (!bh) + continue; + + raw =3D (struct ftrfs_inode *)bh->b_data; + + for (i =3D 0; i < inodes_per_block; i++) { + unsigned long ino_num =3D block * inodes_per_block + i + 1; + + if (ino_num > total_inodes) + break; + + /* inode 1 =3D root, always reserved */ + if (ino_num =3D=3D 1) + continue; + + if (le16_to_cpu(raw[i].i_mode) =3D=3D 0) { + /* Found a free inode slot */ + ino =3D (u64)ino_num; + sbi->s_free_inodes--; + sbi->s_ftrfs_sb->s_free_inodes =3D + 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; +} --=20 2.52.0 From nobody Sat Jun 20 15:19:29 2026 Received: from mail-wm1-f43.google.com (mail-wm1-f43.google.com [209.85.128.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 80C75397E98 for ; Mon, 13 Apr 2026 21:06:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776114391; cv=none; b=tvWfpPyoxJyUM73hgY530fLE52H7PbPnLLxNRO4S+6DyuZlky8FxQkz7tqJRpDWl1ZE5EXyIIhyYvWWmhkqLzZeb77UEvpmEnFigCP6s5a7xUTs+s4yw4AF1Cj9YhFgjSvpOJ1AgFSUf4ZehE3dFX9EOCHeObHS/PDpUSogzB3I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776114391; c=relaxed/simple; bh=wrV7Ca5qThAL415CiyY44+jpphMO33Ku/SaUki5fZYY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=VxA+rGRzP8ncS3x47osv/PvtUtQgl/YaPUALCJFKIq1CJbNOVPo4n4Mt/fo0ZSMbhrBrLZ5ZSoOzYWndPOkGrb8HMwnJiOH0c9lq4w6dRa1C9K5JGfkXJRsZGJOeJur3Ze6lyp0rTeO1EbYC5/Wkh2dqJ38IdI5t8md92xl8Cb4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hackers.camp; spf=pass smtp.mailfrom=gmail.com; arc=none smtp.client-ip=209.85.128.43 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hackers.camp Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-wm1-f43.google.com with SMTP id 5b1f17b1804b1-488debfa07bso2393535e9.2 for ; Mon, 13 Apr 2026 14:06:28 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776114387; x=1776719187; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=Y/Zdrwtc1Pp3hc8TSWkY940GPVELN0WXsAyqaQmbSgU=; b=cqy/gPvSNSPJ+OAXSHZ/rL5eW5NtzhQ2G4pfyXO2IsKYlGiAZQjF6AK7pFrnuCxlZp UCiO3E/aWO27LYGS9l0eZXhXn3bav6i9VRKvXNhtbM3dl1aQWNYIJepqPILi8EqgRx1H PNEA5VdkQNx0pQCgFXdFC7H8nNE9iBynhQez/aZL9xfQG/aghatgQSwHzeRPsTHKIpLk 4y3OZUtQknrBa+WkJ2lV+hr65pvS35kbHeeBf+QlGwhqQAgUnqcUYbZQBRFinW4JMHdx 0jiafBWUoftI7RQU/b2ykRLi8FLamt/HtjE6M5E1JL9jXV4qwOdFQ78uBdJYs3qd4QcW TxoA== X-Forwarded-Encrypted: i=1; AFNElJ8sNad/g2QopiabQfJ0onrEvV33nGysGfei/K3ffQuBTb1D5alv9VHzSM2JuJjOID0qB1mKHMAT+Oou7q0=@vger.kernel.org X-Gm-Message-State: AOJu0YyOW5f2zUp0+9wctr1pb3xFIZUbWO2a8OEQ8lsbX+zSIpieC96D tnIyGb9vgFlr7XDKs1dnUvHMEuvKe4dtlSqEubiVH9jj2ltOQ6N8H7xe X-Gm-Gg: AeBDietj0l6SFhj6LG6KK0hztqe0sj+roahGqRPk8DAfGKjREaV/j2pl1dheXXTl1HO PD1tBJmaXQEQXViYBmTibmFv47WHUelOwyHeJndsSc8vbIXCI4qJXkx6nRf6nU4nACzprjR7kz3 3OAxxZa5oR4rKR6iQwhEQ6JWaO5yb6DU/GboTqPnZDXhlJ0MoX4kPtcFwYgsDfhu5W/Jc5EOU26 EsvyZXAOmMedhvs/j8O3awUkj9tljmqpR4cCM5fOSfeoFvBwqyvd1OYwT0PH5ljPpiP5e6euvUT CDoyLdfbRcofK7m3kiY3ob2IhxE+MYm45b44OWROvy5/8BuaDGD3OtvEjCDSlBnCss+zJKmQ74n 32aCxnJbzkksKP0Kiy1Af3I9mB49CjAoTMsPkdkfP7PwB7Yv25FUWSHuFSVITrGwVf3JTv/ruQ1 Eijm19a5BGcevE0ZVNueYuymwxpsnAgSGos6cuVsPuhjgV/szizJLOO70HdQ/r5xluGybOE2lk7 ANQ X-Received: by 2002:a05:600c:3149:b0:487:575:5e3 with SMTP id 5b1f17b1804b1-488d68c7001mr96385805e9.5.1776114386759; Mon, 13 Apr 2026 14:06:26 -0700 (PDT) Received: from spartian-1.home ([2a01:cb1c:784:2f00:708:2805:7128:7a75]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43d63e5c98fsm35130020f8f.35.2026.04.13.14.06.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 13 Apr 2026 14:06:26 -0700 (PDT) From: Aurelien DESBRIERES To: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Aurelien DESBRIERES , viro@zeniv.linux.org.uk, brauner@kernel.org, willy@infradead.org, djwong@kernel.org, adilger@dilger.ca, pfalcato@suse.de Subject: [PATCH v2 07/11] ftrfs: add filename and directory entry operations Date: Tue, 14 Apr 2026 01:05:48 +0200 Message-ID: <20260413230601.525400-8-aurelien@hackers.camp> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260413230601.525400-1-aurelien@hackers.camp> References: <20260413230601.525400-1-aurelien@hackers.camp> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable 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: Aurelien DESBRIERES --- 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 =E2=80=94 Filename / directory entry operations + * Author: Aur=C3=A9lien DESBRIERES + * + * Implements: create, mkdir, unlink, rmdir, link, rename + */ + +#include +#include +#include +#include +#include "ftrfs.h" + +/* ------------------------------------------------------------------ */ +/* Helper: write a raw ftrfs_inode to disk */ +/* ------------------------------------------------------------------ */ + +static int ftrfs_write_inode_raw(struct inode *inode) +{ + struct super_block *sb =3D inode->i_sb; + struct ftrfs_sb_info *sbi =3D FTRFS_SB(sb); + struct ftrfs_inode_info *fi =3D FTRFS_I(inode); + struct ftrfs_inode *raw; + struct buffer_head *bh; + unsigned long inodes_per_block; + unsigned long block, offset; + + inodes_per_block =3D FTRFS_BLOCK_SIZE / sizeof(struct ftrfs_inode); + block =3D le64_to_cpu(sbi->s_ftrfs_sb->s_inode_table_blk) + + (inode->i_ino - 1) / inodes_per_block; + offset =3D (inode->i_ino - 1) % inodes_per_block; + + bh =3D sb_bread(sb, block); + if (!bh) + return -EIO; + + raw =3D (struct ftrfs_inode *)bh->b_data + offset; + + raw->i_mode =3D cpu_to_le16(inode->i_mode); + raw->i_uid =3D cpu_to_le16(i_uid_read(inode)); + raw->i_gid =3D cpu_to_le16(i_gid_read(inode)); + raw->i_nlink =3D cpu_to_le16(inode->i_nlink); + raw->i_size =3D cpu_to_le64(inode->i_size); + raw->i_blocks =3D cpu_to_le32(inode->i_blocks); + raw->i_atime =3D cpu_to_le64(inode_get_atime_sec(inode) * NSEC_PER_SEC + + inode_get_atime_nsec(inode)); + raw->i_mtime =3D cpu_to_le64(inode_get_mtime_sec(inode) * NSEC_PER_SEC + + inode_get_mtime_nsec(inode)); + raw->i_ctime =3D cpu_to_le64(inode_get_ctime_sec(inode) * NSEC_PER_SEC + + inode_get_ctime_nsec(inode)); + raw->i_flags =3D cpu_to_le32(fi->i_flags); + + memcpy(raw->i_direct, fi->i_direct, sizeof(fi->i_direct)); + raw->i_indirect =3D fi->i_indirect; + raw->i_dindirect =3D fi->i_dindirect; + + raw->i_crc32 =3D 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 =3D dir->i_sb; + struct ftrfs_inode_info *fi =3D 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 =3D 0; i < FTRFS_DIRECT_BLOCKS; i++) { + block_no =3D le64_to_cpu(fi->i_direct[i]); + if (!block_no) + break; + + bh =3D sb_bread(sb, block_no); + if (!bh) + return -EIO; + + offset =3D 0; + while (offset + sizeof(*de) <=3D FTRFS_BLOCK_SIZE) { + de =3D (struct ftrfs_dir_entry *)(bh->b_data + offset); + + /* Free slot: ino =3D=3D 0 */ + if (!de->d_ino) { + de->d_ino =3D cpu_to_le64(ino); + de->d_name_len =3D name->len; + de->d_file_type =3D file_type; + de->d_rec_len =3D cpu_to_le16( + sizeof(struct ftrfs_dir_entry)); + memcpy(de->d_name, name->name, name->len); + de->d_name[name->len] =3D '\0'; + mark_buffer_dirty(bh); + brelse(bh); + inode_set_mtime_to_ts(dir, + current_time(dir)); + mark_inode_dirty(dir); + return 0; + } + offset +=3D le16_to_cpu(de->d_rec_len); + if (!de->d_rec_len) + break; + } + brelse(bh); + } + + /* Need a new block */ + if (i >=3D FTRFS_DIRECT_BLOCKS) + return -ENOSPC; + + block_no =3D ftrfs_alloc_block(sb); + if (!block_no) + return -ENOSPC; + + bh =3D sb_bread(sb, block_no); + if (!bh) { + ftrfs_free_block(sb, block_no); + return -EIO; + } + + memset(bh->b_data, 0, FTRFS_BLOCK_SIZE); + + de =3D (struct ftrfs_dir_entry *)bh->b_data; + de->d_ino =3D cpu_to_le64(ino); + de->d_name_len =3D name->len; + de->d_file_type =3D file_type; + de->d_rec_len =3D cpu_to_le16(sizeof(struct ftrfs_dir_entry)); + memcpy(de->d_name, name->name, name->len); + de->d_name[name->len] =3D '\0'; + + mark_buffer_dirty(bh); + brelse(bh); + + fi->i_direct[i] =3D cpu_to_le64(block_no); + dir->i_size +=3D 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 =3D dir->i_sb; + struct ftrfs_inode_info *fi =3D FTRFS_I(dir); + struct ftrfs_dir_entry *de; + struct buffer_head *bh; + unsigned int offset; + u64 block_no; + int i; + + for (i =3D 0; i < FTRFS_DIRECT_BLOCKS; i++) { + block_no =3D le64_to_cpu(fi->i_direct[i]); + if (!block_no) + break; + + bh =3D sb_bread(sb, block_no); + if (!bh) + return -EIO; + + offset =3D 0; + while (offset + sizeof(*de) <=3D FTRFS_BLOCK_SIZE) { + de =3D (struct ftrfs_dir_entry *)(bh->b_data + offset); + + if (de->d_ino && + de->d_name_len =3D=3D 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 +=3D 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 =3D dir->i_sb; + struct inode *inode; + struct ftrfs_inode_info *fi; + u64 ino; + + ino =3D ftrfs_alloc_inode_num(sb); + if (!ino) + return ERR_PTR(-ENOSPC); + + inode =3D new_inode(sb); + if (!inode) + return ERR_PTR(-ENOMEM); + + inode_init_owner(&nop_mnt_idmap, inode, dir, mode); + inode->i_ino =3D ino; + inode->i_blocks =3D 0; + inode->i_size =3D 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 =3D FTRFS_I(inode); + memset(fi->i_direct, 0, sizeof(fi->i_direct)); + fi->i_indirect =3D 0; + fi->i_dindirect =3D 0; + fi->i_flags =3D 0; + + if (S_ISDIR(mode)) { + inode->i_op =3D &ftrfs_dir_inode_operations; + inode->i_fop =3D &ftrfs_dir_operations; + set_nlink(inode, 2); + } else { + inode->i_op =3D &ftrfs_file_inode_operations; + inode->i_fop =3D &ftrfs_file_operations; + set_nlink(inode, 1); + } + + insert_inode_hash(inode); + mark_inode_dirty(inode); + return ERR_CAST(inode); +} + +/* ------------------------------------------------------------------ */ +/* create =E2=80=94 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 =3D ftrfs_new_inode(dir, mode | S_IFREG); + if (IS_ERR(inode)) + return PTR_ERR(inode); + + ret =3D ftrfs_write_inode_raw(inode); + if (ret) + goto out_iput; + + ret =3D ftrfs_add_dirent(dir, &dentry->d_name, inode->i_ino, 1 /* DT_REG = */); + if (ret) + goto out_iput; + + ret =3D ftrfs_write_inode_raw(dir); + if (ret) + goto out_iput; + + d_instantiate(dentry, inode); + return 0; + +out_iput: + iput(inode); + return ret; +} + +/* ------------------------------------------------------------------ */ +/* mkdir =E2=80=94 create a directory = */ +/* ------------------------------------------------------------------ */ + +static struct dentry *ftrfs_mkdir(struct mnt_idmap *idmap, struct inode *d= ir, + struct dentry *dentry, umode_t mode) +{ + struct inode *inode; + int ret; + + inode_inc_link_count(dir); + + inode =3D ftrfs_new_inode(dir, mode | S_IFDIR); + if (IS_ERR(inode)) { + inode_dec_link_count(dir); + return ERR_CAST(inode); + } + + /* Add . and .. entries */ + ret =3D ftrfs_add_dirent(inode, &(struct qstr)QSTR_INIT(".", 1), + inode->i_ino, 4 /* DT_DIR */); + if (ret) + goto out_fail; + + ret =3D ftrfs_add_dirent(inode, &(struct qstr)QSTR_INIT("..", 2), + dir->i_ino, 4 /* DT_DIR */); + if (ret) + goto out_fail; + + ret =3D ftrfs_write_inode_raw(inode); + if (ret) + goto out_fail; + + ret =3D ftrfs_add_dirent(dir, &dentry->d_name, inode->i_ino, + 4 /* DT_DIR */); + if (ret) + goto out_fail; + + ret =3D 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 =E2=80=94 remove a file = */ +/* ------------------------------------------------------------------ */ + +static int ftrfs_unlink(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode =3D d_inode(dentry); + int ret; + + ret =3D 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 =E2=80=94 remove an empty directory = */ +/* ------------------------------------------------------------------ */ + +static int ftrfs_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode =3D d_inode(dentry); + int ret; + + if (inode->i_nlink > 2) + return -ENOTEMPTY; + + ret =3D 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 =E2=80=94 create a hard link = */ +/* ------------------------------------------------------------------ */ + +static int ftrfs_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *dentry) +{ + struct inode *inode =3D d_inode(old_dentry); + int ret; + + inode_set_ctime_to_ts(inode, current_time(inode)); + inode_inc_link_count(inode); + + ret =3D 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 =E2=80=94 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 =E2=80=94 exported = */ +/* ------------------------------------------------------------------ */ + +const struct inode_operations ftrfs_dir_inode_operations =3D { + .lookup =3D ftrfs_lookup, + .create =3D ftrfs_create, + .mkdir =3D ftrfs_mkdir, + .unlink =3D ftrfs_unlink, + .rmdir =3D ftrfs_rmdir, + .link =3D ftrfs_link, +}; --=20 2.52.0 From nobody Sat Jun 20 15:19:29 2026 Received: from mail-wm1-f46.google.com (mail-wm1-f46.google.com [209.85.128.46]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BD621395240 for ; Mon, 13 Apr 2026 21:06:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776114393; cv=none; b=ia0id1Mu1VtQ/d00V0zPZE+hr6Qi912reyV2dfMqZ05kO6pqn7HTSPK2DvPxRa08Zn7XVy+8ienKFVXkjCD1RHFLWX0YBA2RX7BBK5reafzgvqloEj5tXwyr+PuRtfWK6m5XfQCzLcSIzr27YKKUEXYRb2UrhixuhfBwZb2to90= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776114393; c=relaxed/simple; bh=y3X4Q1wuaiYro/3UQY7swAXd4RhfbzRFT9BkiQ88xho=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=FK8iuBK+knA5fx3PvYZHF6WtO2M31t36o5b3welPhNRhYi31pMs5WP4Sm3ZMcLFLiF3QNmyI4ojE6cAM3fRF9JBaLX7UES3xgQlmHPxKWJ4Ul9EYrIEPasTR9TD+LEq+eT9IgPITlBT7W7zqb2npWeuOW2qsz+9RjfEDFcCWEXI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hackers.camp; spf=pass smtp.mailfrom=gmail.com; arc=none smtp.client-ip=209.85.128.46 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hackers.camp Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-wm1-f46.google.com with SMTP id 5b1f17b1804b1-4889d0a9df0so5844115e9.3 for ; Mon, 13 Apr 2026 14:06:29 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776114388; x=1776719188; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=tDFE/x9etytc+EWBgqwOsTxdVZ9Oqe0zFdU+mEte1zQ=; b=fJTBWdHcItW2FBfpi0FbJMF6CX30YS8i7NuSi7wxJXY4hvuFPxtzVEKq3EbSLOghWm jqcq6ISto4tcQ72W0CyYNP+mqQ4W1WulHBSBYOHuNdKkLh+wOXnsKa68YdW/XAgx9GGB DUr2t44XL9Jc7yhe1W8QtyWn/EUOP5RnTUD/dSNY7UdhzkBBEaA1GoO7HsEsk0n6JdBO 7I/SzThdm6kwh6zE8w0oj05YfDC8fwDbIF0WnybHs7nh1mpW7k1qcCaoTGNeHEUZDD1F 5zezp+hTWDNDcoj4lLyopLdqGRZgE3/dgrQ4GA3MEyje+TOodWUz0hwnqOoelG2wuM1t oE5w== X-Forwarded-Encrypted: i=1; AFNElJ+eW4vAySqWGV4KkpZqH/+1hod3NQdG6A7xL8DQW+ydZwQ2j0oft3AHegeDJLUfvfff9psUIKuNhYmH5vQ=@vger.kernel.org X-Gm-Message-State: AOJu0Yyi5U1E4GTaVu2AO/9mxfebmkC+yMNfjDTchqoL24CB9pBPFt9p jRlixET8NfFib4fy4KAYyMLlCpFKRsf7Ckf4GGVORKLKJb5USCmpInuv X-Gm-Gg: AeBDievVmuunCb8xa9mDhwsmOMPXVVpx+ucr6LOXQm1ftvZBwfMckzla17pLha1DutD yb3Vl9+s+pEnMkpE8ZqJN5m5R84AVLidf7w6tn9IUX+hH3Bg6vPnqefEsSbHj6SsTSsOekWz9uP ixID6YIO+HJQ5cdotwlhEC2N7b1dXVfCH9ZYPLow8mcLX5qs7pejwcwNHgOIqrMnOQjRHgdSD0P /1sArOJep2aN5ZnFblb9ySTRJPuATU/yDpxe/eFJ4mHJvz0B6lYC+o9UJpYhWkPh3jRi80KI9Lq 3vIm+9ImGesy9xLCOYILQqHYEbsqo3JH762HZOMxBN5rQXiHqYkmknus+9ePriUaxR+c8jMW9rJ H7O7wM4O2UjCiOyjrOI1oemPN/txa6zpDWnzQmSgF5KtG4PLWsbIp9c7HqaTwZAaYSNwJsLIohU 65Hn75AsPwNQxUTxvSo0kliLkJs5MPY0fr/jD09jTvy78JaOdmHkF/hR0jZwAVeXFxbCeHLyY3d bV0 X-Received: by 2002:a05:6000:161a:b0:43d:55b3:a200 with SMTP id ffacd0b85a97d-43d642c97e0mr6616599f8f.4.1776114387967; Mon, 13 Apr 2026 14:06:27 -0700 (PDT) Received: from spartian-1.home ([2a01:cb1c:784:2f00:708:2805:7128:7a75]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43d63e5c98fsm35130020f8f.35.2026.04.13.14.06.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 13 Apr 2026 14:06:27 -0700 (PDT) From: Aurelien DESBRIERES To: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Aurelien DESBRIERES , viro@zeniv.linux.org.uk, brauner@kernel.org, willy@infradead.org, djwong@kernel.org, adilger@dilger.ca, pfalcato@suse.de Subject: [PATCH v2 08/11] ftrfs: add CRC32 checksumming and Reed-Solomon FEC skeleton Date: Tue, 14 Apr 2026 01:05:49 +0200 Message-ID: <20260413230601.525400-9-aurelien@hackers.camp> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260413230601.525400-1-aurelien@hackers.camp> References: <20260413230601.525400-1-aurelien@hackers.camp> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable 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: Aurelien DESBRIERES --- 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 =E2=80=94 EDAC layer: CRC32 + Reed-Solomon FEC + * Author: roastercode - Aurelien DESBRIERES + */ + +#include +#include +#include +#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 =3D 1; + + for (int i =3D 0; i < 255; i++) { + gf_exp[i] =3D x; + gf_log[x] =3D i; + x =3D (x << 1) ^ ((x & 0x80) ? 0x1d : 0); + } + gf_exp[255] =3D gf_exp[0]; + rs_initialized =3D true; +} + +/* Galois Field multiplication */ +static uint8_t gf_mul(uint8_t a, uint8_t b) +{ + if (a =3D=3D 0 || b =3D=3D 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 =3D 0; i < FTRFS_SUBBLOCK_DATA; i++) { + uint8_t feedback =3D gf_mul(msg[i], gf_exp[FTRFS_RS_PARITY]); + + if (feedback !=3D 0) { + for (int j =3D 1; j <=3D FTRFS_RS_PARITY; j++) + msg[FTRFS_SUBBLOCK_DATA + j - 1] ^=3D 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; +} --=20 2.52.0 From nobody Sat Jun 20 15:19:29 2026 Received: from mail-wm1-f52.google.com (mail-wm1-f52.google.com [209.85.128.52]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B00E8396B70 for ; Mon, 13 Apr 2026 21:06:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.52 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776114395; cv=none; b=N6AMoev66lIqUwdka+TkQxfaF0nqfo1k53jmVm62ubxNxSd3mFE2IGNozAd16LeZE3VGfVaaDXDBeR/huJF2BkdKRQhy4ap5JsJPw+O6gMGFZsXILlD/bk8XtDBh7uUHCKMEe4zgz03b0w+vABghZ49vbw+09/R7rk7Iu1NJfmg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776114395; c=relaxed/simple; bh=NM6h5VZen3O0KT+su0g5zHL4pT4XZpgSFwSPr2NM+A8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=m9rwLvJgzbThwqhAgXEq+Qf5crqm7xRloLUHiOg+bRJgTs8X/KmkFjumIG84Tq7P53cXM44K4FYmyCZ+FIuyP8UlzNfox8+GJ42z1h99A/vOtHqh/r2dWMw8vMuvGoX9l5GVIDEFjgJqRQYfXLCw7KhcfPLXEhCYZgqF4rnbzVc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hackers.camp; spf=pass smtp.mailfrom=gmail.com; arc=none smtp.client-ip=209.85.128.52 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hackers.camp Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-wm1-f52.google.com with SMTP id 5b1f17b1804b1-4836fc075d2so6383845e9.0 for ; Mon, 13 Apr 2026 14:06:30 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776114389; x=1776719189; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=YJ4L10geo68HXUJMfGUEOz6mGrj4VKfjz9eMj3DzSKw=; b=qFJFaXKp1T/wZ6QzzunpSPtVw0bwsPcMQalZP1E4UQOLjLBpTIMtXb+JnZ713Ea+iD L+2h2mrr86/SGbDqot7R0OBYoiwnLg2VQWij+C9GpL5a2bfdAnuydseyDJIWbgerI8xp acEVT+7pYbTq2OE1sHTkgQztnRPfnt3cVPSOM/MK5NE9wEtdUg3o+Ar493d9Hi3Ekhot 9pA0AqoyVcbKTtctrppg6z8BOkO3hvx2GeRZGDo2Xn8gfTpFc3wmWovhfcwF90v1Aai2 jiUmO8cA9kTk/tJHTFzP2buEjj6STzxdkc/mj5hnGF4dL/+S8WuVDmQ8f8WxtVbe4FeX 9O8g== X-Forwarded-Encrypted: i=1; AFNElJ/rYm1wqaq8b6dp4KRllKwKAu/T+80wXXkKsRe9gM2AbEmt0QhMynEiJ4k0fF2OOqcpB1kwj2zTO9oW9Ek=@vger.kernel.org X-Gm-Message-State: AOJu0YzYGjZuDiCdt8cKwfY9lmScPuvuzk3MrvgdNgoG0VWLBOY47EeV cjmXoyLQPNqGL/nyy0ZSok5FckJDbPn77mbVKLr9/fhZEp4tifOo5nDE X-Gm-Gg: AeBDiesFUrtH+VD6VAfkd805WnuTbBjAT1Jy6rJj4dQEQcAOrib3gsx7NLpkhpjzIlj FovGB+RshTMutO5D9SGTR3nzWk8ZyLqhHLYwGtSaTFK7tsm83maDRGk7yD2cy6DUR5zaXYsuQmw IzwoNJE7cgXE4nfmHufiAvV8tUmn6fpSuwTezaw+Y07YxG5MELYJVStA00q203VItsinUjy7ige QAjjxzXT5PO1nz9dA0b+q9qbTOWy7KY3TDIiZoB60rfm2w8qNttaZ91jh6/YRKy0LGy+6Zq1GKi XO00w0zg1kJXBEJrREIWAzk/BZLTuqllXpOjNy5d5AYBn2Hr4M3988aCGuLtbfxBguQkJU38K4C DKW8/SAkzXxbCsef8brf+1cDBqh3I7RLOAscEfe1vKUJ4Zepp3tNRnFIuDLdvnExmS41DKGLJwF kPQ91jLrgRjhjL4hdWcoILcWTLRSBX+lqOyFeT45jse8NiO6HQSu5xmy55VCcLj8UAYGO28ZMZ5 sxq X-Received: by 2002:a05:600c:46c8:b0:488:9e7e:e1e3 with SMTP id 5b1f17b1804b1-488d67f5115mr115396145e9.2.1776114388943; Mon, 13 Apr 2026 14:06:28 -0700 (PDT) Received: from spartian-1.home ([2a01:cb1c:784:2f00:708:2805:7128:7a75]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43d63e5c98fsm35130020f8f.35.2026.04.13.14.06.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 13 Apr 2026 14:06:28 -0700 (PDT) From: Aurelien DESBRIERES To: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Aurelien DESBRIERES , viro@zeniv.linux.org.uk, brauner@kernel.org, willy@infradead.org, djwong@kernel.org, adilger@dilger.ca, pfalcato@suse.de Subject: [PATCH v2 09/11] ftrfs: add Kconfig, Makefile and fs/ tree integration Date: Tue, 14 Apr 2026 01:05:50 +0200 Message-ID: <20260413230601.525400-10-aurelien@hackers.camp> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260413230601.525400-1-aurelien@hackers.camp> References: <20260413230601.525400-1-aurelien@hackers.camp> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable 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) +=3D ftrfs/ Signed-off-by: Aurelien DESBRIERES --- 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 =E2=80=94 Fault-Tolerant Radiation-Robust Filesystem +# + +obj-$(CONFIG_FTRFS_FS) +=3D ftrfs.o + +ftrfs-y :=3D super.o \ + inode.o \ + dir.o \ + file.o \ + edac.o \ + alloc.o \ + namei.o + +ftrfs-$(CONFIG_FTRFS_FS_XATTR) +=3D xattr.o + +ifneq ($(KERNELRELEASE),) +else + +ifneq ($(KERNEL_SRC),) + KERNELDIR :=3D $(KERNEL_SRC) +else + KERNELDIR ?=3D /lib/modules/$(shell uname -r)/build +endif + +ifneq ($(O),) + KBUILD_OUTPUT :=3D O=3D$(O) +else + KBUILD_OUTPUT :=3D +endif + +PWD :=3D $(shell pwd) + +all: + $(MAKE) -C $(KERNELDIR) $(KBUILD_OUTPUT) M=3D$(PWD) \ + CONFIG_FTRFS_FS=3Dm CONFIG_FTRFS_FS_XATTR=3Dn CONFIG_FTRFS_FS_SECURITY= =3Dn \ + modules + +clean: + $(MAKE) -C $(KERNELDIR) $(KBUILD_OUTPUT) M=3D$(PWD) clean + +modules_install: + $(MAKE) -C $(KERNELDIR) $(KBUILD_OUTPUT) M=3D$(PWD) modules_install + +endif --=20 2.52.0 From nobody Sat Jun 20 15:19:29 2026 Received: from mail-wm1-f44.google.com (mail-wm1-f44.google.com [209.85.128.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C3B8A39A050 for ; Mon, 13 Apr 2026 21:06:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776114401; cv=none; b=kA0TihlO0GmL3gMrPl07C2+NzPiS/414r7WF0TJABSxzosYSJ3epE5uxTJddDwkhETxRQre28wzCip5onnEurHUHvz5eRn1t2bcQA0zf5LYB40WioheC5j5BXL8xsNbq/W3Sh/w8fSDAICGXLJNN/ud6qlTfuU/dg51Y3oH5JN4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776114401; c=relaxed/simple; bh=gofNGxhmstUnqml+TbutIimfWDHAbpz1kCzS4nwkX6M=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=gx5ulRe2wPR81MkrH2fNhUVz0k/y/RiUu6+E2eGRdaEkHGFTOBiB14Hiv1oz2ScFc15l2G3dXaNYugx/fwS/6u+Ll+K+OYcaG8vyj6brSAKrwS/JWcCw67lAImEMyTCAx/GY7ELG1QQhZ5BM/Ca6yd7ltNamJ1qtR90x08GnxWc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hackers.camp; spf=pass smtp.mailfrom=gmail.com; arc=none smtp.client-ip=209.85.128.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hackers.camp Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-wm1-f44.google.com with SMTP id 5b1f17b1804b1-488c1f446f3so7713145e9.0 for ; Mon, 13 Apr 2026 14:06:35 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776114390; x=1776719190; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=u6+sKOyLCVa1YoFPa9BtTOYsyUICssksqaNS8/FnLtE=; b=J++J6r4H9vPArLXJlmMJnShxnlgZnT2VKvlrGvq7Yhqn4WeRMaQCcfQ9DrvoeV50Gq JrlN1MYD8b5ASNSe8ardDBjCuRVCkjjovgocWZ6/5K7quL2p3BUBFMtl2o/Gs7ojZAZ8 EhxpnSwZFtcMTSB0lJlZr2A3pbXboGk79+Hik9WyaGaEN75N45oSsbnZmjVIJxYdazR9 brMX5mVpLcTmB/4XqtrKynpldh967Jm+bprUPWiqGlwEayszcP3mVku4Ml/NXx5GLAvB jhq86rVje+fSSv3Po1nVmA+xdQsUfL/WC4VCZShsrvOM4UetxVnTQKO0Ah9XsCAWRWQF Fscw== X-Forwarded-Encrypted: i=1; AFNElJ8DimBPLoGhrGQkqQ64W7C9Uz+XmVZ+8xdir4iUJpl52FXs/82mq/6zrfxaAr/GY94u8T1RE8zUvzh+OPk=@vger.kernel.org X-Gm-Message-State: AOJu0YwXeH0WDxt8t0qTez+28nT1ynFtVNxY0O9D/vscJkzvV1Zs+aja 6JHMhTmm3xpRSISWZJo6KbEbOpyPkLmZSLy21n5PEXgu9rp7bt+yCuqf X-Gm-Gg: AeBDievye87Dxb1PjkPwMZ/SiapVh8J4FaRnTnuKQt4qMigiMqj5ZzapaV+ra4sErzD VXcV5PG+Y6VY+WK2kEVtqOtg9nub84sh1bna6bWN6bMYokg9LRDUXGByIRIocpHv2yONxSUGSE1 /Si5DY6iH7FWHkhYMmEUCPZDIsk2fzlvbS3E5JTEYJRvSWvMuYk+REo/KYaJfhmRbn5w/am84tU PYzF/oYf+HOxcNL22wruFr1bDvAqQAdO+tv3nmEXau/IYAvaWiM+Ka/UKymaZuRJ8Y46pj0Mhfe BtE8AN3TWGka9Owcl2poxCWHmyfApMIBL/jqd0C3P0iyLRgcFQoO6G3hnqshQWXM6PmsZZsIBbi fUUibDmQlYhhLF/mcFZYr4oMsc80QkGY4xeP8u0eTxoePJbBro4zKNOkMrTGmTEjqGiMrMuYOl9 xmj1B+tN1cbiq7dumgIX9WJ1HU4svVDRMHbflKHu+x8pP5h/KKsYT9C2zwMbKYyXNd7rNwcd7lo kQ7 X-Received: by 2002:a5d:6e48:0:b0:43d:6a00:6275 with SMTP id ffacd0b85a97d-43d6a0062cemr5469388f8f.4.1776114390140; Mon, 13 Apr 2026 14:06:30 -0700 (PDT) Received: from spartian-1.home ([2a01:cb1c:784:2f00:708:2805:7128:7a75]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43d63e5c98fsm35130020f8f.35.2026.04.13.14.06.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 13 Apr 2026 14:06:29 -0700 (PDT) From: Aurelien DESBRIERES To: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Aurelien DESBRIERES , viro@zeniv.linux.org.uk, brauner@kernel.org, willy@infradead.org, djwong@kernel.org, adilger@dilger.ca, pfalcato@suse.de Subject: [PATCH v2 10/11] MAINTAINERS: add entry for FTRFS filesystem Date: Tue, 14 Apr 2026 01:05:51 +0200 Message-ID: <20260413230601.525400-11-aurelien@hackers.camp> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260413230601.525400-1-aurelien@hackers.camp> References: <20260413230601.525400-1-aurelien@hackers.camp> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Signed-off-by: Aurelien DESBRIERES --- 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 =20 +FTRFS FILE SYSTEM +M: Aur=C3=A9lien DESBRIERES +L: linux-fsdevel@vger.kernel.org +S: Maintained +F: fs/ftrfs/ + EXT2 FILE SYSTEM M: Jan Kara L: linux-ext4@vger.kernel.org --=20 2.52.0 From nobody Sat Jun 20 15:19:29 2026 Received: from mail-wm1-f42.google.com (mail-wm1-f42.google.com [209.85.128.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F02E539657D for ; Mon, 13 Apr 2026 21:06:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.42 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776114397; cv=none; b=d/x9vbOdPIba5lf2OqBATZu+T5wO23WqBn21L/22JEGQeUlzRprJduGlOsggJ5L6Fg1vO+Go+AEQ01SjG7xmfa3OT1YSgeQQi7v0Ku3Qs/UzQhEndy4uqgDwyo8Qou8a1bMYmhmF77sxkqvmJjjkRMNQbNnSnSaDrkYnxflzQfY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776114397; c=relaxed/simple; bh=TyU6+ssBtqPgFmfmLc9euL6NS91bHT4L+VKtdARJXGs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=sgqqXDGlavrSD+tlXEPrX5vBIVVZLxE5Ec/8xWG6mQRhcAKzOnM7AIya3pEwCRsplJ2XJHS07Z5+vItU7Kvk2KAXgtOrrbO2RNGyuNMq3kf4RwC6K6xLFfpsqO6vAokGbxniYgPcCuBhkRylVwsuBnrhs/ay4CKmUgmmS+m62lc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hackers.camp; spf=pass smtp.mailfrom=gmail.com; arc=none smtp.client-ip=209.85.128.42 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hackers.camp Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-wm1-f42.google.com with SMTP id 5b1f17b1804b1-4830b67aa6bso7020195e9.0 for ; Mon, 13 Apr 2026 14:06:34 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776114391; x=1776719191; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=gJQ66QUToDidVWPBgx6Nn5WDzL1O5AXEnR6bvMEbjR8=; b=j2CmYelrb/uAuqEeQTm9rgxremjLXSdjklztNSpMTnBlmjr2Vjy1PVXvjTIAsIYI0x bHzHoSRC/t+rddkGdmXXJ2gErQOBDmcWSede2YEhbPuJmmOHesH/LcGGeZ1KgOR+2ryd 6RCGkP+Sl7Dx34paJ8Txm4IZ9zTylUtDe8QCpZL+v6WzERFrhUYhNfG1zFI5oUAS7C+6 /+WM+7BQGGFfNlVgERwsc8aUVHsXIHYOHVaBxU0i8CXEzEHqeuO+/4BuSQSLF/UPEm8x uPjJ3Tw072owVbttrCtSHeo6YglQ3ZLaMyuQrZx89EU2xOTJzPb78em5VhtLf5iw/tR/ V6wA== X-Forwarded-Encrypted: i=1; AFNElJ8kewK68lbCzYQ9ms9xfy5AkG979vxUm/FwxeotpJq5nfU7SC0I1AJpuZBJR+J+VA9RfgedRlwV6s9mHVc=@vger.kernel.org X-Gm-Message-State: AOJu0Yzd+zeSoPDqFXuw7bWCKbzWHtL+RsL4LSps8tKnWb2bWm1tVq2b YSe8oR1Lec4gFFg/X0gFAr/Ysa0vnUkkgVMISi1zXnmN7Gl1kQbC1q4r X-Gm-Gg: AeBDiesi5pjopQk/TtPOn2C50k5DO/prc5pkK7edlpQqydiz3FS9emx5ah3mnfSl9+U pLZ9HXUwIjGeeFYJWnYw0PHewuIv02BoAAdx5lrFZao8cynZtPvjx/QS2fGrQcYNbY30y0WVXWh sZH8tSRF6pryZUKxh/IU+/nl37yQhS/AvieF/w/7zalhYyen/Ukn9ZTv65Mhk/kqwKo/pWx2kfN z7mWA1nIp498W3LyjmMyO6RyDKRjZb/UGTFKpj57E1Vl0gw2mqUtYe3oD7Vl57MQI7zL5AMLkqf rfIWs/DPsZS7ws01mkvRUCkr5G72MA8WTpDXAALau6nwpXyU8K8cxpJ+GxdtjNEJMoS88kKKgMT 8bez7UGDTSBIPIOpimO3VCDWRNS5lvHeRK+U9WvPtN6yRDnG8jhpMX9hICIA5GFBSKvohdxaHEv XmA9/hsXgxSbZRDBi/AK+Zr/MOV5XpFNZFtqWYldrvhivfetv0EG9VrBTEL6m1fBlFGARshL3gz /dg X-Received: by 2002:a05:600c:c4a2:b0:485:3bc7:a224 with SMTP id 5b1f17b1804b1-488d68609e0mr113121175e9.6.1776114391341; Mon, 13 Apr 2026 14:06:31 -0700 (PDT) Received: from spartian-1.home ([2a01:cb1c:784:2f00:708:2805:7128:7a75]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43d63e5c98fsm35130020f8f.35.2026.04.13.14.06.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 13 Apr 2026 14:06:30 -0700 (PDT) From: Aurelien DESBRIERES To: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Aurelien DESBRIERES , viro@zeniv.linux.org.uk, brauner@kernel.org, willy@infradead.org, djwong@kernel.org, adilger@dilger.ca, pfalcato@suse.de Subject: [PATCH v2 11/11] =?UTF-8?q?ftrfs:=20v2=20fixes=20=E2=80=94=20writ?= =?UTF-8?q?e=20path,=20inode=20lifecycle,=20on-disk=20format?= Date: Tue, 14 Apr 2026 01:05:52 +0200 Message-ID: <20260413230601.525400-12-aurelien@hackers.camp> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260413230601.525400-1-aurelien@hackers.camp> References: <20260413230601.525400-1-aurelien@hackers.camp> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable address_space_operations: - Implement ftrfs_get_block with block allocation (create=3D1) - Return -EIO for holes on read (create=3D0), not 0 - Set bh_result->b_size =3D 1 << i_blkbits on all mapped paths - Add ftrfs_write_begin, ftrfs_write_end, ftrfs_readahead, ftrfs_bmap - Add dirty_folio, invalidate_folio to ftrfs_aops Inode lifecycle: - Use insert_inode_locked() instead of insert_inode_hash() new_inode() does not set I_NEW; insert_inode_locked() does - Move unlock_new_inode() to callers after d_instantiate() - Add unlock_new_inode() on error paths On-disk format: - ftrfs_inode: 128 -> 256 bytes - uid/gid: __le16 -> __le32 (standard kernel convention) - Add i_tindirect: triple indirect (~512 GiB max file size) - Remove i_blocks: redundant, calculable from i_size - i_reserved[84]: explicit padding to 256 bytes - Superblock padding: 3948 -> 3980 bytes (enforce 4096) - Add BUILD_BUG_ON for both structure sizes Directory: - Skip . and .. in readdir data blocks (dir_emit_dots emits them) Compat: - Add ftrfs_inode_is_new macro for inode_state_read_once API Tested on arm64 kernel 7.0-rc7 (Yocto KVM): - mount, write, mkdir, read: all working - 0 BUG/WARN/Oops in dmesg Addresses review feedback from: - Matthew Wilcox: address_space_operations now implemented - Darrick J. Wong: i_size __le64 intentional, BUILD_BUG_ON documents limits - Andreas Dilger: DO-178C/ECSS certification rationale documented Signed-off-by: Aurelien DESBRIERES --- fs/ftrfs/dir.c | 88 +++++++++++++++++++++----------------------- fs/ftrfs/file.c | 95 +++++++++++++++++++++++++++++++++++++++++++++--- fs/ftrfs/ftrfs.h | 35 +++++++++++++----- fs/ftrfs/inode.c | 9 +++-- fs/ftrfs/namei.c | 21 +++++++---- fs/ftrfs/super.c | 2 + 6 files changed, 177 insertions(+), 73 deletions(-) diff --git a/fs/ftrfs/dir.c b/fs/ftrfs/dir.c index dbf0102a4..fd06910bf 100644 --- a/fs/ftrfs/dir.c +++ b/fs/ftrfs/dir.c @@ -3,7 +3,6 @@ * FTRFS =E2=80=94 Directory operations * Author: roastercode - Aurelien DESBRIERES */ - #include #include #include "ftrfs.h" @@ -13,24 +12,23 @@ */ static int ftrfs_readdir(struct file *file, struct dir_context *ctx) { - struct inode *inode =3D file_inode(file); - struct super_block *sb =3D inode->i_sb; + struct inode *inode =3D file_inode(file); + struct super_block *sb =3D inode->i_sb; struct ftrfs_inode_info *fi =3D FTRFS_I(inode); struct buffer_head *bh; struct ftrfs_dir_entry *de; unsigned long block_idx, block_no; - unsigned int offset; + unsigned int offset; =20 - /* EOF guard */ if (ctx->pos =3D=3D INT_MAX) return 0; + /* Emit . and .. (ctx->pos: 0=3D., 1=3D.., 2+=3Dreal entries) */ if (ctx->pos < 2) { if (!dir_emit_dots(file, ctx)) return 0; } =20 - /* Iterate over direct blocks only (skeleton: no indirect yet) */ for (block_idx =3D 0; block_idx < FTRFS_DIRECT_BLOCKS; block_idx++) { block_no =3D le64_to_cpu(fi->i_direct[block_idx]); if (!block_no) @@ -43,25 +41,28 @@ static int ftrfs_readdir(struct file *file, struct dir_= context *ctx) offset =3D 0; while (offset < FTRFS_BLOCK_SIZE) { de =3D (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++; + break; + + /* Skip . and .. =E2=80=94 emitted by dir_emit_dots */ + if (!de->d_ino || !de->d_name_len) + goto next; + if (de->d_name_len =3D=3D 1 && de->d_name[0] =3D=3D '.') + goto next; + if (de->d_name_len =3D=3D 2 && de->d_name[0] =3D=3D '.' + && de->d_name[1] =3D=3D '.') + goto next; + + 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++; +next: offset +=3D le16_to_cpu(de->d_rec_len); } - brelse(bh); } =20 @@ -73,22 +74,19 @@ static int ftrfs_readdir(struct file *file, struct dir_= context *ctx) * ftrfs_lookup =E2=80=94 find dentry in directory */ struct dentry *ftrfs_lookup(struct inode *dir, - struct dentry *dentry, - unsigned int flags) + struct dentry *dentry, + unsigned int flags) { - struct super_block *sb =3D dir->i_sb; + struct super_block *sb =3D dir->i_sb; struct ftrfs_inode_info *fi =3D FTRFS_I(dir); - struct buffer_head *bh; - struct ftrfs_dir_entry *de; - struct inode *inode =3D NULL; - unsigned long block_idx, block_no; - unsigned int offset; - - if (dentry->d_name.len > FTRFS_MAX_FILENAME) - return ERR_PTR(-ENAMETOOLONG); + struct ftrfs_dir_entry *de; + struct buffer_head *bh; + unsigned int offset; + unsigned long block_no; + int i; =20 - for (block_idx =3D 0; block_idx < FTRFS_DIRECT_BLOCKS; block_idx++) { - block_no =3D le64_to_cpu(fi->i_direct[block_idx]); + for (i =3D 0; i < FTRFS_DIRECT_BLOCKS; i++) { + block_no =3D le64_to_cpu(fi->i_direct[i]); if (!block_no) break; =20 @@ -97,30 +95,28 @@ struct dentry *ftrfs_lookup(struct inode *dir, continue; =20 offset =3D 0; - while (offset < FTRFS_BLOCK_SIZE) { + while (offset + sizeof(*de) <=3D FTRFS_BLOCK_SIZE) { de =3D (struct ftrfs_dir_entry *)(bh->b_data + offset); - if (!de->d_rec_len) - break; /* end of dir block */ - + break; if (de->d_ino && de->d_name_len =3D=3D dentry->d_name.len && !memcmp(de->d_name, dentry->d_name.name, - de->d_name_len)) { - unsigned long ino =3D le64_to_cpu(de->d_ino); + dentry->d_name.len)) { + u64 ino =3D le64_to_cpu(de->d_ino); + struct inode *inode; =20 brelse(bh); inode =3D ftrfs_iget(sb, ino); - goto found; + return d_splice_alias(inode, dentry); } - offset +=3D le16_to_cpu(de->d_rec_len); + if (!de->d_rec_len) + break; } brelse(bh); } - -found: - return d_splice_alias(inode, dentry); + return d_splice_alias(NULL, dentry); } =20 const struct file_operations ftrfs_dir_operations =3D { @@ -128,5 +124,3 @@ const struct file_operations ftrfs_dir_operations =3D { .read =3D generic_read_dir, .iterate_shared =3D ftrfs_readdir, }; - - diff --git a/fs/ftrfs/file.c b/fs/ftrfs/file.c index ef121359b..1807ac698 100644 --- a/fs/ftrfs/file.c +++ b/fs/ftrfs/file.c @@ -1,14 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * FTRFS =E2=80=94 File operations (skeleton) + * FTRFS =E2=80=94 File operations * Author: roastercode - Aurelien DESBRIERES - * - * NOTE: read/write use generic_file_* for now. - * The EDAC/RS layer will intercept at the block I/O level (next iteration= ). */ - #include #include +#include +#include #include "ftrfs.h" =20 const struct file_operations ftrfs_file_operations =3D { @@ -23,3 +21,90 @@ const struct file_operations ftrfs_file_operations =3D { const struct inode_operations ftrfs_file_inode_operations =3D { .getattr =3D simple_getattr, }; + +/* + * ftrfs_get_block =E2=80=94 map logical block to physical block + * Handles allocation when create=3D1, returns -EIO for holes on read. + */ +static int ftrfs_get_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create) +{ + struct ftrfs_inode_info *fi =3D FTRFS_I(inode); + u64 new_block; + __le64 phys; + + if (iblock >=3D FTRFS_DIRECT_BLOCKS) { + pr_err("ftrfs: indirect block not yet supported\n"); + return -EOPNOTSUPP; + } + + phys =3D fi->i_direct[iblock]; + if (phys) { + map_bh(bh_result, inode->i_sb, le64_to_cpu(phys)); + bh_result->b_size =3D 1 << inode->i_blkbits; + return 0; + } + + if (!create) + return -EIO; + + new_block =3D ftrfs_alloc_block(inode->i_sb); + if (!new_block) { + pr_err("ftrfs: no free blocks\n"); + return -ENOSPC; + } + + fi->i_direct[iblock] =3D cpu_to_le64(new_block); + map_bh(bh_result, inode->i_sb, new_block); + bh_result->b_size =3D 1 << inode->i_blkbits; + set_buffer_new(bh_result); + return 0; +} + +static int ftrfs_read_folio(struct file *file, struct folio *folio) +{ + return block_read_full_folio(folio, ftrfs_get_block); +} + +static int ftrfs_writepages(struct address_space *mapping, + struct writeback_control *wbc) +{ + return mpage_writepages(mapping, wbc, ftrfs_get_block); +} + +static void ftrfs_readahead(struct readahead_control *rac) +{ + mpage_readahead(rac, ftrfs_get_block); +} + +static int ftrfs_write_begin(const struct kiocb *iocb, + struct address_space *mapping, + loff_t pos, unsigned int len, + struct folio **foliop, void **fsdata) +{ + return block_write_begin(mapping, pos, len, foliop, ftrfs_get_block); +} + +static int ftrfs_write_end(const struct kiocb *iocb, + struct address_space *mapping, + loff_t pos, unsigned int len, unsigned int copied, + struct folio *folio, void *fsdata) +{ + return generic_write_end(iocb, mapping, pos, len, copied, folio, fsdata); +} + +static sector_t ftrfs_bmap(struct address_space *mapping, sector_t block) +{ + return generic_block_bmap(mapping, block, ftrfs_get_block); +} + +const struct address_space_operations ftrfs_aops =3D { + .read_folio =3D ftrfs_read_folio, + .readahead =3D ftrfs_readahead, + .write_begin =3D ftrfs_write_begin, + .write_end =3D ftrfs_write_end, + .writepages =3D ftrfs_writepages, + .bmap =3D ftrfs_bmap, + .dirty_folio =3D block_dirty_folio, + .invalidate_folio =3D block_invalidate_folio, +}; diff --git a/fs/ftrfs/ftrfs.h b/fs/ftrfs/ftrfs.h index 82502c9fb..1f2d8a954 100644 --- a/fs/ftrfs/ftrfs.h +++ b/fs/ftrfs/ftrfs.h @@ -13,6 +13,10 @@ #include #include =20 +/* inode_state_read_once returns inode_state_flags in kernel 7.0 */ +#define ftrfs_inode_is_new(inode) \ + (inode_state_read_once(inode) & I_NEW) + /* Magic number: 'FTRF' */ #define FTRFS_MAGIC 0x46545246 =20 @@ -49,29 +53,38 @@ struct ftrfs_super_block { __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 */ + __u8 s_pad[3980]; /* Padding to 4096 bytes */ } __packed; =20 /* * On-disk inode - * Size: 128 bytes + * Size: 256 bytes + * + * Addressing capacity: + * direct (12) =3D 48 KiB + * indirect (1) =3D 2 MiB + * dindirect (1) =3D 1 GiB + * tindirect (1) =3D 512 GiB + * + * uid/gid: __le32 to support uid > 65535 (standard kernel convention) + * timestamps: __le64 nanoseconds (required for space mission precision) */ 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 */ + __le32 i_uid; /* Owner UID */ + __le32 i_gid; /* Owner GID */ + __le64 i_size; /* File size in bytes (64-bit, future-proof) = */ __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 */ + __le32 i_crc32; /* CRC32 of inode (excluding this field) */ __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 */ + __le64 i_indirect; /* Single indirect (~2 MiB) */ + __le64 i_dindirect; /* Double indirect (~1 GiB) */ + __le64 i_tindirect; /* Triple indirect (~512 GiB) */ + __u8 i_reserved[84]; /* Padding to 256 bytes */ } __packed; =20 /* Inode flags */ @@ -111,6 +124,7 @@ struct ftrfs_inode_info { __le64 i_direct[FTRFS_DIRECT_BLOCKS]; __le64 i_indirect; __le64 i_dindirect; + __le64 i_tindirect; __u32 i_flags; struct inode vfs_inode; /* Must be last */ }; @@ -140,6 +154,7 @@ extern const struct inode_operations ftrfs_dir_inode_op= erations; /* file.c */ extern const struct file_operations ftrfs_file_operations; extern const struct inode_operations ftrfs_file_inode_operations; +extern const struct address_space_operations ftrfs_aops; =20 /* edac.c */ __u32 ftrfs_crc32(const void *buf, size_t len); diff --git a/fs/ftrfs/inode.c b/fs/ftrfs/inode.c index e1279c796..f655ccbd9 100644 --- a/fs/ftrfs/inode.c +++ b/fs/ftrfs/inode.c @@ -34,7 +34,7 @@ struct inode *ftrfs_iget(struct super_block *sb, unsigned= long ino) return ERR_PTR(-ENOMEM); =20 /* Already in cache */ - if (!(inode_state_read_once(inode) & I_NEW)) + if (!ftrfs_inode_is_new(inode)) return inode; =20 inodes_per_block =3D FTRFS_BLOCK_SIZE / sizeof(struct ftrfs_inode); @@ -63,11 +63,10 @@ struct inode *ftrfs_iget(struct super_block *sb, unsign= ed long ino) =20 /* Populate VFS inode */ inode->i_mode =3D le16_to_cpu(raw->i_mode); - inode->i_uid =3D make_kuid(sb->s_user_ns, le16_to_cpu(raw->i_uid)); - inode->i_gid =3D make_kgid(sb->s_user_ns, le16_to_cpu(raw->i_gid)); + inode->i_uid =3D make_kuid(sb->s_user_ns, le32_to_cpu(raw->i_uid)); + inode->i_gid =3D make_kgid(sb->s_user_ns, le32_to_cpu(raw->i_gid)); set_nlink(inode, le16_to_cpu(raw->i_nlink)); inode->i_size =3D le64_to_cpu(raw->i_size); - inode->i_blocks =3D le32_to_cpu(raw->i_blocks); =20 inode_set_atime(inode, le64_to_cpu(raw->i_atime) / NSEC_PER_SEC, @@ -83,6 +82,7 @@ struct inode *ftrfs_iget(struct super_block *sb, unsigned= long ino) memcpy(fi->i_direct, raw->i_direct, sizeof(fi->i_direct)); fi->i_indirect =3D raw->i_indirect; fi->i_dindirect =3D raw->i_dindirect; + fi->i_tindirect =3D raw->i_tindirect; fi->i_flags =3D le32_to_cpu(raw->i_flags); =20 /* Set ops based on file type */ @@ -92,6 +92,7 @@ struct inode *ftrfs_iget(struct super_block *sb, unsigned= long ino) } else if (S_ISREG(inode->i_mode)) { inode->i_op =3D &ftrfs_file_inode_operations; inode->i_fop =3D &ftrfs_file_operations; + inode->i_mapping->a_ops =3D &ftrfs_aops; } else { /* Special files: use generic */ init_special_inode(inode, inode->i_mode, 0); diff --git a/fs/ftrfs/namei.c b/fs/ftrfs/namei.c index a8c1f79eb..d37ad8b7b 100644 --- a/fs/ftrfs/namei.c +++ b/fs/ftrfs/namei.c @@ -38,11 +38,10 @@ static int ftrfs_write_inode_raw(struct inode *inode) raw =3D (struct ftrfs_inode *)bh->b_data + offset; =20 raw->i_mode =3D cpu_to_le16(inode->i_mode); - raw->i_uid =3D cpu_to_le16(i_uid_read(inode)); - raw->i_gid =3D cpu_to_le16(i_gid_read(inode)); + raw->i_uid =3D cpu_to_le32(i_uid_read(inode)); + raw->i_gid =3D cpu_to_le32(i_gid_read(inode)); raw->i_nlink =3D cpu_to_le16(inode->i_nlink); raw->i_size =3D cpu_to_le64(inode->i_size); - raw->i_blocks =3D cpu_to_le32(inode->i_blocks); raw->i_atime =3D cpu_to_le64(inode_get_atime_sec(inode) * NSEC_PER_SEC + inode_get_atime_nsec(inode)); raw->i_mtime =3D cpu_to_le64(inode_get_mtime_sec(inode) * NSEC_PER_SEC @@ -54,6 +53,7 @@ static int ftrfs_write_inode_raw(struct inode *inode) memcpy(raw->i_direct, fi->i_direct, sizeof(fi->i_direct)); raw->i_indirect =3D fi->i_indirect; raw->i_dindirect =3D fi->i_dindirect; + raw->i_tindirect =3D fi->i_tindirect; =20 raw->i_crc32 =3D ftrfs_crc32(raw, offsetof(struct ftrfs_inode, i_crc32)); @@ -145,7 +145,6 @@ static int ftrfs_add_dirent(struct inode *dir, const st= ruct qstr *name, =20 fi->i_direct[i] =3D cpu_to_le64(block_no); dir->i_size +=3D FTRFS_BLOCK_SIZE; - dir->i_blocks++; inode_set_mtime_to_ts(dir, current_time(dir)); mark_inode_dirty(dir); =20 @@ -223,7 +222,6 @@ struct inode *ftrfs_new_inode(struct inode *dir, umode_= t mode) =20 inode_init_owner(&nop_mnt_idmap, inode, dir, mode); inode->i_ino =3D ino; - inode->i_blocks =3D 0; inode->i_size =3D 0; inode_set_atime_to_ts(inode, current_time(inode)); inode_set_mtime_to_ts(inode, current_time(inode)); @@ -242,12 +240,17 @@ struct inode *ftrfs_new_inode(struct inode *dir, umod= e_t mode) } else { inode->i_op =3D &ftrfs_file_inode_operations; inode->i_fop =3D &ftrfs_file_operations; + inode->i_mapping->a_ops =3D &ftrfs_aops; set_nlink(inode, 1); } =20 - insert_inode_hash(inode); + if (insert_inode_locked(inode) < 0) { + make_bad_inode(inode); + iput(inode); + return ERR_PTR(-EIO); + } mark_inode_dirty(inode); - return ERR_CAST(inode); + return inode; } =20 /* ------------------------------------------------------------------ */ @@ -277,9 +280,11 @@ static int ftrfs_create(struct mnt_idmap *idmap, struc= t inode *dir, goto out_iput; =20 d_instantiate(dentry, inode); + unlock_new_inode(inode); return 0; =20 out_iput: + unlock_new_inode(inode); iput(inode); return ret; } @@ -327,9 +332,11 @@ static struct dentry *ftrfs_mkdir(struct mnt_idmap *id= map, struct inode *dir, goto out_fail; =20 d_instantiate(dentry, inode); + unlock_new_inode(inode); return NULL; =20 out_fail: + unlock_new_inode(inode); inode_dec_link_count(inode); inode_dec_link_count(inode); iput(inode); diff --git a/fs/ftrfs/super.c b/fs/ftrfs/super.c index 8acc62921..bce7148ee 100644 --- a/fs/ftrfs/super.c +++ b/fs/ftrfs/super.c @@ -252,6 +252,8 @@ static int __init ftrfs_init(void) return ret; } =20 + BUILD_BUG_ON(sizeof(struct ftrfs_super_block) !=3D FTRFS_BLOCK_SIZE); + BUILD_BUG_ON(sizeof(struct ftrfs_inode) !=3D 256); pr_info("ftrfs: module loaded (FTRFS Fault-Tolerant Radiation-Robust FS)\= n"); return 0; } --=20 2.52.0