This patch activates the JBD2 journaling engine by hooking it into the
main ext2fs write paths and system initialization.
Integration details:
- Journal Initialization: Updates `hyper.c` to detect and load the
journal superblock on filesystem startup.
- Inode Writes: Modifies `diskfs_write_disknode` in `inode.c`. If
journaling is active, inode updates are now directed to the journal
transaction rather than being written directly to disk.
- Sync Logic: Implements `journal_sync_everything`.
This provides the deadlock-safe flushing mechanism required by the
journal when forcing a checkpoint.
Libdiskfs changes:
- Adds a new weak symbol `diskfs_notify_change()` to `libdiskfs`.
This provides a hook for filesystems to capture block modifications
at the node level. A default 'no-op' implementation is provided
in `libdiskfs/node-modified.c` to maintain ABI compatibility for
other filesystems. ext2fs overrides this to track dirty blocks
for the journal.
This completes the JBD2 support, enabling crash consistency for
ext2 filesystems on the Hurd.
---
ext2fs/ext2_fs.h | 3 ++-
ext2fs/ext2fs.c | 24 ++++++++++++++++++++
ext2fs/ext2fs.h | 4 ++++
ext2fs/hyper.c | 9 ++++++++
ext2fs/inode.c | 48 +++++++++++++++++++++++++++++++++++----
ext2fs/pager.c | 35 ++++++++++++++++++++++++++++
libdiskfs/diskfs.h | 5 ++++
libdiskfs/node-modified.c | 28 +++++++++++++++++++++++
libdiskfs/priv.h | 6 +++++
9 files changed, 157 insertions(+), 5 deletions(-)
create mode 100644 libdiskfs/node-modified.c
diff --git a/ext2fs/ext2_fs.h b/ext2fs/ext2_fs.h
index 195e9b6b..5712d5d0 100644
--- a/ext2fs/ext2_fs.h
+++ b/ext2fs/ext2_fs.h
@@ -492,7 +492,8 @@ struct ext2_super_block {
#define EXT2_FEATURE_INCOMPAT_ANY 0xffffffff
#define EXT2_FEATURE_COMPAT_SUPP EXT2_FEATURE_COMPAT_EXT_ATTR
-#define EXT2_FEATURE_INCOMPAT_SUPP EXT2_FEATURE_INCOMPAT_FILETYPE
+#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE | \
+ EXT3_FEATURE_INCOMPAT_RECOVER)
#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
diff --git a/ext2fs/ext2fs.c b/ext2fs/ext2fs.c
index 11d1cdf4..ee35b771 100644
--- a/ext2fs/ext2fs.c
+++ b/ext2fs/ext2fs.c
@@ -32,6 +32,7 @@
#include <hurd/store.h>
#include <version.h>
#include "ext2fs.h"
+#include "journal.h"
/* ---------------------------------------------------------------- */
@@ -81,6 +82,7 @@ unsigned long desc_per_block;
unsigned long addr_per_block;
unsigned long groups_count;
+struct journal *ext2_journal = NULL;
/* ---------------------------------------------------------------- */
@@ -252,6 +254,28 @@ main (int argc, char **argv)
ext2_panic ("no root node!");
pthread_mutex_unlock (&diskfs_root_node->lock);
+ if (sblock->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)
+ {
+ JRNL_LOG_DEBUG ("\n[JOURNAL CHECK] >>> Inode 8 DETECTED! <<<");
+ JRNL_LOG_DEBUG ("[JOURNAL CHECK] s_journal_inum: %u (Expected: 8)",
+ sblock->s_journal_inum);
+ JRNL_LOG_DEBUG ("[JOURNAL CHECK] s_journal_dev: %u",
+ sblock->s_journal_dev);
+ struct node *jnode = NULL;
+ error_t err = diskfs_cached_lookup (8, &jnode);
+
+ if (!err && jnode)
+ {
+ ext2_journal = journal_create (jnode);
+ JRNL_LOG_DEBUG ("Global Journal Initialized at %p", ext2_journal);
+ diskfs_nput(jnode);
+ }
+ }
+ else
+ {
+ JRNL_LOG_DEBUG ("\n[JOURNAL CHECK] No Journal flag found.");
+ }
+
/* Now that we are all set up to handle requests, and diskfs_root_node is
set properly, it is safe to export our fsys control port to the
outside world. */
diff --git a/ext2fs/ext2fs.h b/ext2fs/ext2fs.h
index 46d41e08..95aa6975 100644
--- a/ext2fs/ext2fs.h
+++ b/ext2fs/ext2fs.h
@@ -284,6 +284,10 @@ extern int sblock_dirty;
/* Size of one inode. */
extern uint16_t global_inode_size;
+/* Forward declaration prevents circular dependency with journal.h */
+struct journal;
+extern struct journal *ext2_journal;
+
/* Where the super-block is located on disk (at min-block 1). */
#define SBLOCK_BLOCK 1 /* Default location, second 1k block. */
#define SBLOCK_SIZE (sizeof (struct ext2_super_block))
diff --git a/ext2fs/hyper.c b/ext2fs/hyper.c
index 847f9f2b..6919bbd1 100644
--- a/ext2fs/hyper.c
+++ b/ext2fs/hyper.c
@@ -196,11 +196,20 @@ diskfs_set_hypermetadata (int wait, int clean)
/* The filesystem is clean, so we need to set the clean flag. */
{
sblock->s_state |= htole16 (EXT2_VALID_FS);
+ if (ext2_journal)
+ {
+ sblock->s_feature_incompat &= htole32(~EXT3_FEATURE_INCOMPAT_RECOVER);
+ }
sblock_dirty = 1;
}
else if (!clean && (sblock->s_state & htole16 (EXT2_VALID_FS)))
/* The filesystem just became dirty, so clear the clean flag. */
{
+ if (ext2_journal &&
+ !(sblock->s_feature_incompat &
htole32(EXT3_FEATURE_INCOMPAT_RECOVER)))
+ {
+ sblock->s_feature_incompat |=
htole32(EXT3_FEATURE_INCOMPAT_RECOVER);
+ }
sblock->s_state &= htole16 (~EXT2_VALID_FS);
sblock_dirty = 1;
wait = 1;
diff --git a/ext2fs/inode.c b/ext2fs/inode.c
index 8d10af01..60bf71a9 100644
--- a/ext2fs/inode.c
+++ b/ext2fs/inode.c
@@ -20,6 +20,7 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "ext2fs.h"
+#include "journal.h"
#include <string.h>
#include <unistd.h>
#include <stdio.h>
@@ -559,24 +560,55 @@ write_all_disknodes (void)
diskfs_node_iterate (write_one_disknode);
}
+static void
+write_disknode_journaled (struct node *np, int wait)
+{
+ journal_start_transaction(ext2_journal);
+ struct ext2_inode *di = write_node (np);
+
+ if (di)
+ {
+ unsigned long ino = np->dn_stat.st_ino;
+ unsigned long group = inode_group_num(ino);
+ block_t table_start = le32toh (group_desc(group)->bg_inode_table);
+ unsigned long inodes_per_group = le32toh (sblock->s_inodes_per_group);
+ unsigned long inode_index = (ino - 1) % inodes_per_group;
+ unsigned long byte_offset = inode_index * le16toh (sblock->s_inode_size);
+ block_t block_num = table_start + (byte_offset / block_size);
+ void *block_ptr = bptr (block_num);
+ journal_dirty_block(ext2_journal, block_num, block_ptr);
+ }
+ journal_stop_transaction(ext2_journal);
+ if (wait)
+ journal_commit_transaction(ext2_journal);
+}
+
/* Sync the info in NP->dn_stat and any associated format-specific
information to disk. If WAIT is true, then return only after the
physicial media has been completely updated. */
void
diskfs_write_disknode (struct node *np, int wait)
{
- struct ext2_inode *di = write_node (np);
+ struct ext2_inode *di;
+
+ if (ext2_journal)
+ {
+ write_disknode_journaled (np, wait);
+ return;
+ }
+ di = write_node (np);
if (di)
{
if (wait)
{
- sync_global_ptr (di, 1);
+ sync_global_ptr (di, 1);
error_t err = store_sync (store);
+ /* Ignore EOPNOTSUPP (drivers), but warn on real I/O errors */
if (err && err != EOPNOTSUPP)
- ext2_warning ("inode flush failed: %s", strerror (err));
+ ext2_warning ("device flush failed: %s", strerror (err));
}
else
- record_global_poke (di);
+ record_global_poke (di);
}
}
@@ -908,3 +940,11 @@ diskfs_shutdown_soft_ports (void)
/* Should initiate termination of internally held pager ports
(the only things that should be soft) XXX */
}
+
+void
+diskfs_notify_change (struct node *np)
+{
+ /* If journaling is active, capture this metadata change immediately */
+ if (ext2_journal)
+ diskfs_node_update (np, 0);
+}
diff --git a/ext2fs/pager.c b/ext2fs/pager.c
index 1c795784..42f26443 100644
--- a/ext2fs/pager.c
+++ b/ext2fs/pager.c
@@ -25,6 +25,7 @@
#include <inttypes.h>
#include <hurd/store.h>
#include "ext2fs.h"
+#include "journal.h"
/* XXX */
#include "../libpager/priv.h"
@@ -648,6 +649,11 @@ disk_pager_write_page (vm_offset_t page, void *buf)
while (length > 0 && !err)
{
block_t block = boffs_block (offset);
+ if (ext2_journal && journal_block_is_active(ext2_journal, block))
+ {
+ JRNL_LOG_DEBUG ("Pageout conflict on Block %u -> Forcing
Commit", block);
+ journal_commit_transaction(ext2_journal);
+ }
/* We don't clear the block modified bit here because this paging
write request may not be the same one that actually set the bit,
@@ -1580,6 +1586,30 @@ diskfs_shutdown_pager (void)
pager, just make sure it's synced. */
}
+static error_t
+journal_sync_one (void *v_p)
+{
+ struct pager *p = v_p;
+ pager_sync (p, 1);
+ return 0;
+}
+
+/**
+ * Sync all the pagers synchronously, but don't call
+ * journal_commit here. It would deadlock.
+ **/
+void
+journal_sync_everything (void)
+{
+ write_all_disknodes ();
+ ports_bucket_iterate (file_pager_bucket, journal_sync_one);
+ sync_global (1);
+ error_t err = store_sync (store);
+ /* Ignore EOPNOTSUPP (drivers), but warn on real I/O errors */
+ if (err && err != EOPNOTSUPP)
+ ext2_warning ("device flush failed: %s", strerror (err));
+}
+
/* Sync all the pagers. */
void
diskfs_sync_everything (int wait)
@@ -1591,6 +1621,11 @@ diskfs_sync_everything (int wait)
return 0;
}
+ if (ext2_journal)
+ {
+ /* We only commit if we have a running transaction */
+ journal_commit_transaction (ext2_journal);
+ }
write_all_disknodes ();
ports_bucket_iterate (file_pager_bucket, sync_one);
diff --git a/libdiskfs/diskfs.h b/libdiskfs/diskfs.h
index 5f832dd7..7f420725 100644
--- a/libdiskfs/diskfs.h
+++ b/libdiskfs/diskfs.h
@@ -507,6 +507,11 @@ error_t diskfs_validate_flags_change (struct node *np, int
flags);
changed to RDEV; otherwise return an error code. */
error_t diskfs_validate_rdev_change (struct node *np, dev_t rdev);
+/* The user may define this function. It is called immediately when
+ a node's metadata (stat info) is modified in memory, even if
+ diskfs_synchronous is false. The default definition does nothing. */
+void diskfs_notify_change (struct node *np);
+
/* The user must define this function. Sync the info in NP->dn_stat
and any associated format-specific information to disk. If WAIT is true,
then return only after the physicial media has been completely updated. */
diff --git a/libdiskfs/node-modified.c b/libdiskfs/node-modified.c
new file mode 100644
index 00000000..a29dc39f
--- /dev/null
+++ b/libdiskfs/node-modified.c
@@ -0,0 +1,28 @@
+/* Default version of diskfs_notify_change
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ Written by Milos Nikic.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+#include "diskfs.h"
+
+void __attribute__ ((weak))
+diskfs_notify_change (struct node *np)
+{
+ // default function does nothing.
+}
diff --git a/libdiskfs/priv.h b/libdiskfs/priv.h
index ca3c23ca..e8186d49 100644
--- a/libdiskfs/priv.h
+++ b/libdiskfs/priv.h
@@ -140,7 +140,13 @@ extern fshelp_fetch_root_callback2_t
_diskfs_translator_callback2;
pthread_mutex_lock (&np->lock); \
(OPERATION); \
if (diskfs_synchronous) \
+ { \
diskfs_node_update (np, 1);
\
+ } \
+ else \
+ { \
+ diskfs_notify_change (np); \
+ } \
pthread_mutex_unlock (&np->lock); \
return err; \
})
--
2.52.0