254 lines
4.7 KiB
C
254 lines
4.7 KiB
C
#define _GNU_SOURCE
|
|
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
|
|
#define FUSE_USE_VERSION 317
|
|
|
|
#include <fuse3/fuse.h>
|
|
|
|
static char *progname;
|
|
static int fd1, fd2;
|
|
|
|
static int get_fd() {
|
|
pid_t pid = fork();
|
|
if (pid == -1) {
|
|
perror("fork");
|
|
exit(1);
|
|
}
|
|
if (pid == 0) {
|
|
execl(progname, progname, (char *) NULL);
|
|
|
|
exit(2);
|
|
}
|
|
|
|
int wstatus;
|
|
if (waitpid(pid, &wstatus, 0) == -1) {
|
|
perror("waitpid");
|
|
exit(1);
|
|
}
|
|
|
|
int code = WEXITSTATUS(wstatus);
|
|
if (code == 0) return fd1;
|
|
if (code == 1) return fd2;
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int condviewfs_open(const char *name, struct fuse_file_info *fi) {
|
|
int dirfd = get_fd();
|
|
if (dirfd == -1)
|
|
return -ENOLINK;
|
|
|
|
int fd = openat(dirfd, &name[1], fi->flags);
|
|
if (fd < 0)
|
|
return -errno;
|
|
|
|
fi->fh = (uint64_t)fd;
|
|
return 0;
|
|
}
|
|
|
|
static int condviewfs_close(const char *name, struct fuse_file_info *fi) {
|
|
if (close((int)fi->fh))
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int condviewfs_getattr(const char *name,
|
|
struct stat *stbuf,
|
|
struct fuse_file_info *fi) {
|
|
int dirfd = get_fd();
|
|
if (dirfd == -1)
|
|
return -ENOLINK;
|
|
|
|
int ret;
|
|
|
|
if (fi)
|
|
ret = fstat((int)fi->fh, stbuf);
|
|
else
|
|
ret = fstatat(dirfd,
|
|
&name[1],
|
|
stbuf,
|
|
AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
|
|
|
|
if (ret < 0)
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int condviewfs_access(const char *name, int mask) {
|
|
int dirfd = get_fd();
|
|
if (dirfd == -1)
|
|
return -ENOLINK;
|
|
|
|
if ((name[0] != '/') || (name[1] != '\0'))
|
|
++name;
|
|
|
|
if (faccessat(dirfd, name, mask, AT_SYMLINK_NOFOLLOW) < 0)
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int condviewfs_read(const char *path,
|
|
char *buf,
|
|
size_t size,
|
|
off_t off,
|
|
struct fuse_file_info *fi) {
|
|
ssize_t out;
|
|
|
|
out = pread((int)fi->fh, buf, size > INT_MAX ? INT_MAX : size, off);
|
|
if (out < 0)
|
|
return -errno;
|
|
|
|
return (int)out;
|
|
}
|
|
|
|
static int condviewfs_opendir(const char *name, struct fuse_file_info *fi) {
|
|
DIR *dirp;
|
|
int dirfd = get_fd();
|
|
if (dirfd == -1)
|
|
return -ENOLINK;
|
|
|
|
int fd, err;
|
|
|
|
if ((name[0] == '/') && (name[1] == '\0'))
|
|
fd = dup(dirfd);
|
|
else
|
|
fd = openat(dirfd, &name[1], O_DIRECTORY);
|
|
|
|
if (fd < 0)
|
|
return -errno;
|
|
|
|
dirp = fdopendir(fd);
|
|
if (!dirp) {
|
|
err = -errno;
|
|
close(fd);
|
|
return err;
|
|
}
|
|
|
|
fi->fh = (uint64_t)dirp;
|
|
return 0;
|
|
}
|
|
|
|
static int condviewfs_closedir(const char *name, struct fuse_file_info *fi) {
|
|
if (closedir((DIR *)(uintptr_t)fi->fh) < 0)
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int condviewfs_readdir(const char *path,
|
|
void *buf,
|
|
fuse_fill_dir_t filler,
|
|
off_t offset,
|
|
struct fuse_file_info *fi,
|
|
enum fuse_readdir_flags flags) {
|
|
int dirfd = get_fd();
|
|
if (dirfd == -1)
|
|
return -ENOLINK;
|
|
|
|
DIR *dirp = (DIR *)(uintptr_t)fi->fh;
|
|
|
|
if (offset == 0)
|
|
rewinddir(dirp);
|
|
|
|
struct stat stbuf;
|
|
struct dirent *dent;
|
|
while (1) {
|
|
errno = 0;
|
|
dent = readdir(dirp);
|
|
if (!dent) {
|
|
if (errno == 0)
|
|
break;
|
|
return -errno;
|
|
}
|
|
|
|
if (fstatat(dirfd,
|
|
dent->d_name,
|
|
&stbuf,
|
|
AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW) < 0)
|
|
return -errno;
|
|
|
|
if (filler(buf, dent->d_name, &stbuf, 0, FUSE_FILL_DIR_DEFAULTS) == 1) {
|
|
return -errno;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int condviewfs_readlink(const char *name, char *buf, size_t size) {
|
|
int dirfd = get_fd();
|
|
if (dirfd == -1)
|
|
return -ENOLINK;
|
|
|
|
ssize_t len;
|
|
|
|
len = readlinkat(dirfd,
|
|
&name[1],
|
|
buf,
|
|
size - 1);
|
|
if (len < 0)
|
|
return -errno;
|
|
|
|
buf[len] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
static struct fuse_operations condviewfs_oper = {
|
|
.open = condviewfs_open,
|
|
.release = condviewfs_close,
|
|
|
|
.read = condviewfs_read,
|
|
|
|
.getattr = condviewfs_getattr,
|
|
.access = condviewfs_access,
|
|
|
|
.opendir = condviewfs_opendir,
|
|
.releasedir = condviewfs_closedir,
|
|
.readdir = condviewfs_readdir,
|
|
|
|
.readlink = condviewfs_readlink,
|
|
};
|
|
|
|
int main(int argc, char *argv[]) {
|
|
char *fuse_argv[5];
|
|
|
|
if (argc != 5) {
|
|
fprintf(stderr, "Usage: %s target dir1 dir2 prog\n", argv[0]);
|
|
exit(-1);
|
|
}
|
|
|
|
fd1 = open(argv[2], O_DIRECTORY);
|
|
if (fd1 < 0) {
|
|
perror("open 1");
|
|
exit(-1);
|
|
}
|
|
|
|
fd2 = open(argv[3], O_DIRECTORY);
|
|
if (fd2 < 0) {
|
|
perror("open 2");
|
|
exit(-1);
|
|
}
|
|
|
|
progname = argv[4];
|
|
|
|
fuse_argv[0] = argv[0];
|
|
fuse_argv[1] = argv[1];
|
|
fuse_argv[2] = "-oallow_other,default_permissions";
|
|
fuse_argv[3] = "-f";
|
|
fuse_argv[4] = NULL;
|
|
return fuse_main(4, fuse_argv, &condviewfs_oper, NULL);
|
|
}
|