agendafs
A filesystem for your calendar.
git clone git://mccd.space/agendafscommit 6e1f7ca146b7ae8ad2eb5ec5706038a1bade8f92
parent 44dfa5e43b07a58410e0e7de00254a67b89917cf
Author: Marc Coquand <marc@coquand.email>
Date: Mon, 28 Jul 2025 23:42:35 +0200
Support arbitrary extensions
Implements: https://todo.sr.ht/~marcc/agendafs/14
Diffstat:
6 files changed, 144 insertions(+), 2 deletions(-)
diff --git a/fuse_node.c b/fuse_node.c
@@ -356,6 +356,7 @@ append_category_to_memstream(FILE *memstream, const char *category,
char *
get_node_categories(memory_region *mreg, const struct tree_node *node)
{
+ // TODO: Take this as parameter instead...
icalcomponent *component = get_icalcomponent_from_node(mreg, node);
if (!component) {
return NULL;
@@ -452,6 +453,8 @@ set_node_categories(memory_region *mreg, const struct tree_node *node,
const char *
get_node_class(memory_region *mreg, const struct tree_node *node)
{
+
+ // TODO: Take this as parameter instead...
icalcomponent *component = get_icalcomponent_from_node(mreg, node);
if (!component) {
return 0;
diff --git a/ical_extra.c b/ical_extra.c
@@ -3,10 +3,13 @@
#include "mregion.h"
#include "path.h"
#include "sys/stat.h"
+#include "util.h"
#include "uuid/uuid.h"
#include <stdbool.h>
const char *IS_DIRECTORY_PROPERTY = "X-CALDAVFS-ISDIRECTORY";
const char *FILE_EXTENSION_PROPERTY = "X-CALDAVFS-FILEEXT";
+const char *CUSTOM_PROPERTY_PREFIX = "X-CALDAVFS-CUSTOM-";
+size_t CUSTOM_PROPERTY_PREFIX_LEN = 18;
const char *
icalcomponent_get_uniq_x_value(icalcomponent *component, const char *key)
@@ -336,6 +339,36 @@ icalcomponent_remove_x_prop(icalcomponent *component, const char *key)
}
void
+icalcomponent_write_x_props(FILE *memstream, icalcomponent *component)
+{
+ icalcomponent *inner = icalcomponent_get_innermost(component);
+ icalproperty *prop_iter =
+ icalcomponent_get_first_property(inner, ICAL_X_PROPERTY);
+ while (prop_iter != NULL) {
+ icalproperty *next_prop =
+ icalcomponent_get_next_property(inner, ICAL_X_PROPERTY);
+ const char *prop_name = icalproperty_get_x_name(prop_iter);
+ if (starts_with_str(prop_name, CUSTOM_PROPERTY_PREFIX)) {
+ const char *key =
+ prop_name + CUSTOM_PROPERTY_PREFIX_LEN;
+ fprintf(memstream, "user.%s", key);
+ fputc('\0', memstream);
+ }
+ prop_iter = next_prop;
+ }
+}
+
+void
+icalcomponent_remove_custom_prop(memory_region *mreg, icalcomponent *component,
+ const char *key)
+{
+
+ char *x_key = NULL;
+ rasprintf(mreg, &x_key, "%s%s", CUSTOM_PROPERTY_PREFIX, key);
+ icalcomponent_remove_x_prop(component, x_key);
+}
+
+void
icalcomponent_set_unique_x_value(icalcomponent *component, const char *key,
const char *value)
{
@@ -350,9 +383,29 @@ icalcomponent_set_unique_x_value(icalcomponent *component, const char *key,
icalcomponent_add_property(inner, fileext_prop);
}
+void
+icalcomponent_set_custom_x_value(memory_region *mreg, icalcomponent *component,
+ const char *key, const char *value)
+{
+ char *x_key = NULL;
+ rasprintf(mreg, &x_key, "%s%s", CUSTOM_PROPERTY_PREFIX, key);
+
+ icalcomponent_set_unique_x_value(component, x_key, value);
+}
+
+const char *
+icalcomponent_get_custom_x_value(memory_region *mreg, icalcomponent *component,
+ const char *key)
+{
+ char *x_key = NULL;
+ rasprintf(mreg, &x_key, "%s%s", CUSTOM_PROPERTY_PREFIX, key);
+
+ return icalcomponent_get_uniq_x_value(component, x_key);
+}
-// Since empty values are considered null, we store a disallowed extension (..)
-// to indicate that the system has explicitly not set an extension.
+// Since empty values are considered null, we store a disallowed
+// extension (..) to indicate that the system has explicitly not set an
+// extension.
void
icalcomponent_set_file_extension(icalcomponent *component,
const char *extension)
diff --git a/ical_extra.h b/ical_extra.h
@@ -61,4 +61,20 @@ icalcomponent_get_file_extension(icalcomponent *component);
void
icalcomponent_mark_as_directory(icalcomponent *ic);
+
+void
+icalcomponent_set_custom_x_value(memory_region *mreg, icalcomponent *component,
+ const char *key, const char *value);
+
+const char *
+icalcomponent_get_custom_x_value(memory_region *mreg, icalcomponent *component,
+ const char *key);
+
+void
+icalcomponent_remove_custom_prop(memory_region *mreg, icalcomponent *component,
+ const char *key);
+
+void
+icalcomponent_write_x_props(FILE *memstream, icalcomponent *component);
+
#endif // ical_extra_h_INCLUDED
diff --git a/main.c b/main.c
@@ -393,6 +393,20 @@ fuse_removexattr(const char *path, const char *header)
status = delete_node_class(mreg, node);
goto cleanup_return;
}
+ else if (starts_with_str(header, "user.")) {
+ struct tree_node *node = get_node_by_path(mreg, path);
+ if (!node) {
+ status = -ENONET;
+ goto cleanup_return;
+ }
+
+ icalcomponent *ic = get_icalcomponent_from_node(mreg, node);
+
+ const char *key = header + 5;
+ icalcomponent_remove_custom_prop(mreg, ic, key);
+ status = write_ical_file(mreg, node, ic);
+ goto cleanup_return;
+ }
cleanup_return:
pthread_mutex_unlock(&entries_mutex);
@@ -438,6 +452,20 @@ fuse_setxattr(const char *path, const char *header, const char *new_attributes,
status = set_node_class(mreg, node, iclass);
goto cleanup_return;
}
+ else if (starts_with_str(header, "user.")) {
+ struct tree_node *node = get_node_by_path(mreg, path);
+ if (!node) {
+ status = -ENONET;
+ goto cleanup_return;
+ }
+
+ icalcomponent *ic = get_icalcomponent_from_node(mreg, node);
+
+ const char *key = header + 5;
+ icalcomponent_set_custom_x_value(mreg, ic, key, new_attributes);
+ status = write_ical_file(mreg, node, ic);
+ goto cleanup_return;
+ }
else {
status = -EINVAL;
}
@@ -466,6 +494,14 @@ fuse_listxattr(const char *path, char *list, size_t size)
goto cleanup_return;
}
+ if (is_root_node(node)) {
+ return_i = -EPERM;
+ goto cleanup_return;
+ }
+
+ icalcomponent *comp = get_icalcomponent_from_node(mreg, node);
+ assert(comp);
+
stream = open_memstream(&membuffer, &membuffer_len);
if (stream == NULL) {
return_i = -EIO;
@@ -484,6 +520,8 @@ fuse_listxattr(const char *path, char *list, size_t size)
fputc('\0', stream);
}
+ icalcomponent_write_x_props(stream, comp);
+
if (fclose(stream) != 0) {
return_i = -EIO;
goto cleanup_return;
@@ -562,6 +600,28 @@ fuse_getxattr(const char *path, const char *header, char *buf, size_t s)
status = string_len;
goto cleanup_return;
}
+ else if (starts_with_str(header, "user.")) {
+ struct tree_node *node = get_node_by_path(mreg, path);
+ if (!node) {
+ status = -ENONET;
+ goto cleanup_return;
+ }
+
+ icalcomponent *ic = get_icalcomponent_from_node(mreg, node);
+
+ const char *key = header + 5;
+ const char *value =
+ icalcomponent_get_custom_x_value(mreg, ic, key);
+ if (!value) {
+ status = -ENODATA;
+ goto cleanup_return;
+ }
+
+ int string_len = strlen(value);
+ snprintf(buf, s, "%s", value);
+ status = string_len;
+ goto cleanup_return;
+ }
cleanup_return:
pthread_mutex_unlock(&entries_mutex);
diff --git a/util.c b/util.c
@@ -65,3 +65,10 @@ xreallocarray(void *buf, size_t n, size_t m)
return buf;
}
+bool
+starts_with_str(const char *str, const char *prefix)
+{
+ size_t len = strlen(prefix);
+ return strncmp(str, prefix, len) == 0;
+}
+
diff --git a/util.h b/util.h
@@ -1,6 +1,7 @@
#ifndef UTIL_H
#define UTIL_H
+#include <stdbool.h>
#include <stdlib.h>
#ifdef DEBUG
@@ -20,4 +21,6 @@ void *
xcalloc(size_t, size_t);
char *
xstrdup(const char *s);
+bool
+starts_with_str(const char *str, const char *prefix);
#endif