x
diff --git a/fusepro.c b/fusepro.c
index cbe80aa..4f593e7 100644
--- a/fusepro.c
+++ b/fusepro.c
@@ -108,6 +108,28 @@
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 @@
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 @@
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 @@
FUSE_UNLINK,
FUSE_RMDIR,
FUSE_RENAME,
+ FUSE_LINK,
};
/* Conservative buffer size for the client */
@@ -79,10 +80,14 @@
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_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 @@
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 @@
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 @@
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 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 @@
}
}
-
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 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 @@
{
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 @@
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 <string.h>
#include <unistd.h>
#include <errno.h>
+#include <assert.h>
static guint name_hash(const struct node *node)
@@ -29,7 +30,7 @@
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 @@
return (((fino_t) node) - 0x8000000) >> 3;
}
+static struct node *lookup_node(struct fuse *f, fino_t parent,
+ const char *name)
+{
+ struct node tmp;
+
+ tmp.name = (char *) name;
+ tmp.parent = parent;
+
+ 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;
- struct node tmp;
-
- tmp.name = name;
- tmp.parent = parent;
- node = g_hash_table_lookup(f->nametab, &tmp);
+ node = lookup_node(f, parent, name);
if(node != NULL)
return get_ino(node);
@@ -109,6 +117,27 @@
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 @@
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 @@
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 @@
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 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_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 @@
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 @@
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 @@
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 @@
((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 */