agendafs
A filesystem for your calendar.
git clone git://mccd.space/agendafscommit 1c531b7b619cb6e2a0d943e87aef435e625d68ee
parent 462e8c520836e770e96f0f99b8b369e7a910d65b
Author: Marc Coquand <marc@coquand.email>
Date: Tue, 22 Jul 2025 15:09:28 +0200
Code cleanup
Diffstat:
| M | agenda_entry.c | | | 47 | +++++++++++++++-------------------------------- |
| M | agenda_entry.h | | | 3 | ++- |
| M | main.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;
}
}