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)
{
}
}
-
static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry)
{
struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
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;
}
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;
}
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,
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,
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)
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)
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;
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,
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;
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) {
file->private_data = cfile;
}
- return out.h.result;
+ return out.h.error;
}
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,
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 = {
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 = {
{
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,
};
/*
#include <string.h>
#include <unistd.h>
#include <errno.h>
+#include <assert.h>
static guint name_hash(const struct node *node)
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;
}
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);
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;
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;
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;
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)
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));
}
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));
}
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);
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));
}
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)
{
((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 */