agendafs
A filesystem for your calendar.
git clone git://mccd.space/agendafscommit ee300309184acf712d973b6cd6e34dbb4a4007db
parent c39eefc45b224482cb68eb52f83fe397d5cf247d
Author: Marc Coquand <marc@coquand.email>
Date: Tue, 3 Jun 2025 15:02:18 +0100
Refactors
Diffstat:
| M | main.c | | | 235 | ++++++++++++++++++++++++++++++++++++++++++++++++------------------------------- |
1 file changed, 142 insertions(+), 93 deletions(-)
diff --git a/main.c b/main.c
@@ -53,6 +53,15 @@ get_ical_now()
icaltimezone_get_utc_timezone());
}
+// Full filepath of original ics file
+char *
+get_original_filepath(const journal_entry *entry)
+{
+ char *filepath = NULL;
+ asprintf(&filepath, "%s/%s", ICS_DIR, entry->filename_original);
+ return filepath;
+}
+
void
free_journal_entry(void *val)
{
@@ -94,8 +103,14 @@ get_entry_from_fuse_path(const char *path)
LOG("looking up key '%s'\n", key);
journal_entry *entry = hashmap_get(entries_fuse_key, key);
- if (!entry)
+
+ if (!entry) {
LOG("no entry found for key '%s'\n", key);
+ return NULL;
+ }
+
+ LOG("Entry is %s", entry->filename);
+ LOG("Entry original is %s", entry->filename_original);
return entry;
}
@@ -199,6 +214,47 @@ get_unique_filename(journal_entry *entry, struct hashmap *entries_map)
return unique_filename;
}
+// Returns -1 on failure, 0 on success
+int
+load_journal_entry(char *filename, journal_entry *entry)
+{
+ char *filepath = NULL;
+ if (asprintf(&filepath, "%s/%s", ICS_DIR, filename) == -1) {
+ perror("aspintf failed");
+ return -1;
+ }
+ entry->filename_original = strdup(filename);
+ if (!entry->filename_original) {
+ perror("strdup failed");
+ free(filepath);
+ return -1;
+ }
+
+ if (parse_ics_to_journal_entry(filepath, entry) != 0) {
+ LOG("Failed to parse entry: %s", filename);
+ free(filepath);
+ return -1;
+ }
+
+ LOG("Parsed %s", entry->filename_original);
+
+ // Make sure filename, which happens when
+ // Two entries are created on the same minute
+ char *unique_filename = get_unique_filename(entry, entries_fuse_key);
+ if (!unique_filename) {
+ LOG("Failed to generate filename for %s", filename);
+
+ free(filepath);
+ return -1;
+ }
+
+ entry->filename = unique_filename;
+ LOG("Got unique filename %s", unique_filename);
+
+ free(filepath);
+ return 0;
+}
+
// Initializes
// entries_original_key
// entries_fuse_key
@@ -221,65 +277,27 @@ load_journal_entries()
}
struct dirent *entry;
- char *filepath;
-
while ((entry = readdir(dir))) {
if (entry->d_type == DT_REG && strstr(entry->d_name, ".ics")) {
- if (asprintf(&filepath, "%s/%s", ICS_DIR,
- entry->d_name) == -1) {
- perror("aspintf failed");
- continue;
- }
-
journal_entry *new_entry =
xcalloc(1, sizeof(journal_entry));
- new_entry->filename_original = strdup(entry->d_name);
- if (!new_entry->filename_original) {
- perror("strdup failed");
-
- free(filepath);
- free_journal_entry(new_entry);
- continue;
- }
-
- if (parse_ics_to_journal_entry(filepath, new_entry) !=
- 0) {
- LOG("Failed to parse entry: %s", entry->d_name);
-
+ if (load_journal_entry(entry->d_name, new_entry) != 0) {
+ LOG("FAILED TO LOAD JOURNAL ENTRY");
free_journal_entry(new_entry);
continue;
}
- LOG("Parsed %s", new_entry->filename_original);
-
- // Make sure filename, which happens when
- // Two entries are created on the same minute
- char *unique_filename =
- get_unique_filename(new_entry, entries_fuse_key);
- if (!unique_filename) {
- LOG("Failed to generate filename for %s",
- entry->d_name);
-
- free_journal_entry(new_entry);
- continue;
- }
-
- new_entry->filename = unique_filename;
-
- if (hashmap_insert(entries_fuse_key, unique_filename,
+ if (hashmap_insert(entries_fuse_key,
+ new_entry->filename,
new_entry) != 0 ||
hashmap_insert(entries_original_key,
new_entry->filename_original,
new_entry) != 0) {
LOG("Failed to insert entry");
-
- free(filepath);
free_journal_entry(new_entry);
continue;
}
-
- free(filepath);
}
}
closedir(dir);
@@ -334,6 +352,7 @@ int
journal_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi)
{
pthread_mutex_lock(&entries_mutex);
+ LOG("STAT %s", path);
memset(stbuf, 0, sizeof(struct stat));
@@ -381,6 +400,8 @@ journal_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
{
pthread_mutex_lock(&entries_mutex);
+ LOG("READDIR %s", path);
+
size_t n_keys = 0;
char **keys = hashmap_get_keys(entries_fuse_key, &n_keys);
if (!keys) {
@@ -404,9 +425,8 @@ journal_open(const char *path, struct fuse_file_info *fi)
pthread_mutex_lock(&entries_mutex);
LOG("Opening journal for %s", path);
journal_entry *entry = get_entry_from_fuse_path(path);
- int ret = entry ? 0 : -ENOENT;
pthread_mutex_unlock(&entries_mutex);
- return ret;
+ return entry ? 0 : -ENOENT;
}
int
@@ -493,8 +513,11 @@ parse_entry_content(journal_entry *entry, const char *buf, size_t size)
}
size_t
-write_entry_to_ical_file(const journal_entry *entry, const char *filepath)
+write_entry_to_ical_file(const journal_entry *entry)
{
+
+ char *filepath = get_original_filepath(entry);
+
// Always set last modified to the time we write
icalproperty *prop = icalcomponent_get_first_property(
entry->component, ICAL_LASTMODIFIED_PROPERTY);
@@ -506,31 +529,28 @@ write_entry_to_ical_file(const journal_entry *entry, const char *filepath)
icalproperty_set_lastmodified(prop, get_ical_now());
}
+ LOG("Set last modified");
+
char *ical_str = icalcomponent_as_ical_string_r(entry->component);
+ LOG("Got ical_str %s", ical_str);
+
FILE *f = fopen(filepath, "w");
if (!f) {
perror("Failed to open ICS file");
free(ical_str);
+ free(filepath);
return -EIO;
}
fputs(ical_str, f);
fclose(f);
free(ical_str);
+ free(filepath);
return 0;
}
-// Full filepath of original ics file
-char *
-get_original_filepath(const journal_entry *entry)
-{
- char *filepath = NULL;
- asprintf(&filepath, "%s/%s", ICS_DIR, entry->filename_original);
- return filepath;
-}
-
int
journal_write(const char *path, const char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
@@ -549,13 +569,10 @@ journal_write(const char *path, const char *buf, size_t size, off_t offset,
return ret;
}
- char *filepath = get_original_filepath(entry);
- if (write_entry_to_ical_file(entry, filepath) != 0) {
- free(filepath);
+ if (write_entry_to_ical_file(entry) != 0) {
pthread_mutex_unlock(&entries_mutex);
return -EIO;
}
- free(filepath);
pthread_mutex_unlock(&entries_mutex);
return size;
}
@@ -631,15 +648,10 @@ do_journal_entry_rename(time_t new_created_at, const char *old, const char *new)
new_entry);
hashmap_insert(entries_fuse_key, new_entry->filename, new_entry);
- // Write back to the original
- char *filepath = get_original_filepath(new_entry);
-
- if (write_entry_to_ical_file(new_entry, filepath) != 0) {
- free(filepath);
+ if (write_entry_to_ical_file(new_entry) != 0) {
return -EIO;
}
- free(filepath);
return 0;
}
@@ -731,7 +743,8 @@ create_new_unique_ics_uid()
icalcomponent *
create_vjournal_entry(time_t created_at)
{
- struct icaltimetype created_at_ical = get_ical_now();
+ struct icaltimetype created_at_ical = icaltime_from_timet_with_zone(
+ created_at, 0, icaltimezone_get_utc_timezone());
icalcomponent *calendar = icalcomponent_new_vcalendar();
@@ -744,6 +757,7 @@ create_vjournal_entry(time_t created_at)
icalcomponent_add_property(
journal, icalproperty_new_uid(create_new_unique_ics_uid()));
+
icalcomponent_add_property(journal,
icalproperty_new_dtstamp(created_at_ical));
icalcomponent_add_property(journal, icalproperty_new_summary(""));
@@ -770,8 +784,20 @@ journal_create(const char *filename, mode_t mode, struct fuse_file_info *info)
pthread_mutex_unlock(&entries_mutex);
return -EINVAL;
}
+
+ LOG("Got created at");
journal_entry *new_entry = xcalloc(1, sizeof(journal_entry));
- create_vjournal_entry(created_at);
+ icalcomponent *vjournal_component = create_vjournal_entry(created_at);
+
+ new_entry->component = vjournal_component;
+ new_entry->filename = strdup(new_basename);
+ if (asprintf(&new_entry->filename_original, "%s.ics",
+ icalcomponent_get_uid(new_entry->component)) == -1) {
+ LOG("Failed to create journal entry");
+ free_journal_entry(new_entry);
+ return -ENOMEM;
+ }
+ LOG("Inserting vjournal entry");
hashmap_insert(entries_original_key, new_entry->filename_original,
new_entry);
@@ -779,8 +805,11 @@ journal_create(const char *filename, mode_t mode, struct fuse_file_info *info)
// Write back to the original
char *filepath = get_original_filepath(new_entry);
+ LOG("Got original filepath %s. From %s, and %s", filepath,
+ new_entry->filename_original, new_entry->filename);
- if (write_entry_to_ical_file(new_entry, filepath) != 0) {
+ if (write_entry_to_ical_file(new_entry) != 0) {
+ LOG("Write to entry failed");
free(filepath);
return -EIO;
}
@@ -792,36 +821,22 @@ journal_create(const char *filename, mode_t mode, struct fuse_file_info *info)
}
void
-update_fuse_entry_from_original(const char *filename)
+update_or_create_fuse_entry_from_original(const char *filename)
{
pthread_mutex_lock(&entries_mutex);
- const char *base_name = strrchr(filename, '/');
- journal_entry *entry = hashmap_get(entries_original_key, base_name);
- // TODO: Create entry when file is not found
- if (!entry) {
- LOG("Entry not found. Registering new file");
- pthread_mutex_unlock(&entries_mutex);
- return;
- }
+ char *base_name = strrchr(filename, '/');
journal_entry *new_entry = xcalloc(1, sizeof(journal_entry));
- if (!new_entry) {
- perror("calloc");
- pthread_mutex_unlock(&entries_mutex);
- return;
- }
- LOG("Parsing ICS file %s", filename);
-
- if (parse_ics_to_journal_entry(filename, new_entry) != 0) {
- // Parsing failed, clean up and maybe remove old entry if any
+ if (load_journal_entry(base_name, new_entry) != 0) {
+ // Parsing failed, clean up and maybe remove old entry
+ // if any
+ LOG("PARSING FAILED");
free_journal_entry(new_entry);
pthread_mutex_unlock(&entries_mutex);
return;
}
-
- new_entry->filename = strndup(entry->filename, strlen(entry->filename));
- new_entry->filename_original =
- strndup(entry->filename_original, strlen(entry->filename_original));
+ pthread_mutex_unlock(&entries_mutex);
+ return;
hashmap_insert(entries_original_key, new_entry->filename_original,
new_entry);
@@ -832,6 +847,21 @@ update_fuse_entry_from_original(const char *filename)
pthread_mutex_unlock(&entries_mutex);
}
+int
+update_fuse_entry_delete(const char *file)
+{
+ pthread_mutex_lock(&entries_mutex);
+ // Strip path, validate just the new basename
+ const char *keyname = strrchr(file, '/');
+ keyname++;
+ LOG("Found entry to delete");
+ hashmap_remove(entries_original_key, keyname);
+ // Frees
+ hashmap_remove(entries_fuse_key, keyname);
+ pthread_mutex_unlock(&entries_mutex);
+ return 0;
+}
+
void *
watch_ics_dir(void *arg)
{
@@ -842,7 +872,8 @@ watch_ics_dir(void *arg)
}
// TODO: Fix IN_DELETE
- int wd = inotify_add_watch(fd, ICS_DIR, IN_CREATE | IN_MODIFY);
+ int wd =
+ inotify_add_watch(fd, ICS_DIR, IN_CREATE | IN_MODIFY | IN_DELETE);
if (wd < 0) {
perror("inotify_add_watch");
@@ -875,8 +906,17 @@ watch_ics_dir(void *arg)
LOG("Detected change in ICS file: %s\n",
event->name);
- // Reload entries on any change
- update_fuse_entry_from_original(full_path);
+ if (event->mask & IN_DELETE) {
+ update_fuse_entry_delete(full_path);
+ }
+ else if (event->mask & IN_MODIFY) {
+
+ // Reload entries on any change
+ update_or_create_fuse_entry_from_original(
+ full_path);
+ }
+ else if (event->mask & IN_CREATE) {
+ }
}
free(full_path);
@@ -889,10 +929,19 @@ watch_ics_dir(void *arg)
return NULL;
}
+// Ignore for now
+static int
+journal_utimens(const char *path, const struct timespec tv[2],
+ struct fuse_file_info *info)
+{
+ return 0;
+}
+
static const struct fuse_operations journal_oper = {.getattr = journal_getattr,
.readdir = journal_readdir,
.open = journal_open,
.read = journal_read,
+ .utimens = journal_utimens,
.write = journal_write,
.create = journal_create,
.unlink = journal_unlink,