agendafs

A filesystem for your calendar.

git clone git://mccd.space/agendafs
commit 9e88f3cbc71e562b1f2f20c96f3f60593d843630
parent 1aff4c96ef6d5f6deb6c34246f72b9aa544a34c2
Author: Marc Coquand <marc@coquand.email>
Date:   Fri, 27 Jun 2025 12:01:39 +0100

Updates

Diffstat:
Mjournal_entry.c | 94++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Mjournal_entry.h | 14+++++++++++++-
Mmain.c | 42+++++++++++++++++++++++++++++++++++++++---
Mtree.c | 6++++++
Mtree.h | 3+++
5 files changed, 142 insertions(+), 17 deletions(-)
diff --git a/journal_entry.c b/journal_entry.c
@@ -382,15 +382,15 @@ is_directory_component(icalcomponent *component)
 // We assume it to be a directory if it has a property directory property
 // so or is a parent node
 bool
-entry_is_directory(tree_node *node)
+node_is_directory(tree_node *node)
 {
-	if (!node->data) {
-		return true;
-	}
-	else if (node->child_count > 0) {
+	if (is_root_node(node) || node_has_children(node)) {
 		return true;
 	}
 	else {
+		// When user creates a directory, a directory component is
+		// stored in the ics file. So even if a node has no children
+		// it can still be that it should be shown as a directory.
 		struct journal_entry *entry = node->data;
 		return is_directory_component(entry->component);
 	}
@@ -484,10 +484,18 @@ int
 load_journal_entry_from_ics_file(char *filename, struct journal_entry *entry)
 {
 	char *filepath = NULL;
+
 	if (asprintf(&filepath, "%s/%s", ICS_DIR, filename) == -1) {
 		perror("aspintf failed");
 		return -1;
 	}
+	struct stat fileStat;
+	if (stat(filepath, &fileStat) == -1) {
+		LOG("Can not stat %s. skipping", filepath);
+		free(filepath);
+		return -1;
+	}
+
 	entry->filename_original = strdup(filename);
 	if (!entry->filename_original) {
 		perror("strdup failed");
@@ -501,6 +509,9 @@ load_journal_entry_from_ics_file(char *filename, struct journal_entry *entry)
 		return -1;
 	}
 
+	entry->uid = fileStat.st_uid;
+	entry->gid = fileStat.st_gid;
+
 	// Make sure filename, which happens when
 	// Two entries are created on the same minute
 	// TODO: Check for duplicates
@@ -572,6 +583,7 @@ load_journal_entries()
 	// First pass: Store everything in root tree, fill up hashmap
 	while ((entry = readdir(dir))) {
 		if (entry->d_type == DT_REG && strstr(entry->d_name, ".ics")) {
+
 			struct journal_entry *new_entry =
 			    xcalloc(1, sizeof(struct journal_entry));
 
@@ -661,6 +673,24 @@ get_last_modified(struct journal_entry *entry)
 	}
 }
 
+uid_t
+get_node_uid(tree_node *node)
+{
+	if (is_root_node(node)) {
+		return getuid();
+	}
+	return get_entry(node)->uid;
+}
+
+gid_t
+get_node_gid(tree_node *node)
+{
+	if (is_root_node(node)) {
+		return getgid();
+	}
+	return get_entry(node)->gid;
+}
+
 struct journal_entry *
 copy_journal_entry(const struct journal_entry *src)
 {
@@ -669,6 +699,8 @@ copy_journal_entry(const struct journal_entry *src)
 	copy->filename = strdup(src->filename);
 	copy->filename_original = strdup(src->filename_original);
 	copy->component = icalcomponent_new_clone(src->component);
+	copy->gid = src->gid;
+	copy->uid = src->uid;
 
 	return copy;
 }
@@ -957,7 +989,7 @@ create_entry_from_fuse(const char *fuse_path)
 		LOG("Parent is not node");
 		return -ENOENT;
 	}
-	if (!entry_is_directory(parent)) {
+	if (!node_is_directory(parent)) {
 		LOG("Parent is not directory ");
 
 		return -ENOTDIR;
@@ -1013,7 +1045,7 @@ create_directory_from_fuse_path(const char *fuse_path)
 		LOG("Parent is not node");
 		return -ENOENT;
 	}
-	if (!entry_is_directory(parent)) {
+	if (!node_is_directory(parent)) {
 		LOG("Parent is not directory ");
 
 		return -ENOTDIR;
@@ -1070,6 +1102,43 @@ update_or_create_fuse_entry_from_original(const char *filename)
 	LOG("File updated");
 }
 
+int
+delete_dir_from_fuse_path(const char *filepath)
+{
+	int res = 0;
+	tree_node *node = get_node_by_path(fuse_tree_root, filepath);
+	if (!node) {
+		LOG("Node not found");
+		return -EIO;
+	}
+	if (!node_is_directory(node)) {
+		return -ENOTDIR;
+	}
+	if (node->child_count > 0) {
+		return -ENOTEMPTY;
+	}
+
+	struct journal_entry *entry = get_entry(node);
+	char *filepath_original = get_original_filepath(entry);
+	if (!filepath_original) {
+		LOG("Entry not found");
+		return -EIO;
+	}
+	LOG("Deleting file %s, located at %s", filepath, filepath_original);
+
+	res = remove(filepath_original);
+	if (res != 0) {
+		LOG("Failed to delete file");
+		return -res;
+	}
+
+	hashmap_remove(entries_original_ics, filepath_original);
+
+	detach_tree_node(node);
+	free_tree(node);
+	return res;
+}
+
 // Deletes original file too!
 int
 delete_from_fuse_path(const char *filepath)
@@ -1079,22 +1148,21 @@ delete_from_fuse_path(const char *filepath)
 	if (!node) {
 		return -EIO;
 	}
+	if (node_is_directory(node)) {
+		return -EISDIR;
+	}
 	struct journal_entry *entry = get_entry(node);
 	char *filepath_original = get_original_filepath(entry);
 	if (!filepath_original) {
 		LOG("Entry not found");
 		return -EIO;
 	}
-	LOG("Deleting file %s", filepath);
+	LOG("Deleting file %s, located at %s", filepath, filepath_original);
 
-	res = remove(filepath);
+	res = remove(filepath_original);
 
 	hashmap_remove(entries_original_ics, filepath_original);
 
-	// TODO: Subdirectories SHOULD NOT BE REMOVED HERE
-	// Instead, update the children so they have a new parent
-	// which is the parent node.
-	// Requires a change_parent function.
 	detach_tree_node(node);
 	free_tree(node);
 	return res;
diff --git a/journal_entry.h b/journal_entry.h
@@ -31,6 +31,9 @@ struct journal_entry {
 	// filename_original is the relative path to ICS_DIR
 	// I.E. 910319208nrao19p.ics
 	char *filename_original;
+	uid_t uid;
+	gid_t gid;
+
 	icalcomponent *component;
 };
 
@@ -85,8 +88,11 @@ find_node_by_path(struct tree_node *root, const char *path);
 struct journal_entry *
 get_entry(struct tree_node *node);
 
+int
+delete_dir_from_fuse_path(const char *filepath);
+
 bool
-entry_is_directory(struct tree_node *node);
+node_is_directory(struct tree_node *node);
 
 int
 delete_from_fuse_path(const char *filepath);
@@ -144,6 +150,12 @@ create_vjournal_entry(char *summary);
 int
 create_directory_from_fuse_path(const char *fuse_path);
 
+uid_t
+get_node_uid(struct tree_node *node);
+
+gid_t
+get_node_gid(struct tree_node *node);
+
 void
 remove_file_extension(char *path);
 
diff --git a/main.c b/main.c
@@ -39,6 +39,10 @@ journal_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi)
 	if (strcmp(path, "/") == 0) {
 		stbuf->st_mode = S_IFDIR | 0444;
 		stbuf->st_nlink = 2;
+
+		// TODO: Probably should be set with an option?
+		stbuf->st_uid = getuid();
+		stbuf->st_gid = getgid();
 		goto unlock_and_return;
 	}
 
@@ -50,12 +54,18 @@ journal_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi)
 	}
 
 	struct journal_entry *entry = get_entry(node);
-	if (entry_is_directory(node)) {
+	if (node_is_directory(node)) {
 		stbuf->st_mode = S_IFDIR | 0444;
+		stbuf->st_uid = get_node_uid(node);
+		stbuf->st_gid = get_node_gid(node);
+
 		stbuf->st_nlink = 2;
 	}
 	else {
 		stbuf->st_mode = S_IFREG | 0774;
+		stbuf->st_uid = get_node_uid(node);
+		stbuf->st_gid = get_node_gid(node);
+
 		stbuf->st_nlink = 1;
 	}
 
@@ -108,7 +118,10 @@ journal_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
 			st.st_ino = i + 1;
 			st.st_mode = S_IFREG | 0744;
 
-			if (entry_is_directory(node)) {
+			st.st_uid = get_node_uid(node);
+			st.st_gid = get_node_gid(node);
+
+			if (node_is_directory(node)) {
 				st.st_mode = S_IFDIR | 0755;
 			}
 
@@ -127,7 +140,10 @@ journal_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
 				st.st_ino = i + 1;
 				st.st_mode = S_IFREG | 0744;
 
-				if (entry_is_directory(child)) {
+				st.st_uid = get_node_uid(child);
+				st.st_gid = get_node_gid(child);
+
+				if (node_is_directory(child)) {
 					st.st_mode = S_IFDIR | 0755;
 				}
 
@@ -263,6 +279,25 @@ journal_unlink(const char *file)
 }
 
 int
+journal_rmdir(const char *file)
+{
+	pthread_mutex_lock(&entries_mutex);
+	LOG("DELETE %s", file);
+	int res = 0;
+
+	res = delete_dir_from_fuse_path(file);
+	if (res != 0) {
+		LOG("Failed to delete file");
+		pthread_mutex_unlock(&entries_mutex);
+		return res;
+	}
+
+	LOG("Delete successful");
+	pthread_mutex_unlock(&entries_mutex);
+	return 0;
+}
+
+int
 journal_rename(const char *old, const char *new, unsigned int flags)
 {
 	pthread_mutex_lock(&entries_mutex);
@@ -375,6 +410,7 @@ static const struct fuse_operations journal_oper = {.getattr = journal_getattr,
 						    .create = journal_create,
 						    .mkdir = journal_mkdir,
 						    .unlink = journal_unlink,
+						    .rmdir = journal_rmdir,
 						    .readlink =
 							journal_readlink,
 						    .rename = journal_rename};
diff --git a/tree.c b/tree.c
@@ -45,6 +45,12 @@ update_node_data(tree_node *node, void *data)
 	node->data = data;
 }
 
+bool
+node_has_children(tree_node *node)
+{
+	return node->child_count > 0;
+}
+
 // Free a node and all its children
 void
 free_tree(tree_node *node)
diff --git a/tree.h b/tree.h
@@ -18,6 +18,9 @@ typedef struct tree_node {
 tree_node *
 create_tree_node(void *data, void (*free_fn)(void *));
 
+bool
+node_has_children(tree_node *node);
+
 void
 update_node_data(tree_node *node, void *data);