diff options
author | Miklos Szeredi <miklos@szeredi.hu> | 2001-10-28 19:44:14 +0000 |
---|---|---|
committer | Miklos Szeredi <miklos@szeredi.hu> | 2001-10-28 19:44:14 +0000 |
commit | 85c74fcdfd9e67d411c3e1734b34effd0d73fa4d (patch) | |
tree | 908e39d3e0b84bd733261cdde16ef6ae707f2352 /lib | |
parent | 90d8bef61c8c40472ddfb1aafeeb6473ec51a053 (diff) | |
download | libfuse-85c74fcdfd9e67d411c3e1734b34effd0d73fa4d.tar.gz |
x
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile | 16 | ||||
-rw-r--r-- | lib/fuse.c | 393 | ||||
-rw-r--r-- | lib/fuse_i.h | 33 | ||||
-rw-r--r-- | lib/mount.c | 142 |
4 files changed, 584 insertions, 0 deletions
diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 0000000..e72a39d --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,16 @@ +CC = gcc +CFLAGS = -Wall -W -g `glib-config --cflags` +LDFLAGS = `glib-config --libs` +CPPFLAGS = -I../include + + +all: libfuse.a + +libfuse_objs = mount.o fuse.o + +libfuse.a: $(libfuse_objs) + ar cr libfuse.a $(libfuse_objs) + +clean: + rm -f *.o *.a + rm -f *~ diff --git a/lib/fuse.c b/lib/fuse.c new file mode 100644 index 0000000..f846f74 --- /dev/null +++ b/lib/fuse.c @@ -0,0 +1,393 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 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_i.h" +#include <linux/fuse.h> + +#include <string.h> +#include <unistd.h> +#include <errno.h> + + +static guint name_hash(const struct node *node) +{ + return g_str_hash(node->name) ^ node->parent; +} + +static gint name_compare(const struct node *node1, const struct node *node2) +{ + return + node1->parent == node2->parent && + strcmp(node1->name, node2->name) == 0; +} + +static struct node *new_node(fino_t parent, const char *name) +{ + struct node *node = g_new0(struct node, 1); + node->name = strdup(name); + node->parent = parent; + return node; +} + +static int free_node(struct node *node) +{ + g_free(node->name); + g_free(node); + return 1; +} + +static inline struct node *get_node(fino_t ino) +{ + return (struct node *) ino; +} + +static inline fino_t get_ino(struct node *node) +{ + return (fino_t) node; +} + + +static fino_t find_node(struct fuse *f, fino_t parent, char *name, int create) +{ + struct node *node; + struct node tmp; + + tmp.name = name; + tmp.parent = parent; + + node = g_hash_table_lookup(f->nametab, &tmp); + if(node != NULL) + return get_ino(node); + + if(!create) + return (fino_t) -1; + + node = new_node(parent, name); + g_hash_table_insert(f->nametab, node, node); + return get_ino(node); +} + +static char *get_path(fino_t ino) +{ + GString *s; + char *ss; + + s = g_string_new(""); + if(ino == FUSE_ROOT_INO) + g_string_prepend_c(s, '/'); + else { + struct node *node; + for(; ino != FUSE_ROOT_INO; ino = node->parent) { + node = get_node(ino); + g_string_prepend(s, node->name); + g_string_prepend_c(s, '/'); + } + } + + ss = s->str; + g_string_free(s, FALSE); + + return ss; +} + +static void remove_node(struct fuse *f, fino_t ino) +{ + struct node *node = get_node(ino); + g_hash_table_remove(f->nametab, node); + free_node(node); +} + + +static void convert_stat(struct stat *stbuf, struct fuse_attr *attr) +{ + attr->mode = stbuf->st_mode; + attr->nlink = stbuf->st_nlink; + attr->uid = stbuf->st_uid; + attr->gid = stbuf->st_gid; + attr->rdev = stbuf->st_rdev; + attr->size = stbuf->st_size; + attr->blksize = stbuf->st_blksize; + attr->blocks = stbuf->st_blocks; + attr->atime = stbuf->st_atime; + attr->mtime = stbuf->st_mtime; + attr->ctime = stbuf->st_ctime; +} + +static int get_attributes(struct fuse *f, fino_t ino, struct fuse_attr *attr) +{ + char *path; + struct stat buf; + int res; + + if(f->op.getattr == NULL) + return -ENOSYS; + + path = get_path(ino); + res = f->op.getattr(path, &buf); + g_free(path); + if(res == 0) + convert_stat(&buf, attr); + + return res; +} + +static int read_link(struct fuse *f, fino_t ino, char *buf, size_t size) +{ + char *path; + int res; + + if(f->op.readlink == NULL) + return -ENOSYS; + + path = get_path(ino); + res = f->op.readlink(path, buf, size); + g_free(path); + + return res; +} + +static int fill_dir(struct fuse_dh *dh, char *name, int type) +{ + struct fuse_dirent dirent; + size_t reclen; + size_t res; + + dirent.ino = find_node(dh->fuse, dh->dir, name, 0); + dirent.namelen = strlen(name); + strncpy(dirent.name, name, sizeof(dirent.name)); + dirent.type = type; + reclen = FUSE_DIRENT_SIZE(&dirent); + res = fwrite(&dirent, reclen, 1, dh->fp); + if(res == 0) { + perror("writing directory file"); + return -EIO; + } + return 0; +} + +static int get_dir(struct fuse *f, fino_t ino, FILE *fp) +{ + char *path; + int res; + struct fuse_dh dh; + + if(f->op.getdir == NULL) + return -ENOSYS; + + dh.fuse = f; + dh.fp = fp; + dh.dir = ino; + + path = get_path(ino); + res = f->op.getdir(path, &dh, (dirfiller_t) fill_dir); + g_free(path); + + return res; +} + + +static void send_reply(struct fuse *f, struct fuse_in_header *in, int result, + void *arg, size_t argsize) +{ + int res; + char *outbuf; + size_t outsize; + struct fuse_out_header *out; + + if(result > 0) { + fprintf(stderr, "positive result to operation %i : %i\n", in->opcode, + result); + result = -ERANGE; + } + + if(result != 0) + argsize = 0; + + outsize = sizeof(struct fuse_out_header) + argsize; + outbuf = (char *) g_malloc(outsize); + out = (struct fuse_out_header *) outbuf; + out->unique = in->unique; + out->result = result; + if(argsize != 0) + memcpy(outbuf + sizeof(struct fuse_out_header), arg, argsize); + + printf(" unique: %i, result: %i (%s), outsize: %i\n", out->unique, + out->result, strerror(-out->result), outsize); + + res = write(f->fd, outbuf, outsize); + if(res == -1) + perror("writing fuse device"); + + g_free(outbuf); +} + +static void do_lookup(struct fuse *f, struct fuse_in_header *in, char *name) +{ + int res; + struct fuse_lookup_out arg; + + arg.ino = find_node(f, in->ino, name, 1); + res = get_attributes(f, arg.ino, &arg.attr); + + send_reply(f, in, res, &arg, sizeof(arg)); +} + + +static void do_forget(struct fuse *f, unsigned long *inos, size_t num) +{ + size_t i; + + for(i = 0; i < num; i++) + remove_node(f, inos[i]); +} + +static void do_getattr(struct fuse *f, struct fuse_in_header *in) +{ + int res; + struct fuse_getattr_out arg; + + res = get_attributes(f, in->ino, &arg.attr); + send_reply(f, in, res, &arg, sizeof(arg)); +} + +static void do_readlink(struct fuse *f, struct fuse_in_header *in) +{ + int res; + char link[PATH_MAX + 1]; + + res = read_link(f, in->ino, link, PATH_MAX + 1); + send_reply(f, in, res, link, res == 0 ? strlen(link) : 0); +} + +static void do_mknod(struct fuse *f, struct fuse_in_header *in, + struct fuse_mknod_in *inarg) +{ + int res; + struct fuse_mknod_out outarg; + + res = -ENOSYS; + if(f->op.mknod != NULL && f->op.getattr != NULL) { + char *path; + struct stat buf; + + outarg.ino = find_node(f, in->ino, inarg->name, 1); + path = get_path(outarg.ino); + res = f->op.mknod(path, inarg->mode, inarg->rdev); + if(res == 0) + res = f->op.getattr(path, &buf); + g_free(path); + + if(res == 0) + convert_stat(&buf, &outarg.attr); + else + remove_node(f, outarg.ino); + } + send_reply(f, in, res, &outarg, sizeof(outarg)); +} + +static void do_getdir(struct fuse *f, struct fuse_in_header *in) +{ + int res; + struct fuse_getdir_out arg; + FILE *fp = tmpfile(); + + res = get_dir(f, in->ino, fp); + fflush(fp); + arg.fd = fileno(fp); + send_reply(f, in, res, &arg, sizeof(arg)); + fclose(fp); +} + +void fuse_loop(struct fuse *f) +{ + int res; + char inbuf[FUSE_MAX_IN]; + struct fuse_in_header *in = (struct fuse_in_header *) inbuf; + void *inarg = inbuf + sizeof(struct fuse_in_header); + size_t insize; + size_t argsize; + + while(1) { + res = read(f->fd, inbuf, sizeof(inbuf)); + if(res == -1) { + perror("reading fuse device"); + continue; + } + insize = res; + + if(insize < sizeof(struct fuse_in_header)) { + fprintf(stderr, "short read on fuse device\n"); + continue; + } + printf("unique: %i, opcode: %i, ino: %li, insize: %i (%i)\n", + in->unique, in->opcode, in->ino, insize, + g_hash_table_size(f->nametab)); + + argsize = insize - sizeof(struct fuse_in_header); + + switch(in->opcode) { + case FUSE_LOOKUP: + do_lookup(f, in, (char *) inarg); + break; + + case FUSE_FORGET: + do_forget(f, (unsigned long *) inarg, + argsize / sizeof(unsigned long)); + break; + + case FUSE_GETATTR: + do_getattr(f, in); + break; + + case FUSE_READLINK: + do_readlink(f, in); + break; + + case FUSE_GETDIR: + do_getdir(f, in); + break; + + case FUSE_MKNOD: + do_mknod(f, in, (struct fuse_mknod_in *) inarg); + break; + + default: + fprintf(stderr, "Operation %i not implemented\n", in->opcode); + /* No need to send reply to async requests */ + if(in->unique != 0) + send_reply(f, in, -ENOSYS, NULL, 0); + } + } +} + +struct fuse *fuse_new() +{ + struct fuse *f = g_new0(struct fuse, 1); + + f->fd = -1; + f->dir = NULL; + f->nametab = g_hash_table_new((GHashFunc) name_hash, + (GCompareFunc) name_compare); + + return f; +} + + +void fuse_set_operations(struct fuse *f, const struct fuse_operations *op) +{ + f->op = *op; +} + +void fuse_destroy(struct fuse *f) +{ + fuse_unmount(f); + g_hash_table_foreach_remove(f->nametab, (GHRFunc) free_node, NULL); + g_hash_table_destroy(f->nametab); + g_free(f); +} + diff --git a/lib/fuse_i.h b/lib/fuse_i.h new file mode 100644 index 0000000..b3b907b --- /dev/null +++ b/lib/fuse_i.h @@ -0,0 +1,33 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 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 <glib.h> +#include <stdio.h> + +#define FUSE_DEV "/proc/fs/fuse/dev" + +typedef unsigned long fino_t; + +struct node { + char *name; + fino_t parent; +}; + +struct fuse { + char *dir; + int fd; + struct fuse_operations op; + GHashTable *nametab; +}; + +struct fuse_dh { + struct fuse *fuse; + fino_t dir; + FILE *fp; +}; diff --git a/lib/mount.c b/lib/mount.c new file mode 100644 index 0000000..ace3c65 --- /dev/null +++ b/lib/mount.c @@ -0,0 +1,142 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 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_i.h" +#include <linux/fuse.h> + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <sys/mount.h> +#include <mntent.h> + +static int do_mount(const char *dev, const char *dir, const char *type, int fd) +{ + int res; + struct fuse_mount_data data; + + data.version = FUSE_MOUNT_VERSION; + data.fd = fd; + + res = mount(dev, dir, type, MS_MGC_VAL | MS_NOSUID | MS_NODEV, &data); + if(res == -1) { + perror("mount failed"); + return -1; + } + + return 0; +} + +static void add_mntent(const char *dev, const char *dir, const char *type) +{ + int res; + FILE *fp; + struct mntent ent; + + fp = setmntent("/etc/mtab", "a"); + if(fp == NULL) { + perror("setmntent"); + return; + } + + 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(fp, & ent); + if(res != 0) + perror("addmntent"); + + endmntent(fp); + +} + +static void remove_mntent(const char *dir) +{ + int res; + FILE *fdold, *fdnew; + struct mntent *entp; + + fdold = setmntent("/etc/mtab", "r"); + if(fdold == NULL) { + perror("/etc/mtab"); + return; + } + + fdnew = setmntent("/etc/mtab~", "w"); + if(fdnew == NULL) { + perror("/etc/mtab~"); + return; + } + + do { + entp = getmntent(fdold); + if(entp != NULL && strcmp(entp->mnt_dir, dir) != 0) { + res = addmntent(fdnew, entp); + if(res != 0) + perror("addmntent"); + } + } while(entp != NULL); + + endmntent(fdold); + endmntent(fdnew); + + res = rename("/etc/mtab~", "/etc/mtab"); + if(res == -1) + perror("renameing /etc/mtab~ to /etc/mtab"); +} + +int fuse_mount(struct fuse *f, const char *dir) +{ + int res; + const char *dev = FUSE_DEV; + const char *type = "fuse"; + + if(f->dir != NULL) + return 0; + + f->dir = g_strdup(dir); + f->fd = open(dev, O_RDWR); + if(f->fd == -1) { + perror(dev); + return -1; + } + + res = do_mount(dev, dir, type, f->fd); + if(res == -1) + return -1; + + add_mntent(dev, dir, type); + + return 0; +} + +int fuse_unmount(struct fuse *f) +{ + int res; + + if(f->dir == NULL) + return 0; + + close(f->fd); + f->fd = -1; + + res = umount(f->dir); + if(res == -1) + perror("umount failed"); + else + remove_mntent(f->dir); + + g_free(f->dir); + f->dir = NULL; + + return res; +} |