agendafs

A filesystem for your calendar.

git clone git://mccd.space/agendafs
commit e8bc0c04171c9e69c3fd34901ce74845570f8dc6
Author: Marc Coquand <marc@coquand.email>
Date:   Fri, 30 May 2025 18:19:52 +0100

initial commit

Diffstat:
AMakefile | 11+++++++++++
Ajournalfs | 0
Amain.c | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asample_ics/2025-01-01.ics | 8++++++++
Ashell.nix | 13+++++++++++++
5 files changed, 122 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -0,0 +1,11 @@
+CC = gcc
+CFLAGS = -Wall -g
+LIBS = `pkg-config fuse3 --cflags --libs`
+
+all: journalfs
+
+journalfs: main.c
+	$(CC) $(CFLAGS) main.c -o journalfs $(LIBS)
+
+clean:
+	rm -f journalfs
diff --git a/journalfs b/journalfs
Binary files differ.
diff --git a/main.c b/main.c
@@ -0,0 +1,90 @@
+#define FUSE_USE_VERSION 31
+
+#include <fuse3/fuse.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+static const char *virtual_file_path = "/journal.txt";
+static const char *virtual_content = "This is a journal entry.\n";
+
+static int journal_getattr(const char *path, struct stat *stbuf,
+                           struct fuse_file_info *fi)
+{
+    (void) fi;
+    memset(stbuf, 0, sizeof(struct stat));
+
+    if (strcmp(path, "/") == 0) {
+        stbuf->st_mode = S_IFDIR | 0755;
+        stbuf->st_nlink = 2;
+    } else if (strcmp(path, virtual_file_path) == 0) {
+        stbuf->st_mode = S_IFREG | 0444;
+        stbuf->st_nlink = 1;
+        stbuf->st_size = strlen(virtual_content);
+    } else {
+        return -ENOENT;
+    }
+
+    return 0;
+}
+
+static int journal_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
+                           off_t offset, struct fuse_file_info *fi,
+                           enum fuse_readdir_flags flags)
+{
+    (void) offset;
+    (void) fi;
+    (void) flags;
+
+    if (strcmp(path, "/") != 0)
+        return -ENOENT;
+
+    filler(buf, ".", NULL, 0, 0);
+    filler(buf, "..", NULL, 0, 0);
+    filler(buf, virtual_file_path + 1, NULL, 0, 0); // remove leading slash
+
+    return 0;
+}
+
+static int journal_open(const char *path, struct fuse_file_info *fi)
+{
+    if (strcmp(path, virtual_file_path) != 0)
+        return -ENOENT;
+
+    // Only allow read-only
+    if ((fi->flags & O_ACCMODE) != O_RDONLY)
+        return -EACCES;
+
+    return 0;
+}
+
+static int journal_read(const char *path, char *buf, size_t size, off_t offset,
+                        struct fuse_file_info *fi)
+{
+    (void) fi;
+    size_t len = strlen(virtual_content);
+    if (strcmp(path, virtual_file_path) != 0)
+        return -ENOENT;
+
+    if (offset >= len)
+        return 0;
+
+    if (offset + size > len)
+        size = len - offset;
+
+    memcpy(buf, virtual_content + offset, size);
+    return size;
+}
+
+static const struct fuse_operations journal_oper = {
+    .getattr = journal_getattr,
+    .readdir = journal_readdir,
+    .open    = journal_open,
+    .read    = journal_read,
+};
+
+int main(int argc, char *argv[])
+{
+    return fuse_main(argc, argv, &journal_oper, NULL);
+}
+
diff --git a/sample_ics/2025-01-01.ics b/sample_ics/2025-01-01.ics
@@ -0,0 +1,8 @@
+BEGIN:VCALENDAR
+BEGIN:VJOURNAL
+UID:20250101T120000Z-001@host.com
+DTSTAMP:20250101T120000Z
+SUMMARY:New Year's Day Reflections
+DESCRIPTION:Reflecting on the past year and setting goals for the new one.
+END:VJOURNAL
+END:VCALENDAR
diff --git a/shell.nix b/shell.nix
@@ -0,0 +1,13 @@
+{
+  pkgs ? import <nixpkgs> { },
+}:
+
+pkgs.mkShell {
+  hardeningDisable = [ "all" ];
+  packages = with pkgs; [
+    clang-tools
+    fuse3
+    gdb
+    pkg-config
+  ];
+}