commit a93e4ab988faad1117f8c2b2e385961e328abdd3 Author: dan Date: Thu Jul 9 00:42:07 2020 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f8012f7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +tagfs +zdir diff --git a/makefile b/makefile new file mode 100644 index 0000000..10bf897 --- /dev/null +++ b/makefile @@ -0,0 +1,20 @@ +.POSIX: +.PHONY: clean install + +PREFIX=/usr/local + +CC=gcc +CFLAGS=-Wall -Wextra -Wshadow -Wpedantic -std=c11 -O0 -g -Wno-unused-parameter +LDFLAGS=$(shell pkg-config --libs --cflags fuse3) + +SRC=$(wildcard src/*.c) + +tagfs: $(SRC) + $(CC) $(LDFLAGS) $? $(CFLAGS) -o $@ + +clean: + rm tagfs + +install: tagfs + cp $? $(PREFIX)/bin/ + diff --git a/shell/tagfs.sh b/shell/tagfs.sh new file mode 100644 index 0000000..edb77d7 --- /dev/null +++ b/shell/tagfs.sh @@ -0,0 +1,101 @@ +tagfs_nofs() +{ + echo "No tagfs filesystem found in `pwd`" +} + +tagfs_exists() +{ + [ -d .tagfs ] && return 1 || return 0 +} + +tagfs_clear_filter() +{ + rm .tagfs/filters/$$ 2> /dev/null + return 0 +} + +tagfs_list_filter() +{ + cat .tagfs/filters/$$ 2> /dev/null + return 0 +} + +tagfs_list_tags() +{ + /bin/ls -1 .tagfs/tags/ +} + +tagfs_is_tag() +{ + [ -n "$(echo $1 | egrep '^[-+]')" ] && return 1 || return 0 +} + +filter() +{ + tagfs_exists + [ $? -eq 0 ] && tagfs_nofs && return + [ $# -eq 1 ] && [ "$1" = "-" ] && tagfs_clear_filter && return + + filter_file=".tagfs/filters/$$" + tmp_file=".tagfs/filters/tmp_$$" + for t in "$@"; do + case "$(echo $t | cut -b1)" in + +) [ -z "$(fgrep $t $filter_file 2> /dev/null)" ] && echo "${t#+}" >> $filter_file ;; + -) sed -e "/${t#-}/d" $filter_file > $tmp_file && mv $tmp_file $filter_file + echo nigga;; + esac + done + + tagfs_list_filter +} + +tags() +{ + tagfs_exists + [ $? -eq 0 ] && tagfs_nofs && return + [ $# -eq 0 ] && tagfs_list_tags && return + + tagfs_is_tag "$1" + if [ $? -eq 1 ]; then + for tag in "$@"; do + [ "$tag" = "-" ] && continue + + case "$(echo $tag | cut -b1)" in + +) mkdir .tagfs/tags/${tag#+} ;; + -) rm -r .tagfs/tags/${tag#-} ;; + esac + done + else + for tag in "$@"; do + [ "$tag" = "$1" ] && continue + [ "$tag" = "-" ] && rm $(find -name "$1") && continue + + case "$(echo $tag | cut -b1)" in + +) ln -s ../../../"$1" .tagfs/tags/"${tag#+}"/"$1" ;; + -) rm .tagfs/tags/"${tag#-}"/"$1" ;; + esac + done + fi +} + +# Bash completion +# _filter_completion() +# { +# filter_file=".tagfs/filters/$$" +# list="" +# +# for t in $(cat $filter_file 2> /dev/null); do +# list+="-$t " +# done +# +# for t in $(ls .tagfs/tags/); do +# [ -n "$(fgrep $t $filter_file 2> /dev/null)" ] && continue +# list+="+$(basename $t) " +# done +# +# COMPREPLY=($(compgen -W "$list" -- "${COMP_WORDS[$COMP_CWORD]}")) +# } +# +# complete -F _filter_completion filter +# + diff --git a/src/defs.h b/src/defs.h new file mode 100644 index 0000000..8c210c9 --- /dev/null +++ b/src/defs.h @@ -0,0 +1,19 @@ +#ifndef H_DEFS +#define H_DEFS + +/* fusefs version */ +#define FUSE_USE_VERSION 39 + +/* posix */ +#define _POSIX_C_SOURCE 200809 + +/* path constants */ +#define TAGFS_DIR ".tagfs" +#define TAGFS_TAG_DIR TAGFS_DIR "/tags" +#define TAGFS_FILTER_DIR TAGFS_DIR "/filters" + +/* size constants */ +#define TAG_MAX (32) + +#endif + diff --git a/src/dirinfo.h b/src/dirinfo.h new file mode 100644 index 0000000..75e99b9 --- /dev/null +++ b/src/dirinfo.h @@ -0,0 +1,67 @@ +#ifndef H_DIRINFO +#define H_DIRINFO + +#include +#include +#include +#include + +struct dirinfo +{ + int fd; + DIR* dp; + struct stat stat; +}; + +static inline void +close_dirinfo(struct dirinfo* dirinfo) +{ + if (dirinfo->fd > 0) + close(dirinfo->fd); + + if (dirinfo->dp) + closedir(dirinfo->dp); +} + +static inline int +openat_dirinfo(int dirfd, const char* path, struct dirinfo* dirinfo) +{ + if ((dirinfo->fd = openat(dirfd, path, O_DIRECTORY|O_RDONLY)) == -1) + goto err; + + if ((dirinfo->dp = fdopendir(dirinfo->fd)) == NULL) + goto err; + + if (fstat(dirinfo->fd, &dirinfo->stat) == -1) + goto err; + + return 1; + +err: + close_dirinfo(dirinfo); + + return 0; +} + +static inline int +open_dirinfo(const char* path, struct dirinfo* dirinfo) +{ + if ((dirinfo->fd = open(path, O_DIRECTORY|O_RDONLY)) == -1) + goto err; + + if ((dirinfo->dp = fdopendir(dirinfo->fd)) == NULL) + goto err; + + if (fstat(dirinfo->fd, &dirinfo->stat) == -1) + goto err; + + return 1; + +err: + close_dirinfo(dirinfo); + + return 0; +} + +#endif + diff --git a/src/filter.h b/src/filter.h new file mode 100644 index 0000000..a16286f --- /dev/null +++ b/src/filter.h @@ -0,0 +1,238 @@ +#ifndef H_FILTER +#define H_FILTER + +#include +#include +#include +#include +#include +#include + +#define MIN(a, b) (a > b? b : a) +#define MAX(a, b) (a > b? a : b) + +struct filter +{ + struct { + char** ptr; + size_t len; + } tags; + + struct { + struct { + char** ptr; + int* in; + size_t len; + } included; + struct { + char** ptr; + size_t len; + } excluded; + } files; +}; + +static inline int +filter_include_count(struct filter* filter) +{ + int count = 0; + + for (size_t i = 0; i < filter->tags.len; ++i) { + if (filter->tags.ptr[i][0] != '@') + count++; + } + + return count; +} + +static inline int +filter_exclude_count(struct filter* filter) +{ + int count = 0; + + for (size_t i = 0; i < filter->tags.len; ++i) { + if (filter->tags.ptr[i][0] == '@') + count++; + } + + return count; +} + +static inline void +filter_free(struct filter* filter) +{ + if (filter->tags.ptr) { + for (size_t i = 0; i < filter->tags.len; ++i) { + free(filter->tags.ptr[i]); + } + free(filter->tags.ptr); + } + + if (filter->files.included.ptr) { + for (size_t i = 0; i < filter->files.included.len; ++i) { + free(filter->files.included.ptr[i]); + } + free(filter->files.included.ptr); + free(filter->files.included.in); + } + + if (filter->files.excluded.ptr) { + for (size_t i = 0; i < filter->files.excluded.len; ++i) { + free(filter->files.excluded.ptr[i]); + } + free(filter->files.excluded.ptr); + } +} + +static inline int +load_filter_files(struct filter* filter, struct dirinfo* tagdir) +{ + memset(&filter->files, 0, sizeof filter->files); + + for (size_t i = 0; i < filter->tags.len; ++i) { + const char* tag = filter->tags.ptr[i]; + + int excluded = (*tag == '@'); + int fd = openat(tagdir->fd, tag + (excluded? 1 : 0), O_DIRECTORY|O_RDONLY); + + if (fd == -1) + return 0; + + DIR* dp = fdopendir(fd); + struct dirent* ent; + while ((ent = readdir(dp))) { + if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) + continue; + + char name[PATH_MAX]; + if (readlinkat(fd, ent->d_name, name, sizeof name) == -1) { + closedir(dp); + close(fd); + return 0; + } + + const char* p = strrchr(name, '/') + 1; + + if (excluded) { + size_t size = ++filter->files.excluded.len * sizeof *filter->files.excluded.ptr; + void* ptr = realloc(filter->files.excluded.ptr, size); + + if (ptr == NULL) { + closedir(dp); + close(fd); + return 0; + } + + filter->files.excluded.ptr = ptr; + + char** str = &filter->files.excluded.ptr[filter->files.excluded.len - 1]; + size = MIN(strlen(p), NAME_MAX); + *str = malloc(size + 1); + + if (*str == NULL) { + closedir(dp); + close(fd); + return 0; + } + + strncpy(*str, p, size); + (*str)[size] = '\0'; + } + else { + int skip = 0; + + for (size_t j = 0; j < filter->files.included.len; ++j) { + if (strcmp(filter->files.included.ptr[j], p) == 0) { + skip = 1; + filter->files.included.in[j] += 1; + } + } + + if (skip) + continue; + + size_t size = ++filter->files.included.len * sizeof *filter->files.included.ptr; + void* ptr = realloc(filter->files.included.ptr, size); + + if (ptr == NULL) { + closedir(dp); + close(fd); + return 0; + } + + filter->files.included.ptr = ptr; + + size = filter->files.included.len * sizeof *filter->files.included.in; + ptr = realloc(filter->files.included.in, size); + + if (ptr == NULL) { + closedir(dp); + close(fd); + return 0; + } + + filter->files.included.in = ptr; + filter->files.included.in[filter->files.included.len - 1] = 1; + + char** str = &filter->files.included.ptr[filter->files.included.len - 1]; + size = MIN(strlen(p), NAME_MAX); + *str = malloc(size + 1); + + if (*str == NULL) { + closedir(dp); + close(fd); + return 0; + } + + strncpy(*str, p, size); + (*str)[size] = '\0'; + } + } + + closedir(dp); + close(fd); + } + + return 1; +} + +static inline int +load_filter_tags(int fd, struct filter* filter) +{ + FILE* fp = fdopen(fd, "r"); + char tag[TAG_MAX + 1]; + + memset(&filter->tags, 0, sizeof filter->tags); + + while (fgets(tag, sizeof tag, fp)) { + size_t size = ++filter->tags.len * sizeof *filter->tags.ptr; + void* ptr = realloc(filter->tags.ptr, size); + + if (ptr == NULL) { + fclose(fp); + return 0; + } + + filter->tags.ptr = ptr; + + char** str = &filter->tags.ptr[filter->tags.len - 1]; + size = MIN(strlen(tag), TAG_MAX); + *str = malloc(size + 1); + + if (*str == NULL) { + fclose(fp); + return 0; + } + + if (tag[size - 1] == '\n') + tag[size - 1] = '\0'; + + strncpy(*str, tag, size); + (*str)[size] = '\0'; + } + + fclose(fp); + return 1; +} + +#endif + diff --git a/src/proc.h b/src/proc.h new file mode 100644 index 0000000..28dd478 --- /dev/null +++ b/src/proc.h @@ -0,0 +1,49 @@ +#ifndef H_PROC +#define H_PROC + +#include +#include +#include +#include +#include + +static inline int +is_pid_alive(unsigned pid) +{ + char path[PATH_MAX]; + snprintf(path, sizeof path, "/proc/%u", pid); + + return access(path, F_OK) == 0; +} + +static inline int +proc_get_ppid(unsigned pid) +{ + char path[PATH_MAX]; + snprintf(path, sizeof path, "/proc/%u/stat", pid); + + FILE* fp; + if ((fp = fopen(path, "r")) == NULL) + goto err; + + char stat[4096]; + + if (fread(stat, sizeof(char), sizeof stat, fp) == 0) + goto err; + + fclose(fp); + + char unused; + int ppid; + sscanf(strrchr(stat, ')') + 1, " %c %d", &unused, &ppid); + + return ppid; + +err: + if (fp) fclose(fp); + + return -1; +} + +#endif + diff --git a/src/tagfs.c b/src/tagfs.c new file mode 100644 index 0000000..36e3877 --- /dev/null +++ b/src/tagfs.c @@ -0,0 +1,450 @@ +#include "defs.h" +#include +#include +#include +#include +#include +#include + +#include "proc.h" +#include "dirinfo.h" +#include +#include +#include "filter.h" + +struct tagfs_private_data +{ + struct dirinfo mntdir; + struct dirinfo fsdir; + struct dirinfo tagdir; + struct dirinfo filterdir; +}; + +#define TAGFS_DATA ((struct tagfs_private_data*)fuse_get_context()->private_data) + +int +tagfs_op_open(const char* path, struct fuse_file_info* fi) +{ + fuse_log(FUSE_LOG_DEBUG, "open(%s)\n", path); + + if (*path == '\0') + return -EACCES; + + int fd; + + if ((fd = openat(TAGFS_DATA->mntdir.fd, path + 1, fi->flags)) == -1) + return -errno; + + fi->fh = fd; + + return 0; +} + +int +tagfs_op_create(const char* path, mode_t mode, struct fuse_file_info* fi) +{ + fuse_log(FUSE_LOG_DEBUG, "create(%s)\n", path); + + int fd = -1; + int flags = O_CREAT|O_RDWR; + + if (strrchr(path, '/') == path) { + fd = openat(TAGFS_DATA->mntdir.fd, path + 1, flags, mode); + } + else if (strncmp(path, "/" TAGFS_FILTER_DIR, sizeof("/" TAGFS_FILTER_DIR) - 1) == 0) { + const char* p = strrchr(path, '/'); + fd = openat(TAGFS_DATA->filterdir.fd, p + 1, flags, 0644); + } + + if (fd == -1) + return -1; + + fi->fh = fd; + + return 0; +} + +int +tagfs_op_rename(const char* oldpath, const char* newpath, unsigned int flags) +{ + (void) flags; + + return renameat(TAGFS_DATA->mntdir.fd, oldpath + 1, TAGFS_DATA->mntdir.fd, newpath + 1); +} + +int +tagfs_op_mkdir(const char* path, mode_t mode) +{ + fuse_log(FUSE_LOG_DEBUG, "mkdir(%s)\n", path); + + if (strncmp(path, "/" TAGFS_TAG_DIR, sizeof("/" TAGFS_TAG_DIR) - 1) != 0) + return -1; + + const char* p = strrchr(path, '/'); + return mkdirat(TAGFS_DATA->tagdir.fd, p + 1, mode); +} + +int +tagfs_op_rmdir(const char* path) +{ + fuse_log(FUSE_LOG_DEBUG, "rmdir(%s)\n", path); + + if (strncmp(path, "/" TAGFS_TAG_DIR, sizeof("/" TAGFS_TAG_DIR) - 1) != 0) + return -1; + + const char* p = strrchr(path, '/'); + return unlinkat(TAGFS_DATA->tagdir.fd, p + 1, AT_REMOVEDIR); +} + +int +tagfs_op_release(const char* path, struct fuse_file_info* fi) +{ + fuse_log(FUSE_LOG_DEBUG, "release(%s)\n", path); + + if (fi) + close(fi->fh); + + return 0; +} + +int +tagfs_op_read(const char* path, char* buf, size_t size, off_t off, struct fuse_file_info* fi) +{ + fuse_log(FUSE_LOG_DEBUG, "read(%s)\n", path); + + ssize_t r = pread(fi->fh, buf, size, off); + + return r; +} + +int +tagfs_op_write(const char* path, const char* buf, size_t size, off_t off, struct fuse_file_info* fi) +{ + fuse_log(FUSE_LOG_DEBUG, "write(%s)\n", path); + + ssize_t r = pwrite(fi->fh, buf, size, off); + + return r; +} + +void +remove_from_tags(const char* name) +{ + rewinddir(TAGFS_DATA->tagdir.dp); + + struct dirent* tagent; + while ((tagent = readdir(TAGFS_DATA->tagdir.dp))) { + int fd = openat(TAGFS_DATA->tagdir.fd, tagent->d_name, O_DIRECTORY|O_RDONLY); + if (fd == -1) + continue; + + DIR* dp = fdopendir(fd); + struct dirent* ent; + while ((ent = readdir(dp))) { + if (strcmp(name, ent->d_name) == 0) + unlinkat(fd, ent->d_name, 0); + } + + closedir(dp); + close(fd); + } +} + +int +tagfs_op_unlink(const char* path) +{ + fuse_log(FUSE_LOG_DEBUG, "unlink(%s)\n", path); + + if (strrchr(path, '/') == path) { + remove_from_tags(path + 1); + } + + unlinkat(TAGFS_DATA->mntdir.fd, path + 1, 0); + + return 0; +} + +void +fill_readdir(struct dirinfo* dir, void* buf, fuse_fill_dir_t filler, int skip_dirs) +{ + rewinddir(dir->dp); + + struct dirent* ent; + while ((ent = readdir(dir->dp))) { + struct stat stbuf; + + if (fstatat(dir->fd, ent->d_name, &stbuf, 0) == 0 && skip_dirs) { + if ((stbuf.st_mode & S_IFDIR) == S_IFDIR) + continue; + } + + filler(buf, ent->d_name, &stbuf, 0, 0); + } +} + +/* Removes filter files for dead processes, run at access and readdir */ +void +remove_dead_filters(void) +{ + DIR* dp = TAGFS_DATA->filterdir.dp; + rewinddir(dp); + + struct dirent* ent; + while ((ent = readdir(dp))) { + char* end; + int pid = strtol(ent->d_name, &end, 10); + + if (end && *end == 0 && !is_pid_alive(pid)) { + unlinkat(TAGFS_DATA->filterdir.fd, ent->d_name, 0); + } + } +} + +int +open_process_filter(unsigned pid) +{ + char path[PATH_MAX]; + snprintf(path, sizeof path, "%u", pid); + + return openat(TAGFS_DATA->filterdir.fd, path, O_RDONLY); +} + +int +fill_readdir_filtered(int filterfd, void* buf, fuse_fill_dir_t filler) +{ + struct filter filter = { 0 }; + + if (!load_filter_tags(filterfd, &filter)) { + fuse_log(FUSE_LOG_DEBUG, "load_tags failed\n"); + filter_free(&filter); + return 0; + } + + if (!load_filter_files(&filter, &TAGFS_DATA->tagdir)) { + fuse_log(FUSE_LOG_DEBUG, "load_files failed\n"); + filter_free(&filter); + return 0; + } + + int include_count = filter_include_count(&filter); + int exclude_count = filter_exclude_count(&filter); + + if (include_count > 0) { + for (size_t i = 0; i < filter.files.included.len; ++i) { + fuse_log(FUSE_LOG_DEBUG, "- %s\n", filter.files.included.ptr[i]); + + if (filter.files.included.in[i] != include_count) + continue; + + int skip = 0; + for (size_t j = 0; j < filter.files.excluded.len; ++j) { + skip = strcmp(filter.files.included.ptr[i], filter.files.excluded.ptr[j]) == 0; + } + + if (skip) + continue; + + filler(buf, filter.files.included.ptr[i], NULL, 0, 0); + } + } + else if (exclude_count > 0) { + int fd = TAGFS_DATA->mntdir.fd; + DIR* dp = TAGFS_DATA->mntdir.dp; + rewinddir(dp); + struct dirent* ent; + while ((ent = readdir(dp))) { + struct stat stbuf; + if (fstatat(fd, ent->d_name, &stbuf, 0) == 0 && (stbuf.st_mode & S_IFDIR) == S_IFDIR) + continue; + + int skip = 0; + for (size_t i = 0; i < filter.files.excluded.len; ++i) { + if ((skip = strcmp(ent->d_name, filter.files.excluded.ptr[i]) == 0)) + break; + } + + if (skip) + continue; + + filler(buf, ent->d_name, NULL, 0, 0); + } + } + + filter_free(&filter); + + return 1; +} + +int +tagfs_op_readdir(const char* path, void* buf, fuse_fill_dir_t filler, off_t off, struct fuse_file_info* fi, enum fuse_readdir_flags flags) +{ + fuse_log(FUSE_LOG_DEBUG, "readdir(%s)\n", path); + + remove_dead_filters(); + + if (strcmp(path, "/") == 0) + { + unsigned pid = fuse_get_context()->pid; + int fd = open_process_filter(pid); + + while (fd == -1 && pid != 1) { + pid = proc_get_ppid(pid); + fd = open_process_filter(pid); + } + + if (fd == -1) { + filler(buf, TAGFS_DIR, NULL, 0, 0); + fill_readdir(&TAGFS_DATA->mntdir, buf, filler, 1); + } + else { + filler(buf, TAGFS_DIR, NULL, 0, 0); + if (!fill_readdir_filtered(fd, buf, filler)) + close(fd); + } + } + else if (strcmp(path, "/" TAGFS_DIR) == 0) + { + fill_readdir(&TAGFS_DATA->fsdir, buf, filler, 0); + } + else if (strcmp(path, "/" TAGFS_TAG_DIR) == 0) + { + fill_readdir(&TAGFS_DATA->tagdir, buf, filler, 0); + } + else if (strncmp(path, "/" TAGFS_TAG_DIR, sizeof("/" TAGFS_TAG_DIR) - 1) == 0) + { + const char* rpath = strrchr(path, '/') + 1; + int fd = openat(TAGFS_DATA->tagdir.fd, rpath, O_DIRECTORY|O_RDONLY); + if (fd == -1) + return -1; + + DIR* dp = fdopendir(fd); + struct dirinfo dir = { fd, dp, (struct stat){0} }; + fill_readdir(&dir, buf, filler, 0); + + closedir(dp); + close(fd); + } + else if (strcmp(path, "/" TAGFS_FILTER_DIR) == 0) + { + fill_readdir(&TAGFS_DATA->filterdir, buf, filler, 0); + } + + return 0; +} + +int +tagfs_op_readlink(const char* path, char* buf, size_t length) +{ + fuse_log(FUSE_LOG_DEBUG, "readlink(%s)\n", path); + + if (strncmp(path, "/" TAGFS_TAG_DIR, sizeof("/" TAGFS_TAG_DIR) - 1) != 0) + return -1; + + const char* rpath = &path[sizeof("/" TAGFS_TAG_DIR)]; + fuse_log(FUSE_LOG_DEBUG, "- rpath(%s)\n", rpath); + + readlinkat(TAGFS_DATA->tagdir.fd, rpath, buf, length); + + return 0; +} + +int +tagfs_op_symlink(const char* target, const char* path) +{ + fuse_log(FUSE_LOG_DEBUG, "symlink(%s, %s)\n", target, path); + + if (strncmp(path, "/" TAGFS_TAG_DIR, sizeof("/" TAGFS_TAG_DIR) - 1) != 0) + return -1; + + const char* rpath = &path[sizeof("/" TAGFS_TAG_DIR)]; + symlinkat(target, TAGFS_DATA->tagdir.fd, rpath); + + return 0; +} + +int +tagfs_op_getattr(const char* path, struct stat* st, struct fuse_file_info* fi) +{ + fuse_log(FUSE_LOG_DEBUG, "getattr(%s)\n", path); + + if (strcmp(path, "/") == 0) { + *st = TAGFS_DATA->mntdir.stat; + return 0; + } + + struct stat stbuf; + if (fstatat(TAGFS_DATA->mntdir.fd, path + 1, &stbuf, 0) == -1) + return -errno; + + *st = stbuf; + + return 0; +} + +int +tagfs_op_access(const char* path, int mask) +{ + fuse_log(FUSE_LOG_DEBUG, "access(%s)\n", path); + + if (strcmp(path, "/") == 0) { + return faccessat(TAGFS_DATA->mntdir.fd, ".", mask, 0); + + } + + return faccessat(TAGFS_DATA->mntdir.fd, path + 1, mask, 0); +} + +void +init_tagfs_dir(int dirfd) +{ + mode_t mode = 0774; + mkdirat(dirfd, TAGFS_DIR, mode); + mkdirat(dirfd, TAGFS_TAG_DIR, mode); + mkdirat(dirfd, TAGFS_FILTER_DIR, mode); +} + +int +main(int argc, char** argv) +{ + struct fuse_operations tagfs_ops = { + .open = tagfs_op_open, + .release = tagfs_op_release, + .read = tagfs_op_read, + .write = tagfs_op_write, + .create = tagfs_op_create, + .rename = tagfs_op_rename, + .mkdir = tagfs_op_mkdir, + .rmdir = tagfs_op_rmdir, + .unlink = tagfs_op_unlink, + .readdir = tagfs_op_readdir, + .readlink= tagfs_op_readlink, + .symlink = tagfs_op_symlink, + /*.chmod = tagfs_op_chmod, + .chown = tagfs_op_chown,*/ + .access = tagfs_op_access, + .getattr = tagfs_op_getattr, + }; + + const char* mntpoint = argv[argc - 1]; + struct tagfs_private_data data; + + if (!open_dirinfo(mntpoint, &data.mntdir)) + err(1, "open_dirinfo(mntpoint)"); + + init_tagfs_dir(data.mntdir.fd); + + if (!openat_dirinfo(data.mntdir.fd, TAGFS_DIR, &data.fsdir)) + err(1, "openat_dirinfo(fsdir)"); + + if (!openat_dirinfo(data.mntdir.fd, TAGFS_TAG_DIR, &data.tagdir)) + err(1, "openat_dirinfo(tagdir)"); + + if (!openat_dirinfo(data.mntdir.fd, TAGFS_FILTER_DIR, &data.filterdir)) + err(1, "openat_dirinfo(filterdir)"); + + // FIXME: check if mount point is availeble, otherwise exit + fuse_main(argc, argv, &tagfs_ops, &data); + + return 0; +} +