Hi, Thanks again for the earlier feedback — I’ve applied the style corrections and, since then, have rewritten the system a few times from the ground up.
The current version uses an asynchronous producer-consumer model with a ring-buffer queue and a dedicated flusher thread. (it also synchounosly flushes on diskfs_sync_everything). Binary journal entries are now written to a raw device (currently implemented as a regular file at /tmp/journal-pipe) using a fixed-size record format. The journal is CRC32-protected and durably persisted across restarts. The first 4 KB of the journal is reserved for a header that tracks the current head/tail pointers and it too includes a CRC32 checksum for integrity, along with version, and magic etc. On startup, the system replays and validates the existing journal entries. The journaling layer is now hooked into several file system operations, including rename, unlink, create, and utimes. Support for others (e.g., chmod) is still pending. The system has been designed for robustness — if the backing journal file is removed or unavailable, events are dropped gracefully without destabilizing the system. I've tested it under load (e.g., repeated git rebase, mass file deletions, and high-frequency updates), and it performs reliably. I basically use it daily without issues. I’d say the foundation is close to ready. Next steps include expanding the hooks to cover more metadata changes, building a replay utility, and some tooling for managing the raw bytes etc. I took some time off work to explore this — it’s been a fun project, and I’d appreciate your thoughts. Best regards, Milos Nikic I On Tue, Jul 1, 2025 at 5:08 PM Samuel Thibault <samuel.thiba...@gnu.org> wrote: > Hello, > > Milos Nikic, le jeu. 26 juin 2025 23:40:22 -0700, a ecrit: > > diff --git a/ext2fs/inode.c b/ext2fs/inode.c > > index dc309ac8..a3560630 100644 > > --- a/ext2fs/inode.c > > +++ b/ext2fs/inode.c > > /* these flags aren't actually defined by a header file yet, so > temporarily > > disable them if necessary. */ > > @@ -524,6 +525,8 @@ write_all_disknodes (void) > > void > > diskfs_write_disknode (struct node *np, int wait) > > { > > + > > + journal_log_metadata(np, &(struct journal_entry_info){ .action = > "sync" }); > > Try to get used to the GNU coding style: there should be a space before > opening parentheses > > > struct ext2_inode *di = write_node (np); > > if (di) > > { > > > diff --git a/libdiskfs/journal.c b/libdiskfs/journal.c > > new file mode 100644 > > index 00000000..4c8681dc > > --- /dev/null > > +++ b/libdiskfs/journal.c > > @@ -0,0 +1,260 @@ > > +/* > > Note that you need to add a copyright header, and assign your copyright > to the FSF. > > > + * journal.c - Experimental journaling layer for Hurd's ext2fs/libdiskfs > > + * > > + * This is a work-in-progress implementation of a toy journaling layer > > + * intended for exploration and learning purposes. It logs basic > metadata > > + * about file changes into a shared in-memory buffer, which is > periodically > > + * flushed to a file (/tmp/journal.log). > > + * > > + * Features: > > + * - Logs inode metadata (mode, size, nlink, mtime, ctime, etc.) > > + * - Each log entry is wrapped in a transaction with a unique ID and > timestamp > > + * - Uses a fixed-size in-memory buffer with auto-flushing on overflow > > + * - Timestamp includes millisecond precision > > + * - Thread-safe using a mutex > > + * > > + * Missing / Not Implemented Yet: > > + * - Write barriers or guarantees of ordering with actual FS > operations > > + * - Integration at a lower level to capture all metadata changes > (not just sync hooks) > > + * - Actual recovery mechanisms or replays from the journal > > + * - Logging of inode or block bitmap changes > > + * - File name resolution (only available if passed manually) > > + * - UID/GID or finer-grained permission changes > > + * - Disk-backed circular journal buffer for continuous logging > > + * - Atomicity guarantees across flush boundaries (currently only > soft protection) > > + * > > + * Warning: > > + * This code is experimental and not suitable for production. > > + * It is designed to support incremental development and learning. > > + * > > + * Author: Milos Nikic, 2025 > > + */ > > +#include <stdio.h> > > +#include <inttypes.h> > > +#include <time.h> > > +#include <string.h> > > +#include <errno.h> > > +#include <sys/stat.h> > > +#include <libdiskfs/journal.h> > > +#include <diskfs.h> > > +#include <sys/types.h> > > +#include <stdbool.h> > > +#include <stdint.h> > > +#include <sys/time.h> > > +#include <pthread.h> > > + > > +#define JOURNAL_DIR_PATH "/tmp" > > +#define JOURNAL_LOG_PATH JOURNAL_DIR_PATH "/journal.log" > > +#define JOURNAL_BUF_SIZE (64 * 1024) > > +#define MAX_REASONABLE_TIME 4102444800 /* Jan 1, 2100 */ > > +#define MIN_REASONABLE_TIME 946684800 /* Jan 1, 2000 */ > > Better take the current time plus / minus a lot of years, so it'll stay > a reasonable guess decades from now. > > Samuel >
From 9ea4b46d7e35a2d7b76dd9e68b9e3968cec331b0 Mon Sep 17 00:00:00 2001 From: Milos Nikic <nikic.mi...@gmail.com> Date: Wed, 2 Jul 2025 04:54:09 +0100 Subject: [PATCH] libdiskfs: Add experimental raw metadata journaling --- ext2fs/inode.c | 3 + ext2fs/pager.c | 3 + libdiskfs/Makefile | 4 +- libdiskfs/crc32.c | 82 ++++++++ libdiskfs/crc32.h | 31 +++ libdiskfs/dir-rename.c | 13 +- libdiskfs/dir-unlink.c | 9 + libdiskfs/file-utimes.c | 10 + libdiskfs/init-init.c | 2 + libdiskfs/journal.c | 157 +++++++++++++++ libdiskfs/journal.h | 46 +++++ libdiskfs/journal_format.h | 64 ++++++ libdiskfs/journal_queue.c | 165 ++++++++++++++++ libdiskfs/journal_queue.h | 35 ++++ libdiskfs/journal_writer.c | 392 +++++++++++++++++++++++++++++++++++++ libdiskfs/journal_writer.h | 32 +++ libdiskfs/node-create.c | 9 +- libdiskfs/shutdown.c | 2 + 18 files changed, 1055 insertions(+), 4 deletions(-) create mode 100644 libdiskfs/crc32.c create mode 100644 libdiskfs/crc32.h create mode 100644 libdiskfs/journal.c create mode 100644 libdiskfs/journal.h create mode 100644 libdiskfs/journal_format.h create mode 100644 libdiskfs/journal_queue.c create mode 100644 libdiskfs/journal_queue.h create mode 100644 libdiskfs/journal_writer.c create mode 100644 libdiskfs/journal_writer.h diff --git a/ext2fs/inode.c b/ext2fs/inode.c index dc309ac8..61de00ff 100644 --- a/ext2fs/inode.c +++ b/ext2fs/inode.c @@ -28,6 +28,7 @@ #include <sys/statfs.h> #include <sys/statvfs.h> #include <sys/xattr.h> +#include <libdiskfs/journal.h> /* these flags aren't actually defined by a header file yet, so temporarily disable them if necessary. */ @@ -524,6 +525,8 @@ write_all_disknodes (void) void diskfs_write_disknode (struct node *np, int wait) { + + journal_log_metadata(np, &(struct journal_entry_info){ .action = "write_disknode" }); struct ext2_inode *di = write_node (np); if (di) { diff --git a/ext2fs/pager.c b/ext2fs/pager.c index c55107a9..9174e3d5 100644 --- a/ext2fs/pager.c +++ b/ext2fs/pager.c @@ -25,6 +25,7 @@ #include <inttypes.h> #include <hurd/store.h> #include "ext2fs.h" +#include <libdiskfs/journal.h> /* XXX */ #include "../libpager/priv.h" @@ -1437,6 +1438,8 @@ diskfs_shutdown_pager (void) void diskfs_sync_everything (int wait) { + flush_journal_to_file(); + error_t sync_one (void *v_p) { struct pager *p = v_p; diff --git a/libdiskfs/Makefile b/libdiskfs/Makefile index aa6b24a4..c82628a4 100644 --- a/libdiskfs/Makefile +++ b/libdiskfs/Makefile @@ -32,7 +32,7 @@ IOSRCS= io-async-icky.c io-async.c io-duplicate.c io-get-conch.c io-revoke.c \ io-modes-on.c io-modes-set.c io-owner-mod.c io-owner-get.c \ io-pathconf.c io-prenotify.c io-read.c io-readable.c io-identity.c \ io-reauthenticate.c io-rel-conch.c io-restrict-auth.c io-seek.c \ - io-select.c io-stat.c io-stubs.c io-write.c io-version.c io-sigio.c + io-select.c io-stat.c io-stubs.c io-write.c io-version.c io-sigio.c journal.c journal_queue.c crc32.c journal_writer.c FSYSSRCS=fsys-getroot.c fsys-goaway.c fsys-startup.c fsys-getfile.c \ fsys-options.c fsys-syncfs.c fsys-forward.c \ fsys-get-children.c fsys-get-source.c @@ -54,7 +54,7 @@ OTHERSRCS = conch-fetch.c conch-set.c dir-clear.c dir-init.c dir-renamed.c \ validate-mode.c validate-group.c validate-author.c validate-flags.c \ validate-rdev.c validate-owner.c priv.c get-source.c SRCS = $(OTHERSRCS) $(FSSRCS) $(IOSRCS) $(FSYSSRCS) $(IFSOCKSRCS) -installhdrs = diskfs.h diskfs-pager.h +installhdrs = diskfs.h diskfs-pager.h journal.h MIGSTUBS = fsServer.o ioServer.o fsysServer.o exec_startupServer.o \ fsys_replyUser.o fs_notifyUser.o ifsockServer.o \ diff --git a/libdiskfs/crc32.c b/libdiskfs/crc32.c new file mode 100644 index 00000000..fe355344 --- /dev/null +++ b/libdiskfs/crc32.c @@ -0,0 +1,82 @@ +#include "crc32.h" + +static const uint32_t crc32_table[256] = + { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d + }; + +uint32_t +crc32 (const void *data, size_t len) +{ + uint32_t crc = ~0U; + const unsigned char *p = data; + + while (len--) + crc = crc32_table[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + + return ~crc; +} + diff --git a/libdiskfs/crc32.h b/libdiskfs/crc32.h new file mode 100644 index 00000000..1a8ead03 --- /dev/null +++ b/libdiskfs/crc32.h @@ -0,0 +1,31 @@ +/* crc32.h - CRC32 checksum interface for GNU Hurd journaling + + Copyright (C) 2025 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 the GNU Hurd; if not, see <https://www.gnu.org/licenses/>. */ + +#ifndef CRC32_H +#define CRC32_H + +#include <stddef.h> +#include <stdint.h> + +uint32_t crc32 (const void *data, size_t len); + +#endif /* CRC32_H */ + diff --git a/libdiskfs/dir-rename.c b/libdiskfs/dir-rename.c index eb123073..4567ae9d 100644 --- a/libdiskfs/dir-rename.c +++ b/libdiskfs/dir-rename.c @@ -20,6 +20,7 @@ #include "priv.h" #include "fs_S.h" #include <string.h> +#include <libdiskfs/journal.h> /* To avoid races in checkpath, and to prevent a directory from being simultaneously renamed by two processes, we serialize all renames of @@ -225,7 +226,17 @@ diskfs_S_dir_rename (struct protid *fromcred, fnp->dn_stat.st_nlink--; fnp->dn_set_ctime = 1; - + struct journal_entry_info info = { + .action = "rename", + .old_name = fromname, + .new_name = toname, + .src_parent_ino = fdp->dn_stat.st_ino, + .dst_parent_ino = tdp->dn_stat.st_ino, + .name = toname, + .parent_ino = tdp->dn_stat.st_ino + }; + journal_log_metadata (fnp, &info); + if (diskfs_synchronous) diskfs_node_update (fnp, 1); diff --git a/libdiskfs/dir-unlink.c b/libdiskfs/dir-unlink.c index 5a8bef34..653c1c9f 100644 --- a/libdiskfs/dir-unlink.c +++ b/libdiskfs/dir-unlink.c @@ -18,6 +18,7 @@ #include "priv.h" #include "fs_S.h" #include <hurd/fsys.h> +#include <libdiskfs/journal.h> /* Implement dir_unlink as described in <hurd/fs.defs>. */ kern_return_t @@ -74,6 +75,14 @@ diskfs_S_dir_unlink (struct protid *dircred, np->dn_stat.st_nlink--; np->dn_set_ctime = 1; + + struct journal_entry_info info = { + .action = "unlink", + .name = name, + .parent_ino = dnp->dn_stat.st_ino + }; + journal_log_metadata(np, &info); + if (diskfs_synchronous) diskfs_node_update (np, 1); diff --git a/libdiskfs/file-utimes.c b/libdiskfs/file-utimes.c index 13751d0b..1273fc9e 100644 --- a/libdiskfs/file-utimes.c +++ b/libdiskfs/file-utimes.c @@ -17,6 +17,7 @@ #include "priv.h" #include "fs_S.h" +#include <libdiskfs/journal.h> /* Implement file_utimes as described in <hurd/fs.defs>. */ kern_return_t @@ -80,6 +81,15 @@ diskfs_S_file_utimens (struct protid *cred, np->dn_set_ctime = 1; + if (!S_ISCHR(np->dn_stat.st_mode) && !S_ISBLK(np->dn_stat.st_mode)) { + struct journal_entry_info info = { + .name = "(utimes)", + .parent_ino = 0, + .action = "utimes", + }; + journal_log_metadata(np, &info); + } + if (np->filemod_reqs) diskfs_notice_filechange (np, FILE_CHANGED_META, diff --git a/libdiskfs/init-init.c b/libdiskfs/init-init.c index f9b12f6f..8bc5914a 100644 --- a/libdiskfs/init-init.c +++ b/libdiskfs/init-init.c @@ -24,6 +24,7 @@ #include <hurd/fsys.h> #include <stdio.h> #include <maptime.h> +#include <libdiskfs/journal.h> /* For safe inlining of diskfs_node_disknode and diskfs_disknode_node. */ @@ -98,6 +99,7 @@ diskfs_init_diskfs (void) _hurd_port_init (&_diskfs_exec_portcell, MACH_PORT_NULL); + journal_init(); return 0; } diff --git a/libdiskfs/journal.c b/libdiskfs/journal.c new file mode 100644 index 00000000..54b470ef --- /dev/null +++ b/libdiskfs/journal.c @@ -0,0 +1,157 @@ +/* journal.c - Core metadata logger for toy journaling in GNU Hurd + + Copyright (C) 2025 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 the GNU Hurd; if not, see <https://www.gnu.org/licenses/>. */ + +#include <libdiskfs/journal_format.h> +#include <libdiskfs/journal_queue.h> +#include <libdiskfs/journal.h> +#include <diskfs.h> +#include <inttypes.h> +#include <stdio.h> +#include <string.h> +#include <stdbool.h> +#include <time.h> +#include <stdlib.h> +#include <sys/time.h> +#include <hurd/fshelp.h> +#include <sys/stat.h> +#include <pthread.h> + +#define MAX_REASONABLE_TIME 4102444800 /* Jan 1, 2100 */ +#define MIN_REASONABLE_TIME 946684800 /* Jan 1, 2000 */ +#define IGNORE_INODE(inode) \ + ((inode) == 82814 || (inode) == 48803 || (inode) == 49144 \ + || (inode) == 49142 || (inode) == 48795 || (inode) == 48794) + +static volatile uint64_t journal_tx_id = 1; +static volatile bool journal_shutting_down; +static pthread_t journal_flusher_tid; + +static uint64_t +current_time_ms (void) +{ + struct timeval tv; + gettimeofday (&tv, NULL); + return ((uint64_t) tv.tv_sec) * 1000 + tv.tv_usec / 1000; +} + +void +journal_init (void) +{ + fprintf (stderr, "Toy journaling: journal_init() called\n"); + journal_queue_init (); + if (pthread_create (&journal_flusher_tid, NULL, journal_flusher_thread, NULL) != 0) + { + fprintf (stderr, "Toy journaling: failed to create a flusher thread.\n"); + journal_shutting_down = true; + } + fprintf (stderr, "Toy journaling: done initializing.\n"); +} + +void +journal_shutdown (void) +{ + fprintf (stderr, "Toy journaling: journal_shutdown() called\n"); + journal_shutting_down = true; + journal_queue_shutdown (); + pthread_join (journal_flusher_tid, NULL); +} + +void +flush_journal_to_file (void) +{ + journal_flush_now (); +} + +void +journal_log_metadata (void *node_ptr, const struct journal_entry_info *info) +{ + if (!node_ptr) + { + fprintf (stderr, "Toy journaling: NULL node_ptr received in journal_log_metadata, skipping.\n"); + return; + } + if (!info) + { + fprintf (stderr, "Toy journaling: NULL info pointer received in journal_log_metadata, skipping.\n"); + return; + } + + const struct stat *st = &((struct node *) node_ptr)->dn_stat; + if (IGNORE_INODE (st->st_ino)) + return; + + const char *action = info->action ?: ""; + const char *name = info->name ?: ""; + const char *extra = info->extra ?: ""; + const char *old_name = info->old_name ?: ""; + const char *new_name = info->new_name ?: ""; + + size_t total_size = sizeof (struct journal_entry_bin); + if (total_size > JOURNAL_ENTRY_SIZE) + { + fprintf (stderr, "Toy journaling: entry too large, dropped.\n"); + return; + } + + char *buf = calloc (1, total_size); + if (!buf) + return; + + struct journal_entry_bin *entry = (struct journal_entry_bin *) buf; + memset (entry, 0, sizeof (*entry)); + + entry->magic = JOURNAL_MAGIC; + entry->version = JOURNAL_VERSION; + entry->tx_id = ++journal_tx_id; + entry->timestamp_ms = current_time_ms (); + + entry->parent_ino = info->parent_ino; + entry->src_parent_ino = info->src_parent_ino; + entry->dst_parent_ino = info->dst_parent_ino; + entry->ino = st->st_ino; + + entry->st_mode = st->st_mode; + entry->st_size = st->st_size; + entry->st_nlink = st->st_nlink; + entry->st_blocks = st->st_blocks; + + entry->mtime = (st->st_mtime > MIN_REASONABLE_TIME + && st->st_mtime < MAX_REASONABLE_TIME) ? st->st_mtime : -1; + entry->ctime = (st->st_ctime > MIN_REASONABLE_TIME + && st->st_ctime < MAX_REASONABLE_TIME) ? st->st_ctime : -1; + + strncpy (entry->action, action, sizeof (entry->action) - 1); + strncpy (entry->name, name, sizeof (entry->name) - 1); + strncpy (entry->extra, extra, sizeof (entry->extra) - 1); + strncpy (entry->old_name, old_name, sizeof (entry->old_name) - 1); + strncpy (entry->new_name, new_name, sizeof (entry->new_name) - 1); + + entry->action[sizeof (entry->action) - 1] = '\0'; + entry->name[sizeof (entry->name) - 1] = '\0'; + entry->extra[sizeof (entry->extra) - 1] = '\0'; + entry->old_name[sizeof (entry->old_name) - 1] = '\0'; + entry->new_name[sizeof (entry->new_name) - 1] = '\0'; + + entry->crc32 = 0; + journal_enqueue (buf, total_size); + free (buf); +} + diff --git a/libdiskfs/journal.h b/libdiskfs/journal.h new file mode 100644 index 00000000..c88e8288 --- /dev/null +++ b/libdiskfs/journal.h @@ -0,0 +1,46 @@ +/* journal.h - Public interface for journaling metadata events + + Copyright (C) 2025 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 the GNU Hurd; if not, see <https://www.gnu.org/licenses/>. */ + +#ifndef JOURNAL_H +#define JOURNAL_H + +#include <stdbool.h> +#include <sys/types.h> + +struct journal_entry_info +{ + const char *action; /* "create", "unlink", "rename", etc. */ + const char *name; /* Affected file name */ + ino_t parent_ino; /* For actions involving directories */ + const char *old_name; /* For rename */ + const char *new_name; /* For rename */ + ino_t src_parent_ino; /* For rename */ + ino_t dst_parent_ino; /* For rename */ + const char *extra; /* Optional free-form field (e.g. "chmod mode=0755") */ +}; + +void journal_init (void); +void journal_shutdown (void); +void flush_journal_to_file (void); +void journal_log_metadata (void *node_ptr, const struct journal_entry_info *info); + +#endif /* JOURNAL_H */ + diff --git a/libdiskfs/journal_format.h b/libdiskfs/journal_format.h new file mode 100644 index 00000000..118121df --- /dev/null +++ b/libdiskfs/journal_format.h @@ -0,0 +1,64 @@ +/* journal_format.h - Binary journal entry format definitions + + Copyright (C) 2025 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 the GNU Hurd; if not, see <https://www.gnu.org/licenses/>. */ + +#ifndef JOURNAL_FORMAT_H +#define JOURNAL_FORMAT_H + +#include <stdint.h> +#include <sys/types.h> + +#define JOURNAL_MAGIC 0x4A4E4C30 /* "JNL0" */ +#define JOURNAL_VERSION 1 +#define MAX_FIELD_LEN 256 +#define JOURNAL_ENTRY_SIZE 4096 + +struct journal_entry_bin +{ + uint32_t magic; + uint32_t version; + uint64_t tx_id; + uint64_t timestamp_ms; + ino_t parent_ino; + ino_t src_parent_ino; + ino_t dst_parent_ino; + ino_t ino; + uint32_t st_mode; + uint64_t st_size; + uint64_t st_nlink; + uint64_t st_blocks; + int64_t mtime; + int64_t ctime; + char action[MAX_FIELD_LEN]; + char name[MAX_FIELD_LEN]; + char old_name[MAX_FIELD_LEN]; + char new_name[MAX_FIELD_LEN]; + char extra[MAX_FIELD_LEN]; + uint32_t crc32; +}; + +struct journal_payload +{ + const char *data; + size_t len; +}; + +#endif /* JOURNAL_FORMAT_H */ + diff --git a/libdiskfs/journal_queue.c b/libdiskfs/journal_queue.c new file mode 100644 index 00000000..42315b49 --- /dev/null +++ b/libdiskfs/journal_queue.c @@ -0,0 +1,165 @@ +/* journal_queue.c - Asynchronous queue for toy journaling system + + Copyright (C) 2025 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 the GNU Hurd; if not, see <https://www.gnu.org/licenses/>. */ + +#include <libdiskfs/journal_queue.h> +#include <libdiskfs/journal_format.h> +#include <libdiskfs/journal_writer.h> +#include <libdiskfs/crc32.h> +#include <pthread.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <stdint.h> +#include <time.h> +#include <stdbool.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <hurd/fshelp.h> +#include <errno.h> + +#define JOURNAL_FLUSH_TIMEOUT_MS 500 +#define JOURNAL_QUEUE_MAX 256 +#define RAW_DEVICE_PATH "/tmp/journal-pipe" +#define JOURNAL_ENTRY_SIZE 4096 +#define RAW_DEVICE_SIZE (8 * 1024 * 1024) /* 8MB */ + +struct journal_queue_entry +{ + char data[JOURNAL_ENTRY_SIZE]; + size_t len; + bool used; +}; + +static struct journal_queue_entry journal_queue[JOURNAL_QUEUE_MAX]; +static size_t head = 0, tail = 0, count = 0; +static pthread_mutex_t queue_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t queue_cond = PTHREAD_COND_INITIALIZER; +static bool shutdown_in_progress = false; + +void +journal_queue_init (void) +{ + shutdown_in_progress = false; + head = tail = count = 0; + for (size_t i = 0; i < JOURNAL_QUEUE_MAX; i++) + { + journal_queue[i].len = 0; + journal_queue[i].used = false; + } +} + +void +journal_queue_shutdown (void) +{ + pthread_mutex_lock (&queue_lock); + shutdown_in_progress = true; + pthread_cond_signal (&queue_cond); + pthread_mutex_unlock (&queue_lock); +} + +bool +journal_enqueue (const char *data, size_t len) +{ + if (len >= JOURNAL_ENTRY_SIZE) + return false; + + pthread_mutex_lock (&queue_lock); + + if (count >= JOURNAL_QUEUE_MAX) + { + pthread_mutex_unlock (&queue_lock); + return false; + } + + struct journal_queue_entry *e = &journal_queue[tail]; + memcpy (e->data, data, len); + e->len = len; + e->used = true; + + tail = (tail + 1) % JOURNAL_QUEUE_MAX; + count++; + + pthread_cond_signal (&queue_cond); + pthread_mutex_unlock (&queue_lock); + return true; +} + +void +journal_flush_now (void) +{ + fprintf (stderr, "Toy journaling: flush now.\n"); + pthread_mutex_lock (&queue_lock); + pthread_cond_signal (&queue_cond); + pthread_mutex_unlock (&queue_lock); +} + +void * +journal_flusher_thread (void *arg) +{ + while (1) + { + pthread_mutex_lock (&queue_lock); + + while (count == 0 && !shutdown_in_progress) + pthread_cond_wait (&queue_cond, &queue_lock); + + if (shutdown_in_progress && count == 0) + { + pthread_mutex_unlock (&queue_lock); + break; + } + + struct timespec start; + clock_gettime (CLOCK_REALTIME, &start); + + struct timespec deadline = start; + deadline.tv_nsec += (JOURNAL_FLUSH_TIMEOUT_MS % 1000) * 1000000; + deadline.tv_sec += JOURNAL_FLUSH_TIMEOUT_MS / 1000 + deadline.tv_nsec / 1000000000; + deadline.tv_nsec %= 1000000000; + + while (count < JOURNAL_QUEUE_MAX && !shutdown_in_progress) + { + struct timespec now; + clock_gettime (CLOCK_REALTIME, &now); + if (now.tv_sec > deadline.tv_sec + || (now.tv_sec == deadline.tv_sec && now.tv_nsec >= deadline.tv_nsec)) + break; + pthread_cond_timedwait (&queue_cond, &queue_lock, &deadline); + } + + size_t batch_count = count; + struct journal_payload batch[JOURNAL_QUEUE_MAX]; + + for (size_t i = 0; i < batch_count; i++) + { + batch[i].data = journal_queue[head].data; + batch[i].len = journal_queue[head].len; + journal_queue[head].used = false; + head = (head + 1) % JOURNAL_QUEUE_MAX; + } + count = 0; + + pthread_mutex_unlock (&queue_lock); + journal_write_raw (batch, batch_count); + } + return NULL; +} + diff --git a/libdiskfs/journal_queue.h b/libdiskfs/journal_queue.h new file mode 100644 index 00000000..75cfeda8 --- /dev/null +++ b/libdiskfs/journal_queue.h @@ -0,0 +1,35 @@ +/* journal_queue.h - Asynchronous journal queue API + + Copyright (C) 2025 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 the GNU Hurd; if not, see <https://www.gnu.org/licenses/>. */ + +#ifndef JOURNAL_QUEUE_H +#define JOURNAL_QUEUE_H + +#include <stddef.h> +#include <stdbool.h> + +void journal_queue_init (void); +void journal_queue_shutdown (void); +bool journal_enqueue (const char *data, size_t len); +void journal_flush_now (void); +void *journal_flusher_thread (void *arg); + +#endif /* JOURNAL_QUEUE_H */ + diff --git a/libdiskfs/journal_writer.c b/libdiskfs/journal_writer.c new file mode 100644 index 00000000..7d4e612c --- /dev/null +++ b/libdiskfs/journal_writer.c @@ -0,0 +1,392 @@ +/* journal_writer.c - Raw journal writer for GNU Hurd journaling + + Copyright (C) 2025 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 the GNU Hurd; if not, see <https://www.gnu.org/licenses/>. */ + +#include <libdiskfs/journal_writer.h> +#include <libdiskfs/journal_format.h> +#include <libdiskfs/journal_queue.h> +#include <libdiskfs/crc32.h> +#include <hurd/fshelp.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <inttypes.h> +#include <stdint.h> +#include <time.h> +#include <stdbool.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <errno.h> + +#define RAW_DEVICE_PATH "/tmp/journal-pipe" +#define RAW_DEVICE_SIZE (8 * 1024 * 1024) /* 8MB */ +#define JOURNAL_RESERVED_SPACE 4096 /* Leave room for future header growth */ +#define JOURNAL_DATA_CAPACITY (RAW_DEVICE_SIZE - JOURNAL_RESERVED_SPACE) +#define JOURNAL_NUM_ENTRIES (JOURNAL_DATA_CAPACITY / JOURNAL_ENTRY_SIZE) + +struct __attribute__ ((__packed__)) journal_header +{ + uint32_t magic; + uint32_t version; + uint64_t start_index; + uint64_t end_index; + uint32_t crc32; +}; + +static size_t dropped_txs = 0; +static bool replayed_once = false; + +static off_t +index_to_offset (uint64_t index) +{ + return JOURNAL_RESERVED_SPACE + (index % JOURNAL_NUM_ENTRIES) * JOURNAL_ENTRY_SIZE; +} + +static void +journal_replay_and_validate (void) +{ + fprintf (stderr, "Toy journaling: Starting validation.\n"); + int fd = open (RAW_DEVICE_PATH, O_RDONLY); + if (fd < 0) + { + fprintf (stderr, "journal_replay_and_validate: open failed: %s\n", + strerror (errno)); + return; + } + + struct journal_header hdr = { 0 }; + ssize_t n = pread (fd, &hdr, sizeof (hdr), 0); + if (n != sizeof (hdr)) + { + fprintf (stderr, "journal replay: could not read journal header\n"); + close (fd); + return; + } + + uint32_t expected_crc = hdr.crc32; + hdr.crc32 = 0; + uint32_t actual_crc = crc32 ((const void *) &hdr, sizeof (hdr)); + if (actual_crc != expected_crc + || hdr.magic != JOURNAL_MAGIC + || hdr.version != JOURNAL_VERSION) + { + fprintf (stderr, "journal replay: header invalid\n"); + close (fd); + return; + } + + if (hdr.start_index >= JOURNAL_NUM_ENTRIES + || hdr.end_index >= JOURNAL_NUM_ENTRIES) + { + fprintf (stderr, "journal_write_raw: header indices out of bounds\n"); + close (fd); + return; + } + + uint64_t index = hdr.start_index; + uint64_t end_index = hdr.end_index; + uint64_t last_tx_id = 0; + uint64_t last_timestamp = 0; + char buf[JOURNAL_ENTRY_SIZE] = { 0 }; + bool all_good = true; + + while (index != end_index) + { + off_t offset = index_to_offset (index); + if (pread (fd, buf, JOURNAL_ENTRY_SIZE, offset) != JOURNAL_ENTRY_SIZE) + { + fprintf (stderr, + "journal replay: incomplete read at offset %ld\n", + (long) offset); + break; + } + + struct journal_entry_bin *entry = (struct journal_entry_bin *) buf; + + if (entry->magic != JOURNAL_MAGIC) + { + fprintf (stderr, "journal replay: bad magic at offset %ld\n", + (long) offset); + all_good = false; + break; + } + + if (entry->version != JOURNAL_VERSION) + { + fprintf (stderr, "journal replay: version mismatch at offset %ld\n", + (long) offset); + all_good = false; + break; + } + + uint32_t stored_crc = entry->crc32; + entry->crc32 = 0; + + if (crc32 (buf, JOURNAL_ENTRY_SIZE) != stored_crc) + { + fprintf (stderr, "journal replay: CRC mismatch at offset %ld\n", + (long) offset); + all_good = false; + break; + } + + if (entry->timestamp_ms < last_timestamp) + { + fprintf (stderr, + "journal replay: decreasing timestamp at offset %ld (index=%" PRIu64 "): current=%" PRIu64 ", previous=%" PRIu64 "\n", + (long) offset, index, entry->timestamp_ms, last_timestamp); + all_good = false; + break; + } + + if ((entry->timestamp_ms > last_timestamp && entry->tx_id <= last_tx_id) + || (entry->timestamp_ms < last_timestamp + && entry->tx_id >= last_tx_id)) + { + if (llabs ((int64_t) (entry->timestamp_ms - last_timestamp)) > 10000) + { + fprintf (stderr, + "journal replay: timestamp skew too large at offset %ld (index=%" PRIu64 "): tx_id=%" PRIu64 ", previous=%" PRIu64 ", timestamp=%" PRIu64 ", previous_timestamp=%" PRIu64 "\n", + (long) offset, index, entry->tx_id, last_tx_id, + entry->timestamp_ms, last_timestamp); + all_good = false; + break; + } + else + { + fprintf (stderr, + "journal replay: WARNING: non-monotonic tx_id or timestamp at offset %ld (index=%" PRIu64 "): tx_id=%" PRIu64 ", previous=%" PRIu64 ", timestamp=%" PRIu64 ", previous_timestamp=%" PRIu64 "\n", + (long) offset, index, entry->tx_id, last_tx_id, + entry->timestamp_ms, last_timestamp); + } + } + + last_tx_id = entry->tx_id; + last_timestamp = entry->timestamp_ms; + + fprintf (stderr, "journal replay: tx_id=%" PRIu64 ", timestamp=%" PRIu64 "\n", + entry->tx_id, entry->timestamp_ms); + + index = (index + 1) % JOURNAL_NUM_ENTRIES; + } + + if (all_good) + fprintf (stderr, "Toy journaling: Succesful validation of journal entries.\n"); + else + fprintf (stderr, "Toy journaling: Validation completed with errors.\n"); + + close (fd); +} + +static bool +persist_header_with_retry (int fd, uint64_t start_index, + uint64_t end_index, int retries) +{ + struct journal_header hdr = + { + .magic = JOURNAL_MAGIC, + .version = JOURNAL_VERSION, + .start_index = start_index, + .end_index = end_index, + .crc32 = 0, + }; + + hdr.crc32 = crc32 ((const void *) &hdr, sizeof (hdr)); + + while (retries-- > 0) + { + if (pwrite (fd, &hdr, sizeof (hdr), 0) == sizeof (hdr)) + { + fsync (fd); + return true; + } + + fprintf (stderr, + "journal: header write failed, retrying (%d left): %s\n", + retries, strerror (errno)); + usleep (1000); + } + + return false; +} + +static bool +initialize_indices (int fd, uint64_t *start_index, uint64_t *end_index) +{ + struct journal_header hdr = { 0 }; + ssize_t n = pread (fd, &hdr, sizeof (hdr), 0); + + if (n == -1 && errno == EIO) + { + fprintf (stderr, + "journal_write_raw: cannot read journal file: %s\n", + strerror (errno)); + return false; + } + + if (n != sizeof (hdr)) + { + fprintf (stderr, + "journal_write_raw: header read failed or missing\n"); + *start_index = 0; + *end_index = 0; + return true; + } + + uint32_t expected_crc = hdr.crc32; + hdr.crc32 = 0; + uint32_t actual_crc = crc32 ((const void *) &hdr, sizeof (hdr)); + + if (actual_crc != expected_crc + || hdr.magic != JOURNAL_MAGIC + || hdr.version != JOURNAL_VERSION) + { + fprintf (stderr, + "journal_write_raw: header CRC mismatch or invalid\n"); + *start_index = 0; + *end_index = 0; + return true; + } + + if (hdr.start_index >= JOURNAL_NUM_ENTRIES + || hdr.end_index >= JOURNAL_NUM_ENTRIES) + { + fprintf (stderr, + "journal_write_raw: header indices out of bounds\n"); + *start_index = 0; + *end_index = 0; + return true; + } + + *start_index = hdr.start_index; + *end_index = hdr.end_index; + + fprintf (stderr, + "journal_write_raw: start_index=%llu, end_index=%llu\n", + *start_index, *end_index); + + return true; +} + +static bool +journal_write_indexed (int fd, const char *data, size_t len, + uint64_t *end_index, uint64_t *start_index) +{ + if (len > JOURNAL_ENTRY_SIZE) + { + fprintf (stderr, + "journal_write_indexed: entry too large: %zu bytes\n", len); + return false; + } + + uint64_t next = (*end_index + 1) % JOURNAL_NUM_ENTRIES; + if (next == *start_index) + *start_index = (*start_index + 1) % JOURNAL_NUM_ENTRIES; + + char buf[JOURNAL_ENTRY_SIZE] = { 0 }; + memcpy (buf, data, len); + + struct journal_entry_bin *entry = (struct journal_entry_bin *) buf; + entry->crc32 = 0; + entry->crc32 = crc32 (buf, JOURNAL_ENTRY_SIZE); + + off_t offset = index_to_offset (*end_index); + if (lseek (fd, offset, SEEK_SET) == (off_t) -1) + { + fprintf (stderr, + "journal_write_indexed: lseek failed: %s\n", + strerror (errno)); + return false; + } + + ssize_t written = write (fd, buf, JOURNAL_ENTRY_SIZE); + if (written != JOURNAL_ENTRY_SIZE) + { + fprintf (stderr, + "journal_write_indexed: write failed: %s\n", + strerror (errno)); + return false; + } + + *end_index = next; + return true; +} + +bool +journal_write_raw (const struct journal_payload *entries, size_t count) +{ + static uint64_t end_index = 0; + static uint64_t start_index = 0; + static bool offset_initialized = false; + + int fd = open (RAW_DEVICE_PATH, O_RDWR); + if (fd < 0) + { + dropped_txs += count; + fprintf (stderr, + "journal_write_raw: open failed: %s. Dropped %zu txs now and %zu since the start.\n", + strerror (errno), count, dropped_txs); + return false; + } + + if (!offset_initialized) + { + if (!initialize_indices (fd, &start_index, &end_index)) + { + dropped_txs += count; + fprintf (stderr, + "journal_write_raw: initialization failed. Dropped %zu txs now and %zu since the start.\n", + count, dropped_txs); + close (fd); + return false; + } + + //offset_initialized = true; + + if (!replayed_once) + { + journal_replay_and_validate (); + replayed_once = true; + } + } + + for (size_t i = 0; i < count; ++i) + { + if (!journal_write_indexed (fd, entries[i].data, entries[i].len, + &end_index, &start_index)) + { + dropped_txs += count; + fprintf (stderr, + "journal_write_raw: failed to write entry. Dropped %zu txs now and %zu since the start.\n", + count, dropped_txs); + close (fd); + return false; + } + } + + if (!persist_header_with_retry (fd, start_index, end_index, 3)) + fprintf (stderr, + "journal_write_raw: failed to persist updated header after retries.\n"); + + fprintf (stderr, "Toy journaling: wrote %zu entries to raw disk.\n", count); + close (fd); + return true; +} + diff --git a/libdiskfs/journal_writer.h b/libdiskfs/journal_writer.h new file mode 100644 index 00000000..86d1c653 --- /dev/null +++ b/libdiskfs/journal_writer.h @@ -0,0 +1,32 @@ +/* journal_writer.h - Interface for raw journal writer + + Copyright (C) 2025 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 the GNU Hurd; if not, see <https://www.gnu.org/licenses/>. */ + +#ifndef JOURNAL_WRITER_H +#define JOURNAL_WRITER_H + +#include <libdiskfs/journal_format.h> +#include <stdbool.h> +#include <stddef.h> + +bool journal_write_raw (const struct journal_payload *entries, size_t count); + +#endif /* JOURNAL_WRITER_H */ + diff --git a/libdiskfs/node-create.c b/libdiskfs/node-create.c index 5b5e4639..1cade14b 100644 --- a/libdiskfs/node-create.c +++ b/libdiskfs/node-create.c @@ -16,7 +16,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "priv.h" - +#include <libdiskfs/journal.h> /* This enables SysV style group behaviour. New nodes inherit the GID of the user creating them unless the SGID bit is set of the parent directory. */ @@ -60,6 +60,13 @@ diskfs_create_node (struct node *dir, np = *newnode; + struct journal_entry_info info = { + .action = "create", + .name = name, + .parent_ino = dir->dn_stat.st_ino + }; + journal_log_metadata(np, &info); + /* Initialize the on-disk fields. */ if (cred->user->uids->num) newuid = cred->user->uids->ids[0]; diff --git a/libdiskfs/shutdown.c b/libdiskfs/shutdown.c index 3f774c31..c761d603 100644 --- a/libdiskfs/shutdown.c +++ b/libdiskfs/shutdown.c @@ -21,6 +21,7 @@ #include "priv.h" #include <hurd/fsys.h> +#include <libdiskfs/journal.h> struct args { @@ -43,6 +44,7 @@ helper (void *cookie, const char *name, mach_port_t control) error_t diskfs_shutdown (int flags) { + journal_shutdown(); int nports = -1; error_t err; struct args args = { flags }; -- 2.40.1