Initial commit
This commit is contained in:
commit
a93e4ab988
|
@ -0,0 +1,2 @@
|
|||
tagfs
|
||||
zdir
|
|
@ -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/
|
||||
|
|
@ -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
|
||||
#
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
#ifndef H_DIRINFO
|
||||
#define H_DIRINFO
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
|
||||
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
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
#ifndef H_FILTER
|
||||
#define H_FILTER
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#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
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
#ifndef H_PROC
|
||||
#define H_PROC
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <alloca.h>
|
||||
#include <limits.h>
|
||||
|
||||
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
|
||||
|
|
@ -0,0 +1,450 @@
|
|||
#include "defs.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "proc.h"
|
||||
#include "dirinfo.h"
|
||||
#include <fuse.h>
|
||||
#include <fuse_log.h>
|
||||
#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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue