* statfs library API changed to match other methods. Since this
is not backward compatible FUSE_MAJOR_VERSION is changed to 2
- * statfs kernel interface changed to 64 bits, added 'bavail' field
+ * kernel interface changes follow:
+
+ * statfs changed to 64 bits, added 'bavail' field
+
+ * add generation number to lookup result
+
+ * optimized mknod/mkdir/symlink/link (no separate lookup is
+ needed)
+
+ * rdev size increased to 32 bits for mknod
+
+ * kernel interface version changed to 3.1
2004-02-18 Miklos Szeredi <mszeredi@inf.bme.hu>
struct fuse_lookup_out {
unsigned long ino;
+ unsigned long generation;
struct fuse_attr attr;
};
void *file; /* Used by kernel only */
};
-/* FIXME: 2.6 needs 32 bit rdev */
struct fuse_mknod_in {
- unsigned short mode;
- unsigned short rdev;
-};
-
-struct fuse_mknod_out {
- unsigned long ino;
- struct fuse_attr attr;
+ unsigned int mode;
+ unsigned int rdev;
};
struct fuse_mkdir_in {
- unsigned short mode;
+ unsigned int mode;
};
struct fuse_rename_in {
else if(S_ISLNK(inode->i_mode)) {
inode->i_op = &fuse_symlink_inode_operations;
}
- else {
+ else if(S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
+ S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)){
inode->i_op = &fuse_file_inode_operations;
init_special_inode(inode, inode->i_mode,
new_decode_dev(attr->rdev));
- }
+ } else
+ printk("fuse_init_inode: bad file type: %o\n", inode->i_mode);
+
inode->u.generic_ip = inode;
}
-struct inode *fuse_iget(struct super_block *sb, ino_t ino,
+struct inode *fuse_iget(struct super_block *sb, ino_t ino, int generation,
struct fuse_attr *attr, int version)
{
struct inode *inode;
inode = iget(sb, ino);
if(inode) {
- if(!inode->u.generic_ip)
+ if(!inode->u.generic_ip) {
+ inode->i_generation = generation;
fuse_init_inode(inode, attr);
+ } else if(inode->i_generation != generation)
+ printk("fuse_iget: bad generation for ino %lu\n", ino);
change_attributes(inode, attr);
inode->i_version = version;
err = fuse_do_lookup(dir, entry, &outarg, &version);
if(!err) {
- inode = fuse_iget(dir->i_sb, outarg.ino, &outarg.attr, version);
+ inode = fuse_iget(dir->i_sb, outarg.ino, outarg.generation,
+ &outarg.attr, version);
if(!inode)
return -ENOMEM;
} else if(err != -ENOENT)
return 0;
}
-/* create needs to return a positive entry, so this is actually an
- mknod+lookup */
+static int lookup_new_entry(struct inode *dir, struct dentry *entry,
+ struct fuse_lookup_out *outarg, int version,
+ int mode)
+{
+ struct inode *inode;
+ inode = fuse_iget(dir->i_sb, outarg->ino, outarg->generation,
+ &outarg->attr, version);
+ if(!inode)
+ return -ENOMEM;
+
+ /* Don't allow userspace to do really stupid things... */
+ if((inode->i_mode ^ mode) & S_IFMT) {
+ iput(inode);
+ printk("fuse_mknod: inode has wrong type\n");
+ return -EINVAL;
+ }
+
+ d_instantiate(entry, inode);
+ return 0;
+}
+
+
static int _fuse_mknod(struct inode *dir, struct dentry *entry, int mode,
dev_t rdev)
{
struct fuse_in in = FUSE_IN_INIT;
struct fuse_out out = FUSE_OUT_INIT;
struct fuse_mknod_in inarg;
- struct fuse_mknod_out outarg;
- struct inode *inode;
+ struct fuse_lookup_out outarg;
memset(&inarg, 0, sizeof(inarg));
inarg.mode = mode;
if(out.h.error)
return out.h.error;
- inode = fuse_iget(dir->i_sb, outarg.ino, &outarg.attr, out.h.unique);
- if(!inode)
- return -ENOMEM;
-
- /* Don't allow userspace to do really stupid things... */
- if((inode->i_mode ^ mode) & S_IFMT) {
- iput(inode);
- printk("fuse_mknod: inode has wrong type\n");
- return -EPROTO;
- }
-
- d_instantiate(entry, inode);
- return 0;
+ return lookup_new_entry(dir, entry, &outarg, out.h.unique, mode);
}
static int _fuse_create(struct inode *dir, struct dentry *entry, int mode)
return _fuse_mknod(dir, entry, mode, 0);
}
-/* knfsd needs the new entry instantiated in mkdir/symlink/link. this
- should rather be done like mknod: attributes returned in out arg to
- save a call to userspace */
-static int lookup_new_entry(struct inode *dir, struct dentry *entry)
-{
- struct inode *inode;
- int err = fuse_lookup_iget(dir, entry, &inode);
- if(err || !inode) {
- printk("fuse_mkdir: failed to look up new entry\n");
- return err ? err : -ENOENT;
- }
- d_instantiate(entry, inode);
- return 0;
-}
static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode)
{
struct fuse_in in = FUSE_IN_INIT;
struct fuse_out out = FUSE_OUT_INIT;
struct fuse_mkdir_in inarg;
+ struct fuse_lookup_out outarg;
memset(&inarg, 0, sizeof(inarg));
inarg.mode = mode;
in.args[0].value = &inarg;
in.args[1].size = entry->d_name.len + 1;
in.args[1].value = entry->d_name.name;
+ out.numargs = 1;
+ out.args[0].size = sizeof(outarg);
+ out.args[0].value = &outarg;
request_send(fc, &in, &out);
if(out.h.error)
return out.h.error;
- return lookup_new_entry(dir, entry);
+ return lookup_new_entry(dir, entry, &outarg, out.h.unique, S_IFDIR);
}
static int fuse_symlink(struct inode *dir, struct dentry *entry,
struct fuse_conn *fc = INO_FC(dir);
struct fuse_in in = FUSE_IN_INIT;
struct fuse_out out = FUSE_OUT_INIT;
+ struct fuse_lookup_out outarg;
in.h.opcode = FUSE_SYMLINK;
in.h.ino = dir->i_ino;
in.args[0].value = entry->d_name.name;
in.args[1].size = strlen(link) + 1;
in.args[1].value = link;
+ out.numargs = 1;
+ out.args[0].size = sizeof(outarg);
+ out.args[0].value = &outarg;
request_send(fc, &in, &out);
if(out.h.error)
return out.h.error;
- return lookup_new_entry(dir, entry);
+ return lookup_new_entry(dir, entry, &outarg, out.h.unique, S_IFLNK);
}
static int fuse_remove(struct inode *dir, struct dentry *entry,
struct fuse_in in = FUSE_IN_INIT;
struct fuse_out out = FUSE_OUT_INIT;
struct fuse_link_in inarg;
+ struct fuse_lookup_out outarg;
memset(&inarg, 0, sizeof(inarg));
inarg.newdir = newdir->i_ino;
in.args[0].value = &inarg;
in.args[1].size = newent->d_name.len + 1;
in.args[1].value = newent->d_name.name;
+ out.numargs = 1;
+ out.args[0].size = sizeof(outarg);
+ out.args[0].value = &outarg;
request_send(fc, &in, &out);
if(out.h.error)
return out.h.error;
/* Invalidate old entry, so attributes are refreshed */
d_invalidate(entry);
- return lookup_new_entry(newdir, newent);
+ return lookup_new_entry(newdir, newent, &outarg, out.h.unique,
+ inode->i_mode);
}
int fuse_do_getattr(struct inode *inode)
/**
* Get a filled in inode
*/
-struct inode *fuse_iget(struct super_block *sb, ino_t ino,
+struct inode *fuse_iget(struct super_block *sb, ino_t ino, int generation,
struct fuse_attr *attr, int version);
memset(&attr, 0, sizeof(attr));
attr.mode = mode;
- return fuse_iget(sb, 1, &attr, 0);
+ return fuse_iget(sb, 1, 0, &attr, 0);
}
{
__u32 *objp = vobjp;
unsigned long ino = objp[0];
- /* __u32 generation = objp[1]; */
+ __u32 generation = objp[1];
struct inode *inode;
struct dentry *entry;
return ERR_PTR(-ESTALE);
inode = ilookup(sb, ino);
- if(!inode)
+ if(!inode || inode->i_generation != generation)
return ERR_PTR(-ESTALE);
entry = d_alloc_anon(inode);
abort();
}
-static void hash_ino(struct fuse *f, struct node *node, fino_t ino)
+static void hash_ino(struct fuse *f, struct node *node)
{
- size_t hash = ino % f->ino_table_size;
- node->ino = ino;
-
+ size_t hash = node->ino % f->ino_table_size;
node->ino_next = f->ino_table[hash];
f->ino_table[hash] = node;
}
}
}
-static fino_t get_ino(struct node *node)
-{
- return node->ino;
-}
-
static fino_t next_ino(struct fuse *f)
{
- do f->ctr++;
- while(f->ctr == 0 || __get_node(f, f->ctr) != NULL);
-
+ do {
+ f->ctr++;
+ if(!f->ctr)
+ f->generation ++;
+ } while(f->ctr == 0 || __get_node(f, f->ctr) != NULL);
return f->ctr;
}
}
}
-static fino_t find_node(struct fuse *f, fino_t parent, char *name,
- struct fuse_attr *attr, int version)
+static struct node *find_node(struct fuse *f, fino_t parent, char *name,
+ struct fuse_attr *attr, int version)
{
struct node *node;
int mode = attr->mode & S_IFMT;
node = (struct node *) calloc(1, sizeof(struct node));
node->mode = mode;
node->rdev = rdev;
- hash_ino(f, node, next_ino(f));
+ node->ino = next_ino(f);
+ node->generation = f->generation;
+ hash_ino(f, node);
hash_name(f, node, parent, name);
out:
node->version = version;
pthread_mutex_unlock(&f->lock);
- return get_ino(node);
+ return node;
}
static char *add_name(char *buf, char *s, const char *name)
return res;
}
+static int lookup_path(struct fuse *f, fino_t ino, int version, char *name,
+ const char *path, struct fuse_lookup_out *arg)
+{
+ int res;
+ struct stat buf;
+
+ res = f->op.getattr(path, &buf);
+ if(res == 0) {
+ struct node *node;
+
+ memset(arg, 0, sizeof(struct fuse_lookup_out));
+ convert_stat(&buf, &arg->attr);
+ node = find_node(f, ino, name, &arg->attr, version);
+ arg->ino = node->ino;
+ arg->generation = node->generation;
+ if(f->flags & FUSE_DEBUG) {
+ printf(" INO: %li\n", arg->ino);
+ fflush(stdout);
+ }
+ }
+ return res;
+}
+
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;
res = -ENOENT;
}
res = -ENOSYS;
if(f->op.getattr)
- res = f->op.getattr(path, &buf);
+ res = lookup_path(f, in->ino, in->unique, name, path, &arg);
free(path);
}
-
- if(res == 0) {
- memset(&arg, 0, sizeof(struct fuse_lookup_out));
- convert_stat(&buf, &arg.attr);
- arg.ino = find_node(f, in->ino, name, &arg.attr, in->unique);
- if(f->flags & FUSE_DEBUG) {
- printf(" LOOKUP: %li\n", arg.ino);
- fflush(stdout);
- }
- }
send_reply(f, in, res, &arg, sizeof(arg));
}
{
int res;
char *path;
- struct fuse_mknod_out outarg;
- struct stat buf;
+ char *name = PARAM(inarg);
+ struct fuse_lookup_out outarg;
res = -ENOENT;
- path = get_path_name(f, in->ino, PARAM(inarg));
+ path = get_path_name(f, in->ino, name);
if(path != NULL) {
+ if(f->flags & FUSE_DEBUG) {
+ printf("MKNOD %s\n", path);
+ fflush(stdout);
+ }
res = -ENOSYS;
if(f->op.mknod && f->op.getattr) {
res = f->op.mknod(path, inarg->mode, inarg->rdev);
if(res == 0)
- res = f->op.getattr(path, &buf);
+ res = lookup_path(f, in->ino, in->unique, name, path, &outarg);
}
free(path);
}
- if(res == 0) {
- memset(&outarg, 0, sizeof(struct fuse_mknod_out));
- convert_stat(&buf, &outarg.attr);
- outarg.ino = find_node(f, in->ino, PARAM(inarg), &outarg.attr,
- in->unique);
- }
-
send_reply(f, in, res, &outarg, sizeof(outarg));
}
{
int res;
char *path;
+ char *name = PARAM(inarg);
+ struct fuse_lookup_out outarg;
res = -ENOENT;
- path = get_path_name(f, in->ino, PARAM(inarg));
+ path = get_path_name(f, in->ino, name);
if(path != NULL) {
+ if(f->flags & FUSE_DEBUG) {
+ printf("MKDIR %s\n", path);
+ fflush(stdout);
+ }
res = -ENOSYS;
- if(f->op.mkdir)
+ if(f->op.mkdir && f->op.getattr) {
res = f->op.mkdir(path, inarg->mode);
+ if(res == 0)
+ res = lookup_path(f, in->ino, in->unique, name, path, &outarg);
+ }
free(path);
}
- send_reply(f, in, res, NULL, 0);
+ send_reply(f, in, res, &outarg, sizeof(outarg));
}
static void do_remove(struct fuse *f, struct fuse_in_header *in, char *name)
{
int res;
char *path;
+ struct fuse_lookup_out outarg;
res = -ENOENT;
path = get_path_name(f, in->ino, name);
if(path != NULL) {
+ if(f->flags & FUSE_DEBUG) {
+ printf("SYMLINK %s\n", path);
+ fflush(stdout);
+ }
res = -ENOSYS;
- if(f->op.symlink)
+ if(f->op.symlink && f->op.getattr) {
res = f->op.symlink(link, path);
+ if(res == 0)
+ res = lookup_path(f, in->ino, in->unique, name, path, &outarg);
+ }
free(path);
}
- send_reply(f, in, res, NULL, 0);
+ send_reply(f, in, res, &outarg, sizeof(outarg));
}
static void do_rename(struct fuse *f, struct fuse_in_header *in,
int res;
char *oldpath;
char *newpath;
+ char *name = PARAM(arg);
+ struct fuse_lookup_out outarg;
res = -ENOENT;
oldpath = get_path(f, in->ino);
if(oldpath != NULL) {
- newpath = get_path_name(f, arg->newdir, PARAM(arg));
+ newpath = get_path_name(f, arg->newdir, name);
if(newpath != NULL) {
+ if(f->flags & FUSE_DEBUG) {
+ printf("LINK %s\n", newpath);
+ fflush(stdout);
+ }
res = -ENOSYS;
- if(f->op.link)
+ if(f->op.link && f->op.getattr) {
res = f->op.link(oldpath, newpath);
+ if(res == 0)
+ res = lookup_path(f, arg->newdir, in->unique, name,
+ newpath, &outarg);
+ }
free(newpath);
}
free(oldpath);
}
- send_reply(f, in, res, NULL, 0);
+ send_reply(f, in, res, &outarg, sizeof(outarg));
}
static void do_open(struct fuse *f, struct fuse_in_header *in,
f->flags = flags;
f->fd = fd;
f->ctr = 0;
+ f->generation = 0;
/* FIXME: Dynamic hash table */
f->name_table_size = 14057;
f->name_table = (struct node **)
root->rdev = 0;
root->name = strdup("/");
root->parent = 0;
- hash_ino(f, root, FUSE_ROOT_INO);
+ root->ino = FUSE_ROOT_INO;
+ root->generation = 0;
+ hash_ino(f, root);
return f;
}
struct node *name_next;
struct node *ino_next;
fino_t ino;
+ unsigned int generation;
fino_t parent;
char *name;
int mode;
struct node **ino_table;
size_t ino_table_size;
fino_t ctr;
+ unsigned int generation;
pthread_mutex_t lock;
int numworker;
int numavail;