From 19dff1ba9dfe6f474d22224267a7407c949d6803 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 30 Oct 2001 15:06:52 +0000 Subject: [PATCH] x --- fusepro.c | 24 +++++++ include/fuse.h | 4 +- include/linux/fuse.h | 11 +++- kernel/dev.c | 12 ++-- kernel/dir.c | 113 +++++++++++++++++++++----------- kernel/inode.c | 3 + lib/fuse.c | 149 +++++++++++++++++++++++++++++++------------ 7 files changed, 228 insertions(+), 88 deletions(-) diff --git a/fusepro.c b/fusepro.c index cbe80aa..4f593e7 100644 --- a/fusepro.c +++ b/fusepro.c @@ -108,6 +108,28 @@ static int pro_rmdir(const char *path) return 0; } +static int pro_rename(const char *from, const char *to) +{ + int res; + + res = rename(from, to); + if(res == -1) + return -errno; + + return 0; +} + +static int pro_link(const char *from, const char *to) +{ + int res; + + res = link(from, to); + if(res == -1) + return -errno; + + return 0; +} + static void exit_handler() { exit(0); @@ -152,6 +174,8 @@ static struct fuse_operations pro_oper = { symlink: pro_symlink, unlink: pro_unlink, rmdir: pro_rmdir, + rename: pro_rename, + link: pro_link, }; int main(int argc, char *argv[]) diff --git a/include/fuse.h b/include/fuse.h index 6837b9e..e18f606 100644 --- a/include/fuse.h +++ b/include/fuse.h @@ -23,9 +23,11 @@ struct fuse_operations { int (*getdir) (const char *path, struct fuse_dh *h, dirfiller_t filler); int (*mknod) (const char *path, int mode, int rdev); int (*mkdir) (const char *path, int mode); - int (*symlink) (const char *from, const char *to); int (*unlink) (const char *path); int (*rmdir) (const char *path); + int (*rename) (const char *from, const char *to); + int (*symlink) (const char *from, const char *to); + int (*link) (const char *from, const char *to); }; struct fuse *fuse_new(); diff --git a/include/linux/fuse.h b/include/linux/fuse.h index b519a8d..64a4af3 100644 --- a/include/linux/fuse.h +++ b/include/linux/fuse.h @@ -44,6 +44,7 @@ enum fuse_opcode { FUSE_UNLINK, FUSE_RMDIR, FUSE_RENAME, + FUSE_LINK, }; /* Conservative buffer size for the client */ @@ -79,10 +80,14 @@ struct fuse_mkdir_in { char name[1]; }; - struct fuse_rename_in { unsigned long newdir; - char names[2]; + char names[1]; +}; + +struct fuse_link_in { + unsigned long newdir; + char name[1]; }; struct fuse_in_header { @@ -93,7 +98,7 @@ struct fuse_in_header { struct fuse_out_header { int unique; - int result; + int error; }; struct fuse_dirent { diff --git a/kernel/dev.c b/kernel/dev.c index a91a176..d32589d 100644 --- a/kernel/dev.c +++ b/kernel/dev.c @@ -53,14 +53,14 @@ static int request_check(struct fuse_req *req, struct fuse_out *outp) oh = (struct fuse_out_header *) req->out; size = req->outsize - OHSIZE; - if (oh->result <= -512 || oh->result > 0) { - printk("fuse: bad result\n"); + if (oh->error <= -512 || oh->error > 0) { + printk("fuse: bad error value: %i\n", oh->error); return -EPROTO; } if(size > outp->argsize || - (oh->result == 0 && !outp->argvar && size != outp->argsize) || - (oh->result != 0 && size != 0)) { + (oh->error == 0 && !outp->argvar && size != outp->argsize) || + (oh->error != 0 && size != 0)) { printk("fuse: invalid argument length: %i (%i)\n", size, req->opcode); return -EPROTO; @@ -71,7 +71,7 @@ static int request_check(struct fuse_req *req, struct fuse_out *outp) if(size) memcpy(outp->arg, req->out + OHSIZE, size); - return oh->result; + return oh->error; } static void request_free(struct fuse_req *req) @@ -158,7 +158,7 @@ void request_send(struct fuse_conn *fc, struct fuse_in *inp, spin_unlock(&fuse_lock); out: if(outp) - outp->h.result = ret; + outp->h.error = ret; } static int request_wait(struct fuse_conn *fc) diff --git a/kernel/dir.c b/kernel/dir.c index 065ac01..b23def7 100644 --- a/kernel/dir.c +++ b/kernel/dir.c @@ -22,6 +22,7 @@ static struct inode_operations fuse_special_inode_operations; static struct file_operations fuse_dir_operations; static struct file_operations fuse_file_operations; +static struct dentry_operations fuse_dentry_opertations; static void change_attributes(struct inode *inode, struct fuse_attr *attr) { @@ -57,7 +58,6 @@ void fuse_init_inode(struct inode *inode, struct fuse_attr *attr) } } - static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry) { struct fuse_conn *fc = dir->i_sb->u.generic_sbp; @@ -74,19 +74,18 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry) out.arg = &arg; request_send(fc, &in, &out); - if(out.h.result) { - /* Negative dentries are not hashed */ - if(out.h.result == -ENOENT) - return NULL; - else - return ERR_PTR(out.h.result); + inode = NULL; + if(!out.h.error) { + inode = iget(dir->i_sb, arg.ino); + if(!inode) + return ERR_PTR(-ENOMEM); + + fuse_init_inode(inode, &arg.attr); } + else if(out.h.error != -ENOENT) + return ERR_PTR(out.h.error); - inode = iget(dir->i_sb, arg.ino); - if(!inode) - return ERR_PTR(-ENOMEM); - - fuse_init_inode(inode, &arg.attr); + entry->d_op = &fuse_dentry_opertations; d_add(entry, inode); return NULL; } @@ -121,15 +120,15 @@ static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode, request_send(fc, &in, &out); kfree(inarg); - if(out.h.result) - return out.h.result; + if(out.h.error) + return out.h.error; inode = iget(dir->i_sb, outarg.ino); if(!inode) return -ENOMEM; fuse_init_inode(inode, &outarg.attr); - d_add(entry, inode); + d_instantiate(entry, inode); return 0; } @@ -163,7 +162,7 @@ static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode) request_send(fc, &in, &out); kfree(inarg); - return out.h.result; + return out.h.error; } static int fuse_symlink(struct inode *dir, struct dentry *entry, @@ -190,7 +189,7 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry, request_send(fc, &in, &out); kfree(inarg); - return out.h.result; + return out.h.error; } static int fuse_remove(struct inode *dir, struct dentry *entry, @@ -205,12 +204,7 @@ static int fuse_remove(struct inode *dir, struct dentry *entry, in.argsize = entry->d_name.len + 1; in.arg = entry->d_name.name; request_send(fc, &in, &out); - if(!out.h.result) { - d_drop(entry); - entry->d_inode->i_nlink = 0; - } - - return out.h.result; + return out.h.error; } static int fuse_unlink(struct inode *dir, struct dentry *entry) @@ -251,7 +245,35 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent, request_send(fc, &in, &out); kfree(inarg); - return out.h.result; + return out.h.error; +} + +static int fuse_link(struct dentry *entry, struct inode *newdir, + struct dentry *newent) +{ + struct inode *inode = entry->d_inode; + struct fuse_conn *fc = inode->i_sb->u.generic_sbp; + struct fuse_in in = FUSE_IN_INIT; + struct fuse_out out = FUSE_OUT_INIT; + struct fuse_link_in *inarg; + unsigned int insize; + + insize = offsetof(struct fuse_link_in, name) + newent->d_name.len + 1; + inarg = kmalloc(insize, GFP_KERNEL); + if(!inarg) + return -ENOMEM; + + inarg->newdir = newdir->i_ino; + strcpy(inarg->name, newent->d_name.name); + + in.h.opcode = FUSE_LINK; + in.h.ino = inode->i_ino; + in.argsize = insize; + in.arg = inarg; + request_send(fc, &in, &out); + kfree(inarg); + + return out.h.error; } static int fuse_permission(struct inode *inode, int mask) @@ -259,6 +281,8 @@ static int fuse_permission(struct inode *inode, int mask) return 0; } +/* Only revalidate the root inode, since lookup is always redone on + the last path segment, and lookup refreshes the attributes */ static int fuse_revalidate(struct dentry *dentry) { struct inode *inode = dentry->d_inode; @@ -267,18 +291,19 @@ static int fuse_revalidate(struct dentry *dentry) struct fuse_out out = FUSE_OUT_INIT; struct fuse_getattr_out arg; + if(inode->i_ino != FUSE_ROOT_INO) + return 0; + in.h.opcode = FUSE_GETATTR; in.h.ino = inode->i_ino; out.argsize = sizeof(arg); out.arg = &arg; request_send(fc, &in, &out); - if(out.h.result == 0) + if(!out.h.error) change_attributes(inode, &arg.attr); - else - d_drop(dentry); - - return out.h.result; + + return out.h.error; } static int parse_dirfile(char *buf, size_t nbytes, struct file *file, @@ -347,9 +372,9 @@ static int read_link(struct dentry *dentry, char **bufp) out.argsize = PAGE_SIZE - 1; out.argvar = 1; request_send(fc, &in, &out); - if(out.h.result) { + if(out.h.error) { free_page(page); - return out.h.result; + return out.h.error; } *bufp = (char *) page; @@ -405,7 +430,7 @@ static int fuse_dir_open(struct inode *inode, struct file *file) out.argsize = sizeof(outarg); out.arg = &outarg; request_send(fc, &in, &out); - if(out.h.result == 0) { + if(!out.h.error) { struct file *cfile = outarg.file; struct inode *inode; if(!cfile) { @@ -422,7 +447,7 @@ static int fuse_dir_open(struct inode *inode, struct file *file) file->private_data = cfile; } - return out.h.result; + return out.h.error; } static int fuse_dir_release(struct inode *inode, struct file *file) @@ -437,6 +462,14 @@ static int fuse_dir_release(struct inode *inode, struct file *file) return 0; } +static int fuse_dentry_revalidate(struct dentry *entry, int flags) +{ + if(!entry->d_inode || !(flags & LOOKUP_CONTINUE)) + return 0; + else + return 1; +} + static struct inode_operations fuse_dir_inode_operations = { lookup: fuse_lookup, @@ -447,12 +480,12 @@ static struct inode_operations fuse_dir_inode_operations = unlink: fuse_unlink, rmdir: fuse_rmdir, rename: fuse_rename, -#if 0 link: fuse_link, +#if 0 setattr: fuse_setattr, #endif permission: fuse_permission, - revalidate: fuse_revalidate, + revalidate: fuse_revalidate, }; static struct file_operations fuse_dir_operations = { @@ -464,12 +497,12 @@ static struct file_operations fuse_dir_operations = { static struct inode_operations fuse_file_inode_operations = { permission: fuse_permission, - revalidate: fuse_revalidate, + revalidate: fuse_revalidate, }; static struct inode_operations fuse_special_inode_operations = { permission: fuse_permission, - revalidate: fuse_revalidate, + revalidate: fuse_revalidate, }; static struct file_operations fuse_file_operations = { @@ -479,7 +512,11 @@ static struct inode_operations fuse_symlink_inode_operations = { readlink: fuse_readlink, follow_link: fuse_follow_link, - revalidate: fuse_revalidate, + revalidate: fuse_revalidate, +}; + +static struct dentry_operations fuse_dentry_opertations = { + d_revalidate: fuse_dentry_revalidate, }; /* diff --git a/kernel/inode.c b/kernel/inode.c index b59a146..702ef57 100644 --- a/kernel/inode.c +++ b/kernel/inode.c @@ -182,6 +182,9 @@ static struct super_block *fuse_read_super(struct super_block *sb, sb->u.generic_sbp = fc; sb->s_root = d_alloc_root(root); + if(!sb->s_root) + goto err; + fc->sb = sb; spin_unlock(&fuse_lock); diff --git a/lib/fuse.c b/lib/fuse.c index 62695f0..f033e04 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -12,6 +12,7 @@ #include #include #include +#include static guint name_hash(const struct node *node) @@ -29,7 +30,7 @@ static gint name_compare(const struct node *node1, const struct node *node2) static struct node *new_node(fino_t parent, const char *name) { struct node *node = g_new0(struct node, 1); - node->name = strdup(name); + node->name = g_strdup(name); node->parent = parent; return node; } @@ -51,15 +52,22 @@ static inline fino_t get_ino(struct node *node) return (((fino_t) node) - 0x8000000) >> 3; } -static fino_t find_node(struct fuse *f, fino_t parent, char *name, int create) +static struct node *lookup_node(struct fuse *f, fino_t parent, + const char *name) { - struct node *node; struct node tmp; - - tmp.name = name; + + tmp.name = (char *) name; tmp.parent = parent; - node = g_hash_table_lookup(f->nametab, &tmp); + return g_hash_table_lookup(f->nametab, &tmp); +} + +static fino_t find_node(struct fuse *f, fino_t parent, char *name, int create) +{ + struct node *node; + + node = lookup_node(f, parent, name); if(node != NULL) return get_ino(node); @@ -109,6 +117,27 @@ static void remove_node(struct fuse *f, fino_t ino) free_node(node); } +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); + + assert(node != NULL); + + /* The overwritten node is left to dangle until deleted */ + if(newnode != NULL) + g_hash_table_remove(f->nametab, newnode); + + /* The renamed node is not freed, since it's pointer is the key */ + 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); +} + + static void convert_stat(struct stat *stbuf, struct fuse_attr *attr) { attr->mode = stbuf->st_mode; @@ -124,23 +153,6 @@ static void convert_stat(struct stat *stbuf, struct fuse_attr *attr) 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; - - path = get_path(ino); - res = -ENOSYS; - if(f->op.getattr) - res = f->op.getattr(path, &buf); - g_free(path); - if(res == 0) - convert_stat(&buf, attr); - - return res; -} - static int fill_dir(struct fuse_dh *dh, char *name, int type) { struct fuse_dirent dirent; @@ -160,7 +172,7 @@ static int fill_dir(struct fuse_dh *dh, char *name, int type) return 0; } -static void send_reply(struct fuse *f, struct fuse_in_header *in, int result, +static void send_reply(struct fuse *f, struct fuse_in_header *in, int error, void *arg, size_t argsize) { int res; @@ -168,25 +180,24 @@ static void send_reply(struct fuse *f, struct fuse_in_header *in, int result, 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(error > 0) { + fprintf(stderr, "positive error code: %i\n", error); + error = -ERANGE; } - if(result != 0) + if(error) 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; + out->error = error; 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); + printf(" unique: %i, error: %i (%s), outsize: %i\n", out->unique, + out->error, strerror(-out->error), outsize); res = write(f->fd, outbuf, outsize); if(res == -1) @@ -198,11 +209,19 @@ static void send_reply(struct fuse *f, struct fuse_in_header *in, int result, 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; - arg.ino = find_node(f, in->ino, name, 1); - res = get_attributes(f, arg.ino, &arg.attr); - + path = get_path_name(in->ino, name); + res = -ENOSYS; + if(f->op.getattr) + res = f->op.getattr(path, &buf); + g_free(path); + if(res == 0) { + convert_stat(&buf, &arg.attr); + arg.ino = find_node(f, in->ino, name, 1); + } send_reply(f, in, res, &arg, sizeof(arg)); } @@ -217,9 +236,18 @@ static void do_forget(struct fuse *f, unsigned long *inos, size_t num) static void do_getattr(struct fuse *f, struct fuse_in_header *in) { int res; + char *path; + struct stat buf; struct fuse_getattr_out arg; - res = get_attributes(f, in->ino, &arg.attr); + path = get_path(in->ino); + res = -ENOSYS; + if(f->op.getattr) + res = f->op.getattr(path, &buf); + g_free(path); + if(res == 0) + convert_stat(&buf, &arg.attr); + send_reply(f, in, res, &arg, sizeof(arg)); } @@ -269,8 +297,7 @@ static void do_mknod(struct fuse *f, struct fuse_in_header *in, struct fuse_mknod_out outarg; struct stat buf; - outarg.ino = find_node(f, in->ino, inarg->name, 1); - path = get_path(outarg.ino); + path = get_path_name(in->ino, inarg->name); res = -ENOSYS; if(f->op.mknod && f->op.getattr) { res = f->op.mknod(path, inarg->mode, inarg->rdev); @@ -278,9 +305,10 @@ static void do_mknod(struct fuse *f, struct fuse_in_header *in, res = f->op.getattr(path, &buf); } g_free(path); - - if(res == 0) + if(res == 0) { convert_stat(&buf, &outarg.attr); + outarg.ino = find_node(f, in->ino, inarg->name, 1); + } send_reply(f, in, res, &outarg, sizeof(outarg)); } @@ -332,6 +360,39 @@ static void do_symlink(struct fuse *f, struct fuse_in_header *in, char *name, send_reply(f, in, res, NULL, 0); } +static void do_rename(struct fuse *f, struct fuse_in_header *in, + struct fuse_rename_in *inarg) +{ + int res; + fino_t olddir = in->ino; + fino_t newdir = inarg->newdir; + char *oldname = inarg->names; + char *newname = inarg->names + strlen(oldname) + 1; + char *oldpath = get_path_name(olddir, oldname); + char *newpath = get_path_name(newdir, newname); + + res = -ENOSYS; + if(f->op.rename) + res = f->op.rename(oldpath, newpath); + if(res == 0) + rename_node(f, olddir, oldname, newdir, newname); + send_reply(f, in, res, NULL, 0); +} + +static void do_link(struct fuse *f, struct fuse_in_header *in, + struct fuse_link_in *inarg) +{ + int res; + char *oldpath = get_path(in->ino); + char *newpath = get_path_name(inarg->newdir, inarg->name); + + res = -ENOSYS; + if(f->op.link) + res = f->op.link(oldpath, newpath); + + send_reply(f, in, res, NULL, 0); +} + void fuse_loop(struct fuse *f) { @@ -400,6 +461,14 @@ void fuse_loop(struct fuse *f) ((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; + default: fprintf(stderr, "Operation %i not implemented\n", in->opcode); /* No need to send reply to async requests */ -- 2.30.2