agendafs
A filesystem for your calendar.
git clone git://mccd.space/agendafscommit bf02536a0f7f28bba661a5cfead7e694bce03325
parent 702aba8c32d11f478b35e6996138ed8bd8b7811b
Author: Marc Coquand <marc@coquand.email>
Date: Sat, 26 Jul 2025 12:02:49 +0200
Refactor and code cleanup
Diffstat:
9 files changed, 112 insertions(+), 178 deletions(-)
diff --git a/agenda_entry.c b/agenda_entry.c
@@ -26,9 +26,6 @@ copy_agenda_entry(const struct agenda_entry *src)
struct agenda_entry *copy = xmalloc(sizeof(struct agenda_entry));
copy->filename = xstrdup(src->filename);
copy->filename_vdir = xstrdup(src->filename_vdir);
- copy->gid = src->gid;
- copy->uid = src->uid;
- copy->ino = src->ino;
return copy;
}
@@ -45,6 +42,14 @@ free_agenda_entry(struct agenda_entry *entry)
return;
}
+// Frees old filename and copies over the new
+void
+set_filename(struct agenda_entry *e, const char *new)
+{
+ free(e->filename);
+ e->filename = xstrdup(new);
+}
+
struct agenda_entry *
create_agenda_entry(memory_region *mreg, const char *filename,
const char *filename_vdir)
@@ -52,6 +57,7 @@ create_agenda_entry(memory_region *mreg, const char *filename,
struct agenda_entry *copy = xmalloc(sizeof(struct agenda_entry));
copy->filename = xstrdup(filename);
copy->filename_vdir = xstrdup(filename_vdir);
+ // TODO: These should be read from icalcomponent instead
// copy->gid = src->gid;
// copy->uid = src->uid;
// copy->ino = src->ino;
diff --git a/agenda_entry.h b/agenda_entry.h
@@ -28,9 +28,6 @@ struct agenda_entry {
// filename_original is the relative path to ICS_DIR
// I.E. 910319208nrao19p.ics
char *filename_vdir;
- uid_t uid;
- gid_t gid;
- ino_t ino;
};
struct agenda_entry *
diff --git a/fuse_node.c b/fuse_node.c
@@ -204,8 +204,7 @@ parse_ics_to_agenda_entry(memory_region *mreg, const char *vdir_filepath)
return e;
}
-// agenda_entry is automatically cleaned up by mreg, use copy_agenda_entry
-// to take ownership!
+// Owner: memory_region
struct agenda_entry *
load_agenda_entry_from_ics_file(memory_region *mreg, const char *filename)
{
@@ -225,11 +224,6 @@ load_agenda_entry_from_ics_file(memory_region *mreg, const char *filename)
return NULL;
}
- // TODO: Load these from the original file dynamically instead.
- new_entry->uid = fileStat.st_uid;
- new_entry->gid = fileStat.st_gid;
- new_entry->ino = fileStat.st_ino;
-
LOG("Parsed %s to file %s", new_entry->filename_vdir,
new_entry->filename);
return new_entry;
@@ -256,11 +250,15 @@ load_root_node_tree()
struct agenda_entry *new_entry =
load_agenda_entry_from_ics_file(mreg,
entry->d_name);
-
- create_fuse_node(new_entry);
-
- LOG("Inserted filename_vdir: %s, %s",
- new_entry->filename, new_entry->filename_vdir);
+ if (new_entry) {
+ create_fuse_node(new_entry);
+ LOG("Inserted filename_vdir: %s, %s",
+ new_entry->filename,
+ new_entry->filename_vdir);
+ }
+ else {
+ LOG("Could not load entry. Skipping");
+ }
}
}
closedir(dir);
@@ -273,7 +271,7 @@ load_root_node_tree()
struct tree_node *child =
get_fuse_node_from_vdir_name(vdirname);
- LOG("Parsing %s", filename);
+ LOG("Parsing %s", vdirname);
icalcomponent *ic = get_icalcomponent_from_node(mreg, child);
LOG("Success");
@@ -500,30 +498,35 @@ set_node_class(memory_region *mreg, const struct tree_node *node,
return write_ical_file(mreg, node, component);
}
-uid_t
-get_node_uid(const struct tree_node *node)
+struct stat
+get_node_stat(memory_region *mreg, const struct tree_node *node,
+ icalcomponent *node_component)
{
if (is_root_node(node)) {
- return getuid();
+ struct stat st = {0};
+ st.st_gid = getgid();
+ st.st_uid = getuid();
+ st.st_ino = 0;
+ return st;
}
- return get_entry(node)->uid;
-}
+ else {
+ struct stat vdir_stat = {0};
+ char *vdir_path = get_vdir_filepath(mreg, node);
+ int res = stat(vdir_path, &vdir_stat);
+ assert(res == 0);
+ vdir_stat.st_size =
+ (off_t)get_entry_content_size(node_component);
+ if (node_is_directory(mreg, node, node_component)) {
+ vdir_stat.st_mode = S_IFDIR | 0444;
+ vdir_stat.st_nlink = 2;
+ }
+ else {
+ vdir_stat.st_mode = S_IFREG | 0774;
+ vdir_stat.st_nlink = 1;
+ }
-gid_t
-get_node_gid(const struct tree_node *node)
-{
- if (is_root_node(node)) {
- return getgid();
- }
- return get_entry(node)->gid;
-}
-ino_t
-get_node_ino(const struct tree_node *node)
-{
- if (is_root_node(node)) {
- return 0;
+ return vdir_stat;
}
- return get_entry(node)->ino;
}
int
@@ -592,9 +595,7 @@ create_entry_from_fuse(memory_region *mreg, const char *fuse_path)
if (write_ical_file(mreg, new_node, new_component) != 0) {
LOG("Write to entry failed");
- hashmap_remove(entries_vdir, new_entry->filename_vdir);
-
- free_tree(new_node);
+ delete_fuse_node(new_node);
return -EIO;
}
@@ -605,59 +606,36 @@ create_entry_from_fuse(memory_region *mreg, const char *fuse_path)
int
create_directory_from_fuse_path(memory_region *mreg, const char *fuse_path)
{
+
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_filename = get_filename(fuse_path);
+ icalcomponent *new_component =
+ create_vjournal_directory(mreg, new_filename);
+
+ char *new_filname_vdir = NULL;
+ rasprintf(mreg, &new_filname_vdir, "%s.ics",
+ icalcomponent_get_uid(new_component));
struct agenda_entry *new_entry =
- xcalloc(1, sizeof(struct agenda_entry));
- icalcomponent *new_component =
- create_vjournal_directory(mreg, new_basename);
+ create_agenda_entry(mreg, new_filename, new_filname_vdir);
- new_entry->filename = xstrdup(new_basename);
- if (asprintf(&new_entry->filename_vdir, "%s.ics",
- icalcomponent_get_uid(new_component)) == -1) {
- LOG("Failed to create journal entry");
- free_agenda_entry(new_entry);
- return -ENOMEM;
- }
LOG("Inserting vjournal directory");
- struct tree_node *new_node =
- create_tree_node(new_entry, (void *)free_agenda_entry);
+ struct tree_node *new_node = create_fuse_node(new_entry);
- char *parent_path = get_parent_path(mreg, fuse_path);
- LOG("Parent path is %s", parent_path);
- struct tree_node *parent = get_node_by_path(mreg, parent_path);
- icalcomponent *parent_ics = get_icalcomponent_from_node(mreg, parent);
+ int status =
+ insert_fuse_node_to_path(mreg, fuse_path, new_node, new_component);
- if (!parent) {
- LOG("Parent does not exist");
+ if (status != 0) {
return -ENOENT;
}
- if (!is_root_node(parent) ||
- (parent_ics && !node_is_directory(mreg, parent, parent_ics))) {
- LOG("Parent is not directory ");
- return -ENOTDIR;
- }
-
- if (parent_ics) {
- write_parent_child_components(mreg, parent, parent_ics,
- new_node, new_component);
-
- add_child(parent, new_node);
- }
-
- hashmap_insert(entries_vdir, new_entry->filename_vdir, new_node);
if (write_ical_file(mreg, new_node, new_component) != 0) {
LOG("Write to entry failed");
- hashmap_remove(entries_vdir, new_entry->filename_vdir);
-
- free_tree(new_node);
+ delete_fuse_node(new_node);
return -EIO;
}
@@ -665,7 +643,7 @@ create_directory_from_fuse_path(memory_region *mreg, const char *fuse_path)
return 0;
}
-void
+int
update_or_create_fuse_entry_from_vdir(memory_region *mreg,
const char *filepath_vdir)
{
@@ -675,26 +653,13 @@ update_or_create_fuse_entry_from_vdir(memory_region *mreg,
struct agenda_entry *updated_entry =
load_agenda_entry_from_ics_file(mreg, filename_vdir);
- struct agenda_entry *updated_entry_owned =
- copy_agenda_entry(updated_entry);
-
- struct tree_node *new_or_updated_node =
- hashmap_get(entries_vdir, filename_vdir);
-
- if (new_or_updated_node) {
- LOG("Updating existing entry");
- update_node_data(new_or_updated_node, updated_entry_owned);
- }
- else {
- LOG("Creating new entry");
- new_or_updated_node = create_tree_node(
- updated_entry_owned, (void *)free_agenda_entry);
- hashmap_insert(entries_vdir, updated_entry_owned->filename_vdir,
- new_or_updated_node);
+ if (!updated_entry) {
+ return -EIO;
}
- icalcomponent *entry_ics =
- get_icalcomponent_from_node(mreg, new_or_updated_node);
+ struct tree_node *node = upsert_fuse_node(updated_entry);
+
+ icalcomponent *entry_ics = get_icalcomponent_from_node(mreg, node);
const char *new_parent_uid = get_parent_uid(entry_ics);
@@ -702,21 +667,20 @@ update_or_create_fuse_entry_from_vdir(memory_region *mreg,
LOG("Has parent");
struct tree_node *new_parent =
get_node_by_uuid(mreg, new_parent_uid);
- move_node(new_parent, new_or_updated_node);
+ move_node(new_parent, node);
}
else {
LOG("Does not have parent, adding to root");
- detach_tree_node(new_or_updated_node);
- add_child(fuse_root, new_or_updated_node);
+ move_node(fuse_root, node);
}
LOG("Tree updated");
+ return 0;
}
int
delete_dir_from_fuse_path(memory_region *mreg, const char *filepath)
{
- int res = 0;
struct tree_node *node = get_node_by_path(mreg, filepath);
if (!node) {
return -EIO;
@@ -738,7 +702,7 @@ delete_dir_from_fuse_path(memory_region *mreg, const char *filepath)
}
LOG("Deleting file %s, located at %s", filepath, filepath_original);
- res = remove(filepath_original);
+ int res = remove(filepath_original);
if (res != 0) {
LOG("Failed to delete file");
return -res;
@@ -746,7 +710,7 @@ delete_dir_from_fuse_path(memory_region *mreg, const char *filepath)
delete_fuse_node(node);
- return res;
+ return 0;
}
int
@@ -766,16 +730,6 @@ do_agenda_rename(memory_region *mreg, const char *old, const char *new)
return -EINVAL;
}
- char *new_summary = rstrdup(mreg, new_filename);
-
- // Remove everything after extension
- // TODO: Store the extension as a separate field in the ics file
- char *dot = strrchr(new_summary, '.');
- if (dot) {
- *dot = '\0';
- }
-
- LOG("Summary is now %s", new_summary);
struct tree_node *child_node = get_node_by_path(mreg, old);
assert(child_node);
icalcomponent *child_ics =
@@ -797,8 +751,6 @@ do_agenda_rename(memory_region *mreg, const char *old, const char *new)
child_ics);
}
- detach_tree_node(child_node);
-
if (new_parent_ics) {
move_node(new_parent_node, child_node);
write_parent_child_components(mreg, new_parent_node,
@@ -806,7 +758,7 @@ do_agenda_rename(memory_region *mreg, const char *old, const char *new)
child_ics);
}
else {
- add_child(fuse_root, child_node);
+ move_node(fuse_root, child_node);
}
return 0;
}
@@ -833,9 +785,7 @@ delete_from_fuse_path(memory_region *mreg, const char *filepath)
return -EIO;
}
- hashmap_remove(entries_vdir, filepath_original);
- detach_tree_node(node);
- free_tree(node);
+ delete_fuse_node(node);
return res;
}
@@ -886,22 +836,16 @@ get_fuse_path_from_vdir(memory_region *mreg, const char *filepath)
int
delete_from_vdir_path(memory_region *mreg, const char *filepath)
{
- // Strip path, validate just the new basename
- const char *keyname = get_filename(filepath);
- struct agenda_entry *entry = hashmap_get(entries_vdir, keyname);
- if (!entry) {
- return -EIO;
- }
- // TODO: Fix so it finds by actual UUID, not filepath
struct tree_node *node = get_node_by_uuid(mreg, filepath);
+ if (!node) {
+ return -ENOENT;
+ }
// 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);
- hashmap_remove(entries_vdir, keyname);
+ delete_fuse_node(node);
return 0;
}
diff --git a/fuse_node.h b/fuse_node.h
@@ -95,14 +95,9 @@ int
set_node_class(memory_region *mreg, const struct tree_node *node,
const icalproperty_class new_class);
-uid_t
-get_node_uid(const struct tree_node *node);
-
-gid_t
-get_node_gid(const struct tree_node *node);
-
-ino_t
-get_node_ino(const struct tree_node *node);
+struct stat
+get_node_stat(memory_region *mreg, const struct tree_node *node,
+ icalcomponent *node_component);
int
insert_fuse_node_to_path(memory_region *mreg, const char *fuse_path,
diff --git a/fuse_node_store.c b/fuse_node_store.c
@@ -38,7 +38,6 @@ get_fuse_node_from_vdir_name(const char *vdir_name)
const struct agenda_entry *
get_entry(const struct tree_node *node)
{
- assert(node->data);
return node->data;
}
@@ -72,3 +71,18 @@ create_fuse_node(const struct agenda_entry *entry)
return new_node;
}
+struct tree_node *
+upsert_fuse_node(const struct agenda_entry *entry)
+{
+ struct tree_node *node =
+ hashmap_get(entries_vdir, entry->filename_vdir);
+ if (node) {
+ set_node_filename(node, entry->filename);
+ }
+ else {
+ node = create_fuse_node(entry);
+ }
+
+ return node;
+}
+
diff --git a/fuse_node_store.h b/fuse_node_store.h
@@ -38,4 +38,7 @@ create_fuse_node(const struct agenda_entry *entry);
void
delete_fuse_node(struct tree_node *node);
+struct tree_node *
+upsert_fuse_node(const struct agenda_entry *entry);
+
#endif // fuse_node_store_h_INCLUDED
diff --git a/main.c b/main.c
@@ -60,37 +60,10 @@ fuse_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi)
}
icalcomponent *ic = get_icalcomponent_from_node(mreg, node);
- stbuf->st_uid = get_node_uid(node);
- stbuf->st_gid = get_node_gid(node);
- stbuf->st_ino = get_node_ino(node);
- int size = get_entry_content_size(ic);
- stbuf->st_size = (off_t)size;
+ struct stat st = get_node_stat(mreg, node, ic);
- if (node_is_directory(mreg, node, ic)) {
- stbuf->st_mode = S_IFDIR | 0444;
- stbuf->st_nlink = 2;
- }
- else {
- stbuf->st_mode = S_IFREG | 0774;
- stbuf->st_nlink = 1;
- }
-
- char *vdir_path = get_vdir_filepath(mreg, node);
- struct stat vdir_file_stat;
- if (stat(vdir_path, &vdir_file_stat) == 0) {
- stbuf->st_mtime = vdir_file_stat.st_mtime;
- stbuf->st_ctime = vdir_file_stat.st_ctime;
- }
- else {
- icaltimetype ical_last_modified = get_last_modified(ic);
-
- time_t last_modified = icaltime_as_timet(ical_last_modified);
- LOG("STAT TIME IS: %ld", last_modified);
-
- stbuf->st_mtime = last_modified;
- stbuf->st_ctime = last_modified;
- }
+ *stbuf = st;
stbuf->st_atime = time(NULL);
@@ -132,17 +105,7 @@ fuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset,
icalcomponent *ic = get_icalcomponent_from_node(mreg, child);
assert(ic);
- struct stat st = {0};
- // TODO: Correct perms
- st.st_mode = S_IFREG | 0744;
-
- st.st_uid = get_node_uid(child);
- st.st_gid = get_node_gid(child);
- st.st_ino = get_node_ino(child);
-
- if (node_is_directory(mreg, child, ic)) {
- st.st_mode = S_IFDIR | 0755;
- }
+ struct stat st = get_node_stat(mreg, node, ic);
if (filler(buf, get_node_filename(mreg, child), &st, 0, 0) !=
0) {
diff --git a/path.c b/path.c
@@ -21,6 +21,15 @@ without_file_extension(memory_region *m, const path *p)
}
path *
+get_file_extension(memory_region *m, const path *p)
+{
+ char *cpy = rstrdup(m, p);
+ char *last_dot = strrchr(cpy, '.');
+
+ return last_dot;
+}
+
+path *
append_path(memory_region *m, const path *parentp, const char *childp)
{
char *filepath = NULL;
diff --git a/path.h b/path.h
@@ -19,6 +19,9 @@ append_path(memory_region *m, const path *parentp, const char *childp);
const path *
get_filename(const char *full_path);
+path *
+get_file_extension(memory_region *m, const path *p);
+
char *
get_parent_path(memory_region *mreg, const char *path);