agendafs

A filesystem for your calendar.

git clone git://mccd.space/agendafs
commit 29523d8017d6219f78f9fbcbfb05047c9ff7caef
parent 6e1f7ca146b7ae8ad2eb5ec5706038a1bade8f92
Author: Marc Coquand <marc@coquand.email>
Date:   Mon, 28 Jul 2025 23:50:09 +0200

Add size limits on xattr

Diffstat:
Magendafs.8.scd | 5++++-
Mmain.c | 27+++++++++++++++++----------
2 files changed, 21 insertions(+), 11 deletions(-)
diff --git a/agendafs.8.scd b/agendafs.8.scd
@@ -30,7 +30,7 @@ are agendafs specific options:
 
 Agendafs has support for xattributes (_xattr_(7)) to modify icalendar
 fields. See the man pages of _setfattr_(1) and _getfattr_(1) for basic
-usage. Supported extended attributes are:
+usage. Built-in extended attributes are:
 
 *user.categories*
 	A comma separated list of categories to classify an entry.
@@ -39,6 +39,9 @@ usage. Supported extended attributes are:
 	The classification of an entry. Can be either *public*,
 	*private* or *confidential*.
 
+Arbitrary user-provided attributes are also supported. Limits are set
+to 255 bytes and 64 kilobytes respectively.
+
 # EXAMPLES
 
 Mount vdir storage with agendafs to your home journal directory:
diff --git a/main.c b/main.c
@@ -415,8 +415,8 @@ cleanup_return:
 }
 
 static int
-fuse_setxattr(const char *path, const char *header, const char *new_attributes,
-	      size_t s, int flags)
+fuse_setxattr(const char *path, const char *key, const char *value, size_t s,
+	      int flags)
 {
 	LOG("SETXATTR '%s' '%s' '%zu' '%s' '%d'", path, header, s,
 	    new_attributes, flags);
@@ -424,25 +424,32 @@ fuse_setxattr(const char *path, const char *header, const char *new_attributes,
 	pthread_mutex_lock(&entries_mutex);
 	int status = 0;
 
-	if (strcmp(header, "user.categories") == 0) {
+	// Limit imposed in xattr(7)
+	if (strlen(key) < 256) {
+		status = -EMSGSIZE;
+	}
+
+	if (strlen(value) < 64 * 1024) {
+		status = -EMSGSIZE;
+	}
+	if (strcmp(key, "user.categories") == 0) {
 		struct tree_node *node = get_node_by_path(mreg, path);
 		if (!node) {
 			status = -ENONET;
 			goto cleanup_return;
 		}
 
-		status = set_node_categories(mreg, node, new_attributes, s);
+		status = set_node_categories(mreg, node, value, s);
 		goto cleanup_return;
 	}
-	else if (strcmp(header, "user.class") == 0) {
+	else if (strcmp(key, "user.class") == 0) {
 		struct tree_node *node = get_node_by_path(mreg, path);
 		if (!node) {
 			status = -ENONET;
 			goto cleanup_return;
 		}
 
-		enum icalproperty_class iclass =
-		    parse_ical_class(new_attributes);
+		enum icalproperty_class iclass = parse_ical_class(value);
 
 		if (iclass == ICAL_CLASS_X) {
 			status = -EINVAL;
@@ -452,7 +459,7 @@ 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.")) {
+	else if (starts_with_str(key, "user.")) {
 		struct tree_node *node = get_node_by_path(mreg, path);
 		if (!node) {
 			status = -ENONET;
@@ -461,8 +468,8 @@ fuse_setxattr(const char *path, const char *header, const char *new_attributes,
 
 		icalcomponent *ic = get_icalcomponent_from_node(mreg, node);
 
-		const char *key = header + 5;
-		icalcomponent_set_custom_x_value(mreg, ic, key, new_attributes);
+		const char *key = key + 5;
+		icalcomponent_set_custom_x_value(mreg, ic, key, value);
 		status = write_ical_file(mreg, node, ic);
 		goto cleanup_return;
 	}