diff options
Diffstat (limited to 'fusemount.c')
-rw-r--r-- | fusemount.c | 302 |
1 files changed, 296 insertions, 6 deletions
diff --git a/fusemount.c b/fusemount.c index 3ae2932..574a3fd 100644 --- a/fusemount.c +++ b/fusemount.c @@ -9,18 +9,172 @@ #include "fuse.h" #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> +#include <signal.h> +#include <dirent.h> +#include <assert.h> +#include <errno.h> +#include <sys/stat.h> #include <sys/types.h> #include <sys/mount.h> #include <mntent.h> +#include <glib.h> + +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)); @@ -28,15 +182,53 @@ static void loop(int devfd) perror("read"); exit(1); } - + printf("unique: %i, opcode: %i\n", param.unique, param.u.i.opcode); - param.u.o.result = 0; - + + 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"); + } } } @@ -81,10 +273,98 @@ static int mount_fuse(const char *dev, const char *dir, int devfd) 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; - const char *dir; int devfd; if(argc < 3) { @@ -93,7 +373,7 @@ int main(int argc, char *argv[]) } dev = argv[1]; - dir = argv[2]; + mount_dir = argv[2]; devfd = open(dev, O_RDWR); if(devfd == -1) { @@ -101,9 +381,19 @@ int main(int argc, char *argv[]) exit(1); } - mount_fuse(dev, dir, devfd); + 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; } + + + |