#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #define FUSE_USE_VERSION 317 #include 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); }