451 lines
9.6 KiB
C
451 lines
9.6 KiB
C
#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;
|
|
}
|
|
|