agendafs

A filesystem for your calendar.

git clone git://mccd.space/agendafs
commit 1c531b7b619cb6e2a0d943e87aef435e625d68ee
parent 462e8c520836e770e96f0f99b8b369e7a910d65b
Author: Marc Coquand <marc@coquand.email>
Date:   Tue, 22 Jul 2025 15:09:28 +0200

Code cleanup

Diffstat:
Magenda_entry.c | 47+++++++++++++++--------------------------------
Magenda_entry.h | 3++-
Mmain.c | 180+++++++++++++++++++++++++++++++++----------------------------------------------
3 files changed, 92 insertions(+), 138 deletions(-)
diff --git a/agenda_entry.c b/agenda_entry.c
@@ -22,6 +22,10 @@
 const char *IS_DIRECTORY_PROPERTY = "X-CALDAVFS-ISDIRECTORY";
 char ICS_DIR[256];
 
+// Most memory is managed by memory_region, two exceptions are the
+// global states fuse_root and entries_original_ics, which manages its own
+// resources.
+
 // A structure of the current root
 struct tree_node *fuse_root = NULL;
 // ics entries, keys are the filename as stored in ICS_DIR
@@ -200,18 +204,11 @@ parse_ics_file(memory_region *mreg, const char *filename)
 	LOG("filename is %s", filename);
 
 	struct stat st;
-
-	LOG("Stating...");
-	if (stat(filename, &st) != 0) {
-		LOG("Failed to stat file: %s", filename);
-		exit(1);
-	}
+	assert(stat(filename, &st) == 0);
 
 	size_t size = st.st_size;
-	LOG("rmallocing...");
 	char *buffer = rmalloc(mreg, size + 1);
 
-	LOG("freading...");
 	size_t bytes_read = fread(buffer, 1, size, file);
 	buffer[bytes_read] = '\0';
 	fclose(file);
@@ -255,12 +252,8 @@ write_ical_file(memory_region *mreg, const struct tree_node *node,
 					      ical_descr_prop);
 	}
 
-	LOG("Set last modified");
-
 	char *ical_str = ricalcomponent_as_ical_string_r(mreg, ic);
 
-	LOG("Got ical_str %s", ical_str);
-
 	int res = write_to_file(get_original_filepath(mreg, node), ical_str);
 
 	return res;
@@ -450,11 +443,9 @@ load_agenda_entry_from_ics_file(memory_region *mreg, const char *filename)
 void
 load_root_node_tree()
 {
-	LOG("Allocating journal entries");
 	entries_original_ics = hashmap_new(NULL);
 	fuse_root = create_tree_node(NULL, NULL);
 	memory_region *mreg = create_region();
-
 	LOG("Loading journal entries from: %s\n", ICS_DIR);
 
 	DIR *dir = opendir(ICS_DIR);
@@ -464,7 +455,6 @@ load_root_node_tree()
 	}
 
 	struct dirent *entry;
-	LOG("Load files to root directory");
 	while ((entry = readdir(dir))) {
 		if (entry->d_type == DT_REG && strstr(entry->d_name, ".ics")) {
 			LOG("Loading %s", entry->d_name);
@@ -577,7 +567,7 @@ append_category_to_memstream(FILE *memstream, const char *category,
 
 	res = fputs(category, memstream);
 	assert(res != EOF);
-	return 0; // Success
+	return 0;
 }
 
 // Returns comma separated list of categories for a file
@@ -591,6 +581,7 @@ get_node_categories(memory_region *mreg, const struct tree_node *node)
 
 	FILE *memstream = NULL;
 	char *result_buffer = NULL;
+	region_register(mreg, result_buffer, free);
 	size_t buffer_size = 0;
 
 	memstream = open_memstream(&result_buffer, &buffer_size);
@@ -783,7 +774,7 @@ create_new_unique_ics_uid(memory_region *mreg)
 }
 
 icalcomponent *
-create_vjournal_entry(memory_region *mreg, char *summary)
+create_vjournal_entry(memory_region *mreg, const char *summary)
 {
 
 	icalcomponent *calendar = ricalcomponent_new_vcalendar(mreg);
@@ -875,9 +866,6 @@ create_vjournal_directory(memory_region *mreg, const char *summary)
 			 icaltimezone_get_utc_timezone())));
 	icalcomponent_add_property(journal, icalproperty_new_summary(summary));
 
-	// Empty description; unused
-	icalcomponent_add_property(journal, icalproperty_new_description(""));
-
 	icalproperty *is_directory = icalproperty_new_x(IS_DIRECTORY_PROPERTY);
 	icalproperty_set_x_name(is_directory, IS_DIRECTORY_PROPERTY);
 	icalproperty_set_x(is_directory, "YES");
@@ -890,16 +878,17 @@ create_vjournal_directory(memory_region *mreg, const char *summary)
 int
 create_entry_from_fuse(memory_region *mreg, const char *fuse_path)
 {
-	// TODO: Move to path.c
+	// TODO: We are mixing some responsibilities here.
+	// Any function that operates on the global state should not
+	// have memory_region as a parameter, as that makes it easier
+	// to mix up the contexts.
 	const char *entry_prefix = "/";
 	if (strncmp(fuse_path, entry_prefix, strlen(entry_prefix)) != 0) {
 		return -EINVAL;
 	}
-	char *new_basename = strrchr(fuse_path, '/');
-	new_basename++;
+	const char *new_basename = get_filename(fuse_path);
 
-	struct agenda_entry *new_entry =
-	    xcalloc(1, sizeof(struct agenda_entry));
+	struct agenda_entry *new_entry = xmalloc(sizeof(struct agenda_entry));
 	icalcomponent *new_component =
 	    create_vjournal_entry(mreg, new_basename);
 
@@ -949,7 +938,7 @@ create_entry_from_fuse(memory_region *mreg, const char *fuse_path)
 		return -EIO;
 	}
 
-	LOG("DIRECTORY CREATED");
+	LOG("FILE CREATED");
 	return 0;
 }
 
@@ -1031,12 +1020,6 @@ update_or_create_fuse_entry_from_original(memory_region *mreg,
 	struct agenda_entry *updated_entry_owned =
 	    copy_agenda_entry(updated_entry);
 
-	if (!updated_entry_owned) {
-		LOG("PARSING FAILED");
-		// TODO: Error code
-		return;
-	}
-
 	struct tree_node *new_or_updated_node =
 	    hashmap_get(entries_original_ics, filename_original);
 
diff --git a/agenda_entry.h b/agenda_entry.h
@@ -162,7 +162,8 @@ char *
 create_new_unique_ics_uid(memory_region *mreg);
 
 icalcomponent *
-create_vjournal_entry(memory_region *mreg, char *summary);
+create_vjournal_entry(memory_region *mreg, const char *summary);
+
 uid_t
 get_node_uid(const struct tree_node *node);
 
diff --git a/main.c b/main.c
@@ -1,8 +1,8 @@
-#include "mregion.h"
 #define FUSE_USE_VERSION 31
 
 #include "agenda_entry.h"
 #include "hashmap.h"
+#include "mregion.h"
 #include "tree.h"
 #include "util.h"
 #include <dirent.h>
@@ -359,41 +359,34 @@ journal_removexattr(const char *path, const char *header)
 {
 	LOG("REMOVEXATTR '%s' '%s'", path, header);
 	memory_region *mreg = create_region();
+	int status = -EINVAL;
+	pthread_mutex_lock(&entries_mutex);
 
 	if (strcmp(header, "user.categories") == 0) {
-		pthread_mutex_lock(&entries_mutex);
 		struct tree_node *node = get_node_by_path(mreg, path);
 		if (!node) {
-			pthread_mutex_unlock(&entries_mutex);
-
-			rfree_all(mreg);
-			return -ENONET;
+			status = -ENONET;
+			goto cleanup_return;
 		}
 
-		int status = set_node_categories(mreg, node, "", 0);
-		pthread_mutex_unlock(&entries_mutex);
-
-		rfree_all(mreg);
-		return status;
+		status = set_node_categories(mreg, node, "", 0);
+		goto cleanup_return;
 	}
 	else if (strcmp(header, "user.class") == 0) {
-		pthread_mutex_lock(&entries_mutex);
 		struct tree_node *node = get_node_by_path(mreg, path);
 		if (!node) {
-			pthread_mutex_unlock(&entries_mutex);
-			rfree_all(mreg);
-			return -ENONET;
+			status = -ENONET;
+			goto cleanup_return;
 		}
 
-		int status = delete_node_class(mreg, node);
-		pthread_mutex_unlock(&entries_mutex);
-
-		rfree_all(mreg);
-		return status;
+		status = delete_node_class(mreg, node);
+		goto cleanup_return;
 	}
-	rfree_all(mreg);
 
-	return -EINVAL;
+cleanup_return:
+	pthread_mutex_unlock(&entries_mutex);
+	rfree_all(mreg);
+	return status;
 }
 
 static int
@@ -403,51 +396,42 @@ journal_setxattr(const char *path, const char *header,
 	LOG("SETXATTR '%s' '%s' '%zu' '%s' '%d'", path, header, s,
 	    new_attributes, flags);
 	memory_region *mreg = create_region();
+	int status = -EINVAL;
+	pthread_mutex_lock(&entries_mutex);
 
 	if (strcmp(header, "user.categories") == 0) {
-		pthread_mutex_lock(&entries_mutex);
 		struct tree_node *node = get_node_by_path(mreg, path);
 		if (!node) {
-			pthread_mutex_unlock(&entries_mutex);
-			rfree_all(mreg);
-
-			return -ENONET;
+			status = -ENONET;
+			goto cleanup_return;
 		}
 
-		int status = set_node_categories(mreg, node, new_attributes, s);
-		pthread_mutex_unlock(&entries_mutex);
-
-		return status;
+		status = set_node_categories(mreg, node, new_attributes, s);
+		goto cleanup_return;
 	}
 	else if (strcmp(header, "user.class") == 0) {
-		pthread_mutex_lock(&entries_mutex);
 		struct tree_node *node = get_node_by_path(mreg, path);
 		if (!node) {
-			pthread_mutex_unlock(&entries_mutex);
-
-			rfree_all(mreg);
-			return -ENONET;
+			status = -ENONET;
+			goto cleanup_return;
 		}
 
 		enum icalproperty_class iclass =
 		    parse_ical_class(new_attributes);
 
 		if (iclass == ICAL_CLASS_X) {
-			pthread_mutex_unlock(&entries_mutex);
-
-			rfree_all(mreg);
-			return -EINVAL;
+			status = -EINVAL;
+			goto cleanup_return;
 		}
 
-		int status = set_node_class(mreg, node, iclass);
-		pthread_mutex_unlock(&entries_mutex);
-		rfree_all(mreg);
-
-		return status;
+		status = set_node_class(mreg, node, iclass);
+		goto cleanup_return;
 	}
 
+cleanup_return:
+	pthread_mutex_unlock(&entries_mutex);
 	rfree_all(mreg);
-	return -EINVAL;
+	return status;
 }
 
 static int
@@ -463,6 +447,7 @@ journal_listxattr(const char *path, char *list, size_t size)
 	struct tree_node *node = get_node_by_path(mreg, path);
 	if (!node) {
 		pthread_mutex_unlock(&entries_mutex);
+		rfree_all(mreg);
 
 		return -ENONET;
 	}
@@ -470,6 +455,7 @@ journal_listxattr(const char *path, char *list, size_t size)
 	stream = open_memstream(&membuffer, &membuffer_len);
 	if (stream == NULL) {
 		pthread_mutex_unlock(&entries_mutex);
+		rfree_all(mreg);
 		return -EIO;
 	}
 	char *cats = get_node_categories(mreg, node);
@@ -489,6 +475,7 @@ journal_listxattr(const char *path, char *list, size_t size)
 		free(membuffer);
 
 		pthread_mutex_unlock(&entries_mutex);
+		rfree_all(mreg);
 		return -EIO;
 	}
 
@@ -511,7 +498,6 @@ static int
 journal_getxattr(const char *path, const char *header, char *buf, size_t s)
 {
 	LOG("GETXATTR '%s' '%s' '%zu'\n", path, header, s);
-	fuse_get_context();
 	memory_region *mreg = create_region();
 
 	if (strcmp(header, "user.categories") == 0) {
@@ -519,7 +505,7 @@ journal_getxattr(const char *path, const char *header, char *buf, size_t s)
 		struct tree_node *node = get_node_by_path(mreg, path);
 		if (!node) {
 			pthread_mutex_unlock(&entries_mutex);
-
+			rfree_all(mreg);
 			return -ENONET;
 		}
 
@@ -527,6 +513,7 @@ journal_getxattr(const char *path, const char *header, char *buf, size_t s)
 		pthread_mutex_unlock(&entries_mutex);
 
 		if (!cats || *cats == '\0') {
+			rfree_all(mreg);
 			return -ENODATA;
 		}
 
@@ -538,7 +525,6 @@ journal_getxattr(const char *path, const char *header, char *buf, size_t s)
 			snprintf(buf, s, "%s", cats);
 		}
 		rfree_all(mreg);
-
 		return string_len;
 	}
 	else if (strcmp(header, "user.class") == 0) {
@@ -573,83 +559,67 @@ journal_getxattr(const char *path, const char *header, char *buf, size_t s)
 	return -ENODATA;
 }
 
+static void
+handle_ics_event(struct inotify_event *event)
+{
+	if (!event->len || !strstr(event->name, ".ics"))
+		return;
+
+	LOG("Detected change in ICS file: %s\n", event->name);
+
+	memory_region *mreg = create_region();
+	char *full_path = NULL;
+	rasprintf(mreg, &full_path, "%s/%s", ICS_DIR, event->name);
+
+	pthread_mutex_lock(&entries_mutex);
+
+	if (event->mask & IN_DELETE) {
+		delete_from_original_path(mreg, full_path);
+	}
+	else if (event->mask & IN_MODIFY) {
+		LOG("RUNNING IN_MODIFY HOOK");
+		update_or_create_fuse_entry_from_original(mreg, full_path);
+	}
+	else if (event->mask & IN_CREATE) {
+		LOG("RUNNING IN_CREATE HOOK");
+		update_or_create_fuse_entry_from_original(mreg, full_path);
+	}
+
+	pthread_mutex_unlock(&entries_mutex);
+	rfree_all(mreg);
+}
+
 static void *
 watch_ics_dir(void *arg)
 {
 	int fd = inotify_init1(IN_NONBLOCK);
-	if (fd < 0) {
-		perror("inotify_init");
-		return NULL;
-	}
+	assert(fd >= 0);
 
-	// TODO: Fix IN_DELETE
 	int wd =
 	    inotify_add_watch(fd, ICS_DIR, IN_CREATE | IN_MODIFY | IN_DELETE);
-
-	if (wd < 0) {
-		perror("inotify_add_watch");
-		close(fd);
-		return NULL;
-	}
+	assert(wd >= 0);
 
 	char buffer[EVENT_BUF_LEN];
 
 	while (1) {
 		ssize_t length = read(fd, buffer, EVENT_BUF_LEN);
 
-		// No event, sleep and check again
 		if (length < 0) {
-			if (errno == EAGAIN || errno == EINTR) {
-				usleep(100000);
-				continue;
-			}
-			perror("read");
-			break;
+			// No event, sleep and check again
+			assert(errno == EAGAIN || errno == EINTR);
+			usleep(100000);
+			continue;
 		}
 
 		size_t i = 0;
 		while (i < length) {
 			struct inotify_event *event =
 			    (struct inotify_event *)&buffer[i];
+			handle_ics_event(event);
+			size_t event_size =
+			    sizeof(struct inotify_event) + event->len;
 
-			char *full_path = NULL;
-			asprintf(&full_path, "%s/%s", ICS_DIR, event->name);
-
-			if (event->len && strstr(event->name, ".ics")) {
-				LOG("Detected change in ICS file: %s\n",
-				    event->name);
-				memory_region *mreg = create_region();
-
-				if (event->mask & IN_DELETE) {
-					pthread_mutex_lock(&entries_mutex);
-					delete_from_original_path(mreg,
-								  full_path);
-					rfree_all(mreg);
-					pthread_mutex_unlock(&entries_mutex);
-				}
-				else if (event->mask & IN_MODIFY) {
-					pthread_mutex_lock(&entries_mutex);
-					LOG("RUNNING IN_MODIFY HOOK");
-					update_or_create_fuse_entry_from_original(
-					    mreg, full_path);
-					rfree_all(mreg);
-					pthread_mutex_unlock(&entries_mutex);
-				}
-				else if (event->mask & IN_CREATE) {
-					pthread_mutex_lock(&entries_mutex);
-					LOG("RUNNING IN_CREATE HOOK");
-					update_or_create_fuse_entry_from_original(
-					    mreg, full_path);
-					rfree_all(mreg);
-					pthread_mutex_unlock(&entries_mutex);
-				}
-				else {
-					rfree_all(mreg);
-				}
-			}
-			free(full_path);
-
-			i += sizeof(struct inotify_event) + event->len;
+			i += event_size;
 		}
 	}