/* AVFS: A Virtual File System Library Copyright (C) 1998-2001 Miklos Szeredi (mszeredi@inf.bme.hu) This program can be distributed under the terms of the GNU GPL. See the file COPYING. */ #include "fuse.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static char *mount_dir; const char *basedir = "/tmp/pro"; struct node { char *name; unsigned long ino; }; static GNode *root; static GHashTable *nodetab; static unsigned long inoctr = FUSE_ROOT_INO; static GNode *new_node(const char *name, unsigned long ino) { struct node *node = g_new0(struct node, 1); GNode *gn = g_node_new(node); node->name = g_strdup(name); node->ino = ino; return gn; } static unsigned long find_node(unsigned long ino, const char *name) { GNode *cn; GNode *pn = g_hash_table_lookup(nodetab, (gpointer) ino); if(pn == NULL) { fprintf(stderr, "Can't find parent node %li\n", ino); return 0; } for(cn = pn->children; cn != NULL; cn = cn->next) { struct node *node = (struct node *) cn->data; if(strcmp(node->name, name) == 0) return node->ino; } do inoctr++; while(!inoctr && g_hash_table_lookup(nodetab, (gpointer) ino) != NULL); cn = new_node(name, inoctr); g_node_insert(pn, -1, cn); g_hash_table_insert(nodetab, (gpointer) inoctr, cn); return inoctr; } static char *real_path(unsigned long ino) { GString *s; char *ss; GNode *gn = g_hash_table_lookup(nodetab, (gpointer) ino); if(gn == NULL) { fprintf(stderr, "Can't find node %li\n", ino); return NULL; } s = g_string_new(""); for(; gn != NULL; gn = gn->parent) { g_string_prepend(s, ((struct node *) gn->data)->name); g_string_prepend_c(s, '/'); } g_string_prepend(s, basedir); ss = s->str; g_string_free(s, FALSE); return ss; } static int get_dir(unsigned long dir) { int dirfd; struct fuse_dirent dirent; DIR *dp; struct dirent *de; size_t reclen; char *path; path = real_path(dir); if(path == NULL) return -ENOENT; dp = opendir(path); g_free(path); if(dp == NULL) { perror(path); return -errno; } dirfd = open("/tmp/dirtmp", O_RDWR | O_TRUNC | O_CREAT, 0600); if(dirfd == -1) { perror("/tmp/dirtmp"); exit(1); } while((de = readdir(dp)) != NULL) { unsigned long ino = find_node(dir, de->d_name); assert(ino != 0); dirent.ino = ino; dirent.namelen = strlen(de->d_name); assert(dirent.namelen <= NAME_MAX); strcpy(dirent.name, de->d_name); dirent.type = de->d_type; reclen = FUSE_DIRENT_SIZE(&dirent); write(dirfd, &dirent, reclen); } closedir(dp); return dirfd; } static int get_attributes(unsigned long ino, struct fuse_attr *attr) { char *path; struct stat buf; int res; path = real_path(ino); if(path == NULL) return -ENOENT; res = stat(path, &buf); g_free(path); if(res == -1) return -errno; attr->mode = buf.st_mode; attr->nlink = buf.st_nlink; attr->uid = buf.st_uid; attr->gid = buf.st_gid; attr->rdev = buf.st_rdev; attr->size = buf.st_size; attr->blksize = buf.st_blksize; attr->blocks = buf.st_blocks; attr->atime = buf.st_atime; attr->mtime = buf.st_mtime; attr->ctime = buf.st_ctime; return 0; } static void loop(int devfd) { int res; struct fuse_param param; struct fuse_outparam out; struct fuse_inparam in; int dirfd; while(1) { res = read(devfd, ¶m, sizeof(param)); if(res == -1) { perror("read"); exit(1); } printf("unique: %i, opcode: %i\n", param.unique, param.u.i.opcode); dirfd = -1; in = param.u.i; switch(in.opcode) { case FUSE_LOOKUP: out.u.lookup.ino = find_node(in.ino, in.u.lookup.name); if(out.u.lookup.ino == 0) out.result = -ENOENT; else out.result = get_attributes(out.u.lookup.ino, &out.u.lookup.attr); break; case FUSE_GETATTR: out.result = get_attributes(in.ino, &out.u.getattr.attr); break; case FUSE_OPEN: dirfd = get_dir(in.ino); if(dirfd >= 0) { out.u.open.fd = dirfd; out.result = 0; } else out.result = dirfd; break; case FUSE_RELEASE: out.result = 0; break; default: out.result = -EOPNOTSUPP; } param.u.o = out; res = write(devfd, ¶m, sizeof(param)); if(res == -1) { perror("write"); exit(1); } if(dirfd != -1) { close(dirfd); unlink("/tmp/dirtmp"); } } } static int mount_fuse(const char *dev, const char *dir, int devfd) { int res; const char *type; FILE *fd; struct mntent ent; struct fuse_mount_data data; data.version = FUSE_MOUNT_VERSION; data.fd = devfd; type = "fuse"; res = mount(dev, dir, type, MS_MGC_VAL | MS_NOSUID | MS_NODEV, &data); if(res == -1) { fprintf(stderr, "mount failed: %s\n", strerror(errno)); return -1; } fd = setmntent("/etc/mtab", "a"); if(fd == NULL) { fprintf(stderr, "setmntent(\"/etc/mtab\") failed: %s\n", strerror(errno)); return -1; } ent.mnt_fsname = (char *) dev; ent.mnt_dir = (char *) dir; ent.mnt_type = (char *) type; ent.mnt_opts = "rw,nosuid,nodev"; ent.mnt_freq = 0; ent.mnt_passno = 0; res = addmntent(fd, & ent); if(res != 0) fprintf(stderr, "addmntent() failed: %s\n", strerror(errno)); endmntent(fd); return 0; } int unmount_fuse(const char *dir) { int res; FILE *fdold, *fdnew; struct mntent *entp; res = umount(dir); if(res == -1) { fprintf(stderr, "umount failed: %s\n", strerror(errno)); return -1; } fdold = setmntent("/etc/mtab", "r"); if(fdold == NULL) { fprintf(stderr, "setmntent(\"/etc/mtab\") failed: %s\n", strerror(errno)); return -1; } fdnew = setmntent("/etc/mtab~", "w"); if(fdnew == NULL) { fprintf(stderr, "setmntent(\"/etc/mtab~\") failed: %s\n", strerror(errno)); return -1; } do { entp = getmntent(fdold); if(entp != NULL) { if(strcmp(entp->mnt_dir, dir) != 0) { res = addmntent(fdnew, entp); if(res != 0) { fprintf(stderr, "addmntent() failed: %s\n", strerror(errno)); } } } } while(entp != NULL); endmntent(fdold); endmntent(fdnew); res = rename("/etc/mtab~", "/etc/mtab"); if(res == -1) { fprintf(stderr, "rename(\"/etc/mtab~\", \"/etc/mtab\") failed: %s\n", strerror(errno)); return -1; } return 0; } void cleanup() { unmount_fuse(mount_dir); } void exit_handler() { exit(0); } void set_signal_handlers() { struct sigaction sa; sa.sa_handler = exit_handler; sigemptyset(&(sa.sa_mask)); sa.sa_flags = 0; if (sigaction(SIGHUP, &sa, NULL) == -1 || sigaction(SIGINT, &sa, NULL) == -1 || sigaction(SIGTERM, &sa, NULL) == -1) { perror("Cannot set exit signal handlers"); exit(1); } sa.sa_handler = SIG_IGN; if(sigaction(SIGPIPE, &sa, NULL) == -1) { perror("Cannot set ignored signals"); exit(1); } } int main(int argc, char *argv[]) { const char *dev; int devfd; if(argc < 3) { fprintf(stderr, "usage: %s dev dir\n", argv[0]); exit(1); } dev = argv[1]; mount_dir = argv[2]; devfd = open(dev, O_RDWR); if(devfd == -1) { fprintf(stderr, "failed to open %s: %s\n", dev, strerror(errno)); exit(1); } mount_fuse(dev, mount_dir, devfd); set_signal_handlers(); atexit(cleanup); root = new_node("/", FUSE_ROOT_INO); nodetab = g_hash_table_new(NULL, NULL); g_hash_table_insert(nodetab, (gpointer) FUSE_ROOT_INO, root); loop(devfd); return 0; }