diff options
Diffstat (limited to 'lib/fuse.c')
-rw-r--r-- | lib/fuse.c | 493 |
1 files changed, 321 insertions, 172 deletions
@@ -9,12 +9,23 @@ #include "fuse_i.h" #include <linux/fuse.h> + #include <string.h> #include <unistd.h> #include <errno.h> #include <assert.h> +static inline struct node *get_node(fino_t ino) +{ + return (struct node *) ((ino << 3) + 0x8000000); +} + +static inline fino_t get_ino(struct node *node) +{ + return (((fino_t) node) - 0x8000000) >> 3; +} + static guint name_hash(const struct node *node) { return g_str_hash(node->name) ^ node->parent; @@ -27,15 +38,6 @@ static gint name_compare(const struct node *node1, const struct node *node2) strcmp(node1->name, node2->name) == 0; } -static struct node *new_node(fino_t parent, const char *name, int mode) -{ - struct node *node = g_new0(struct node, 1); - node->name = g_strdup(name); - node->parent = parent; - node->mode = mode; - return node; -} - static int free_node(struct node *node) { g_free(node->name); @@ -43,16 +45,6 @@ static int free_node(struct node *node) return 1; } -static inline struct node *get_node(fino_t ino) -{ - return (struct node *) ((ino << 3) + 0x8000000); -} - -static inline fino_t get_ino(struct node *node) -{ - return (((fino_t) node) - 0x8000000) >> 3; -} - static struct node *lookup_node(struct fuse *f, fino_t parent, const char *name) { @@ -64,29 +56,51 @@ static struct node *lookup_node(struct fuse *f, fino_t parent, return g_hash_table_lookup(f->nametab, &tmp); } +static void hash_node(struct fuse *f, struct node *node, fino_t parent, + const char *name) +{ + node->name = g_strdup(name); + node->parent = parent; + g_hash_table_insert(f->nametab, node, node); +} + static void unhash_node(struct fuse *f, struct node *node) { - g_hash_table_remove(f->nametab, node); - g_free(node->name); - node->parent = 0; - node->name = NULL; + if(node->name != NULL) { + g_hash_table_remove(f->nametab, node); + g_free(node->name); + node->parent = 0; + node->name = NULL; + } } -static fino_t find_node(struct fuse *f, fino_t parent, char *name, int mode) +static fino_t find_node(struct fuse *f, fino_t parent, char *name, + struct fuse_attr *attr, int version) { struct node *node; - mode &= S_IFMT; + int mode = attr->mode & S_IFMT; + int rdev = 0; + + if(S_ISCHR(mode) || S_ISBLK(mode)) + rdev = attr->rdev; + pthread_mutex_lock(&f->lock); node = lookup_node(f, parent, name); if(node != NULL) { - if(node->mode == mode) - return get_ino(node); - + if(node->mode == mode && node->rdev == rdev) + goto out; + unhash_node(f, node); } - node = new_node(parent, name, mode); - g_hash_table_insert(f->nametab, node, node); + node = g_new0(struct node, 1); + node->mode = mode; + node->rdev = rdev; + hash_node(f, node, parent, name); + + out: + node->version = version; + pthread_mutex_unlock(&f->lock); return get_ino(node); } @@ -94,14 +108,17 @@ static fino_t find_node_dir(struct fuse *f, fino_t parent, char *name) { struct node *node; + pthread_mutex_lock(&f->lock); node = lookup_node(f, parent, name); + pthread_mutex_unlock(&f->lock); + if(node != NULL) return get_ino(node); else return (fino_t) -1; } -static char *get_path(fino_t ino) +static char *get_path(struct fuse *f, fino_t ino) { GString *s; char *ss; @@ -111,15 +128,18 @@ static char *get_path(fino_t ino) g_string_prepend_c(s, '/'); else { struct node *node; + pthread_mutex_lock(&f->lock); for(; ino != FUSE_ROOT_INO; ino = node->parent) { node = get_node(ino); if(node->name == NULL) { + pthread_mutex_unlock(&f->lock); g_string_free(s, TRUE); return NULL; } g_string_prepend(s, node->name); g_string_prepend_c(s, '/'); } + pthread_mutex_unlock(&f->lock); } ss = s->str; @@ -128,49 +148,66 @@ static char *get_path(fino_t ino) return ss; } -static char *get_path_name(fino_t ino, const char *name) +static char *get_path_name(struct fuse *f, fino_t ino, const char *name) { - char *path; char *path2; + char *path; - path = get_path(ino); + if(ino == FUSE_ROOT_INO) + return g_strconcat("/", name, NULL); + + path = get_path(f, ino); if(path == NULL) return NULL; - + path2 = g_strconcat(path, "/", name, NULL); g_free(path); + return path2; } -static void destroy_node(struct fuse *f, fino_t ino) +static void destroy_node(struct fuse *f, fino_t ino, int version) { - struct node *node = get_node(ino); - unhash_node(f, node); - free_node(node); + struct node *node; + + pthread_mutex_lock(&f->lock); + node = get_node(ino); + if(node->version == version) { + unhash_node(f, node); + free_node(node); + } + pthread_mutex_unlock(&f->lock); + } static void remove_node(struct fuse *f, fino_t dir, const char *name) { - struct node *node = lookup_node(f, dir, name); + struct node *node; + + pthread_mutex_lock(&f->lock); + node = lookup_node(f, dir, name); assert(node != NULL); unhash_node(f, node); + pthread_mutex_unlock(&f->lock); } static void rename_node(struct fuse *f, fino_t olddir, const char *oldname, fino_t newdir, const char *newname) { - struct node *node = lookup_node(f, olddir, oldname); - struct node *newnode = lookup_node(f, newdir, newname); + struct node *node; + struct node *newnode; + + pthread_mutex_lock(&f->lock); + node = lookup_node(f, olddir, oldname); + newnode = lookup_node(f, newdir, newname); assert(node != NULL); if(newnode != NULL) unhash_node(f, newnode); - g_hash_table_remove(f->nametab, node); - g_free(node->name); - node->name = g_strdup(newname); - node->parent = newdir; - g_hash_table_insert(f->nametab, node, node); + unhash_node(f, node); + hash_node(f, node, newdir, newname); + pthread_mutex_unlock(&f->lock); } @@ -189,7 +226,7 @@ static void convert_stat(struct stat *stbuf, struct fuse_attr *attr) attr->ctime = stbuf->st_ctime; } -static int fill_dir(struct fuse_dh *dh, char *name, int type) +static int fill_dir(struct fuse_dirhandle *dh, char *name, int type) { struct fuse_dirent dirent; size_t reclen; @@ -234,6 +271,7 @@ static void send_reply(struct fuse *f, struct fuse_in_header *in, int error, printf(" unique: %i, error: %i (%s), outsize: %i\n", out->unique, out->error, strerror(-out->error), outsize); + fflush(stdout); res = write(f->fd, outbuf, outsize); if(res == -1) @@ -242,34 +280,40 @@ static void send_reply(struct fuse *f, struct fuse_in_header *in, int error, g_free(outbuf); } +static void fill_cred(struct fuse_in_header *in, struct fuse_cred *cred) +{ + cred->uid = in->uid; + cred->gid = in->gid; +} + static void do_lookup(struct fuse *f, struct fuse_in_header *in, char *name) { int res; char *path; struct stat buf; struct fuse_lookup_out arg; + struct fuse_cred cred; + fill_cred(in, &cred); res = -ENOENT; - path = get_path_name(in->ino, name); + path = get_path_name(f, in->ino, name); if(path != NULL) { res = -ENOSYS; if(f->op.getattr) - res = f->op.getattr(path, &buf); + res = f->op.getattr(&cred, path, &buf); g_free(path); } if(res == 0) { convert_stat(&buf, &arg.attr); - arg.ino = find_node(f, in->ino, name, arg.attr.mode); + arg.ino = find_node(f, in->ino, name, &arg.attr, in->unique); } send_reply(f, in, res, &arg, sizeof(arg)); } -static void do_forget(struct fuse *f, unsigned long *inos, size_t num) +static void do_forget(struct fuse *f, struct fuse_in_header *in, + struct fuse_forget_in *arg) { - size_t i; - - for(i = 0; i < num; i++) - destroy_node(f, inos[i]); + destroy_node(f, in->ino, arg->version); } static void do_getattr(struct fuse *f, struct fuse_in_header *in) @@ -278,13 +322,15 @@ static void do_getattr(struct fuse *f, struct fuse_in_header *in) char *path; struct stat buf; struct fuse_getattr_out arg; + struct fuse_cred cred; + fill_cred(in, &cred); res = -ENOENT; - path = get_path(in->ino); + path = get_path(f, in->ino); if(path != NULL) { res = -ENOSYS; if(f->op.getattr) - res = f->op.getattr(path, &buf); + res = f->op.getattr(&cred, path, &buf); g_free(path); } if(res == 0) @@ -300,15 +346,18 @@ static void do_setattr(struct fuse *f, struct fuse_in_header *in, char *path; int valid = arg->valid; struct fuse_attr *attr = &arg->attr; + struct fuse_setattr_out outarg; + struct fuse_cred cred; + fill_cred(in, &cred); res = -ENOENT; - path = get_path(in->ino); + path = get_path(f, in->ino); if(path != NULL) { res = 0; if(!res && (valid & FATTR_MODE)) { res = -ENOSYS; if(f->op.chmod) - res = f->op.chmod(path, attr->mode); + res = f->op.chmod(&cred, path, attr->mode); } if(!res && (valid & (FATTR_UID | FATTR_GID))) { uid_t uid = (valid & FATTR_UID) ? attr->uid : (uid_t) -1; @@ -316,12 +365,18 @@ static void do_setattr(struct fuse *f, struct fuse_in_header *in, res = -ENOSYS; if(f->op.chown) - res = f->op.chown(path, uid, gid); + res = f->op.chown(&cred, path, uid, gid); } if(!res && (valid & FATTR_SIZE)) { res = -ENOSYS; - if(f->op.truncate) - res = f->op.truncate(path, attr->size); + if(f->op.truncate && f->op.getattr) { + res = f->op.truncate(&cred, path, attr->size); + if(!res) { + struct stat buf; + res = f->op.getattr(&cred, path, &buf); + outarg.newsize = buf.st_size; + } + } } if(!res && (valid & FATTR_UTIME)) { struct utimbuf buf; @@ -329,11 +384,11 @@ static void do_setattr(struct fuse *f, struct fuse_in_header *in, buf.modtime = attr->mtime; res = -ENOSYS; if(f->op.utime) - res = f->op.utime(path, &buf); + res = f->op.utime(&cred, path, &buf); } g_free(path); } - send_reply(f, in, res, NULL, 0); + send_reply(f, in, res, &outarg, sizeof(outarg)); } static void do_readlink(struct fuse *f, struct fuse_in_header *in) @@ -341,13 +396,15 @@ static void do_readlink(struct fuse *f, struct fuse_in_header *in) int res; char link[PATH_MAX + 1]; char *path; + struct fuse_cred cred; + fill_cred(in, &cred); res = -ENOENT; - path = get_path(in->ino); + path = get_path(f, in->ino); if(path != NULL) { res = -ENOSYS; if(f->op.readlink) - res = f->op.readlink(path, link, sizeof(link)); + res = f->op.readlink(&cred, path, link, sizeof(link)); g_free(path); } send_reply(f, in, res, link, !res ? strlen(link) : 0); @@ -357,19 +414,20 @@ static void do_getdir(struct fuse *f, struct fuse_in_header *in) { int res; struct fuse_getdir_out arg; - struct fuse_dh dh; + struct fuse_dirhandle dh; char *path; + struct fuse_cred cred; + fill_cred(in, &cred); dh.fuse = f; dh.fp = tmpfile(); dh.dir = in->ino; - res = -ENOENT; - path = get_path(in->ino); + path = get_path(f, in->ino); if(path != NULL) { res = -ENOSYS; if(f->op.getdir) - res = f->op.getdir(path, &dh, (dirfiller_t) fill_dir); + res = f->op.getdir(&cred, path, &dh, (fuse_dirfil_t) fill_dir); g_free(path); } fflush(dh.fp); @@ -385,21 +443,24 @@ static void do_mknod(struct fuse *f, struct fuse_in_header *in, char *path; struct fuse_mknod_out outarg; struct stat buf; + struct fuse_cred cred; + fill_cred(in, &cred); res = -ENOENT; - path = get_path_name(in->ino, inarg->name); + path = get_path_name(f, in->ino, inarg->name); if(path != NULL) { res = -ENOSYS; if(f->op.mknod && f->op.getattr) { - res = f->op.mknod(path, inarg->mode, inarg->rdev); + res = f->op.mknod(&cred, path, inarg->mode, inarg->rdev); if(res == 0) - res = f->op.getattr(path, &buf); + res = f->op.getattr(&cred, path, &buf); } g_free(path); } if(res == 0) { convert_stat(&buf, &outarg.attr); - outarg.ino = find_node(f, in->ino, inarg->name, outarg.attr.mode); + outarg.ino = find_node(f, in->ino, inarg->name, &outarg.attr, + in->unique); } send_reply(f, in, res, &outarg, sizeof(outarg)); @@ -410,13 +471,15 @@ static void do_mkdir(struct fuse *f, struct fuse_in_header *in, { int res; char *path; + struct fuse_cred cred; + fill_cred(in, &cred); res = -ENOENT; - path = get_path_name(in->ino, inarg->name); + path = get_path_name(f, in->ino, inarg->name); if(path != NULL) { res = -ENOSYS; if(f->op.mkdir) - res = f->op.mkdir(path, inarg->mode); + res = f->op.mkdir(&cred, path, inarg->mode); g_free(path); } send_reply(f, in, res, NULL, 0); @@ -426,18 +489,20 @@ static void do_remove(struct fuse *f, struct fuse_in_header *in, char *name) { int res; char *path; + struct fuse_cred cred; + fill_cred(in, &cred); res = -ENOENT; - path = get_path_name(in->ino, name); + path = get_path_name(f, in->ino, name); if(path != NULL) { res = -ENOSYS; if(in->opcode == FUSE_UNLINK) { if(f->op.unlink) - res = f->op.unlink(path); + res = f->op.unlink(&cred, path); } else { if(f->op.rmdir) - res = f->op.rmdir(path); + res = f->op.rmdir(&cred, path); } g_free(path); } @@ -451,13 +516,15 @@ static void do_symlink(struct fuse *f, struct fuse_in_header *in, char *name, { int res; char *path; + struct fuse_cred cred; + fill_cred(in, &cred); res = -ENOENT; - path = get_path_name(in->ino, name); + path = get_path_name(f, in->ino, name); if(path != NULL) { res = -ENOSYS; if(f->op.symlink) - res = f->op.symlink(link, path); + res = f->op.symlink(&cred, link, path); g_free(path); } send_reply(f, in, res, NULL, 0); @@ -473,15 +540,17 @@ static void do_rename(struct fuse *f, struct fuse_in_header *in, char *newname = inarg->names + strlen(oldname) + 1; char *oldpath; char *newpath; + struct fuse_cred cred; + fill_cred(in, &cred); res = -ENOENT; - oldpath = get_path_name(olddir, oldname); + oldpath = get_path_name(f, olddir, oldname); if(oldpath != NULL) { - newpath = get_path_name(newdir, newname); + newpath = get_path_name(f, newdir, newname); if(newpath != NULL) { res = -ENOSYS; if(f->op.rename) - res = f->op.rename(oldpath, newpath); + res = f->op.rename(&cred, oldpath, newpath); if(res == 0) rename_node(f, olddir, oldname, newdir, newname); g_free(newpath); @@ -497,15 +566,17 @@ static void do_link(struct fuse *f, struct fuse_in_header *in, int res; char *oldpath; char *newpath; + struct fuse_cred cred; + fill_cred(in, &cred); res = -ENOENT; - oldpath = get_path(in->ino); + oldpath = get_path(f, in->ino); if(oldpath != NULL) { - newpath = get_path_name(arg->newdir, arg->name); + newpath = get_path_name(f, arg->newdir, arg->name); if(newpath != NULL) { res = -ENOSYS; if(f->op.link) - res = f->op.link(oldpath, newpath); + res = f->op.link(&cred, oldpath, newpath); g_free(newpath); } g_free(oldpath); @@ -518,13 +589,15 @@ static void do_open(struct fuse *f, struct fuse_in_header *in, { int res; char *path; + struct fuse_cred cred; + fill_cred(in, &cred); res = -ENOENT; - path = get_path(in->ino); + path = get_path(f, in->ino); if(path != NULL) { res = -ENOSYS; if(f->op.open) - res = f->op.open(path, arg->flags); + res = f->op.open(&cred, path, arg->flags); g_free(path); } send_reply(f, in, res, NULL, 0); @@ -537,12 +610,14 @@ static void do_read(struct fuse *f, struct fuse_in_header *in, char *path; char *buf = g_malloc(arg->size); size_t size; + struct fuse_cred cred; - path = get_path(in->ino); + fill_cred(in, &cred); + path = get_path(f, in->ino); if(path != NULL) { res = -ENOSYS; - if(f->op.pread) - res = f->op.pread(path, buf, arg->size, arg->offset); + if(f->op.read) + res = f->op.read(&cred, path, buf, arg->size, arg->offset); g_free(path); } @@ -556,110 +631,183 @@ static void do_read(struct fuse *f, struct fuse_in_header *in, g_free(buf); } +static void do_write(struct fuse *f, struct fuse_in_header *in, + struct fuse_write_in *arg) +{ + int res; + char *path; + struct fuse_cred cred; + + fill_cred(in, &cred); + path = get_path(f, in->ino); + if(path != NULL) { + res = -ENOSYS; + if(f->op.write) + res = f->op.write(&cred, path, arg->buf, arg->size, arg->offset); + g_free(path); + } + + if(res > 0) { + if((size_t) res != arg->size) { + fprintf(stderr, "short write: %u (should be %u)\n", res, + arg->size); + res = -EIO; + } + else + res = 0; + } + + send_reply(f, in, res, NULL, 0); +} + +struct cmd { + struct fuse *f; + char *buf; + size_t buflen; +}; + +static void *do_command(void *data) +{ + struct cmd *cmd = (struct cmd *) data; + struct fuse_in_header *in = (struct fuse_in_header *) cmd->buf; + void *inarg = cmd->buf + sizeof(struct fuse_in_header); + size_t argsize; + struct fuse *f = cmd->f; + + printf("unique: %i, opcode: %i, ino: %li, insize: %i\n", in->unique, + in->opcode, in->ino, cmd->buflen); + fflush(stdout); + + argsize = cmd->buflen - sizeof(struct fuse_in_header); + + switch(in->opcode) { + case FUSE_LOOKUP: + do_lookup(f, in, (char *) inarg); + break; + + case FUSE_FORGET: + do_forget(f, in, (struct fuse_forget_in *) inarg); + break; + + case FUSE_GETATTR: + do_getattr(f, in); + break; + + case FUSE_SETATTR: + do_setattr(f, in, (struct fuse_setattr_in *) inarg); + 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; + + case FUSE_MKDIR: + do_mkdir(f, in, (struct fuse_mkdir_in *) inarg); + break; + + case FUSE_UNLINK: + case FUSE_RMDIR: + do_remove(f, in, (char *) inarg); + break; + + case FUSE_SYMLINK: + do_symlink(f, in, (char *) inarg, + ((char *) inarg) + strlen((char *) inarg) + 1); + break; + + case FUSE_RENAME: + do_rename(f, in, (struct fuse_rename_in *) inarg); + break; + + case FUSE_LINK: + do_link(f, in, (struct fuse_link_in *) inarg); + break; + + case FUSE_OPEN: + do_open(f, in, (struct fuse_open_in *) inarg); + break; + + case FUSE_READ: + do_read(f, in, (struct fuse_read_in *) inarg); + break; + + case FUSE_WRITE: + do_write(f, in, (struct fuse_write_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); + } + + g_free(cmd->buf); + g_free(cmd); + + return NULL; +} + 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; + pthread_attr_t attr; + pthread_t thrid; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); while(1) { + struct cmd *cmd; + res = read(f->fd, inbuf, sizeof(inbuf)); if(res == -1) { perror("reading fuse device"); continue; } - insize = res; - - if(insize < sizeof(struct fuse_in_header)) { + if((size_t) res < 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_SETATTR: - do_setattr(f, in, (struct fuse_setattr_in *) inarg); - 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; - - case FUSE_MKDIR: - do_mkdir(f, in, (struct fuse_mkdir_in *) inarg); - break; - - case FUSE_UNLINK: - case FUSE_RMDIR: - do_remove(f, in, (char *) inarg); - break; - - case FUSE_SYMLINK: - do_symlink(f, in, (char *) inarg, - ((char *) inarg) + strlen((char *) inarg) + 1); - break; - - case FUSE_RENAME: - do_rename(f, in, (struct fuse_rename_in *) inarg); - break; - - case FUSE_LINK: - do_link(f, in, (struct fuse_link_in *) inarg); - break; - - case FUSE_OPEN: - do_open(f, in, (struct fuse_open_in *) inarg); - break; - - case FUSE_READ: - do_read(f, in, (struct fuse_read_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); + cmd = g_new0(struct cmd, 1); + cmd->f = f; + cmd->buflen = res; + cmd->buf = g_malloc(cmd->buflen); + memcpy(cmd->buf, inbuf, cmd->buflen); + + if(f->flags & FUSE_MULTITHREAD) { + res = pthread_create(&thrid, &attr, do_command, cmd); + if(res != 0) { + fprintf(stderr, "Error creating thread: %s\n", + strerror(errno)); + exit(1); + } } + else + do_command(cmd); } } -struct fuse *fuse_new() +struct fuse *fuse_new(int flags) { struct fuse *f = g_new0(struct fuse, 1); + f->flags = flags; f->fd = -1; f->dir = NULL; f->nametab = g_hash_table_new((GHashFunc) name_hash, (GCompareFunc) name_compare); + pthread_mutex_init(&f->lock, NULL); return f; } @@ -671,8 +819,9 @@ void fuse_set_operations(struct fuse *f, const struct fuse_operations *op) void fuse_destroy(struct fuse *f) { - fuse_unmount(f); + close(f->fd); g_hash_table_foreach_remove(f->nametab, (GHRFunc) free_node, NULL); g_hash_table_destroy(f->nametab); + pthread_mutex_destroy(&f->lock); g_free(f); } |