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


Reply via email to