[PATCH] FUSE - read-write operations
This patch adds the write filesystem operations of FUSE. The following operations are added: o setattr o symlink o mknod o mkdir o create o unlink o rmdir o rename o link Signed-off-by: Miklos Szeredi <miklos@szeredi.hu> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
e5e5558e92
commit
9e6268db49
373
fs/fuse/dir.c
373
fs/fuse/dir.c
@ -42,7 +42,6 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
|
|||||||
return 0;
|
return 0;
|
||||||
else if (time_after(jiffies, entry->d_time)) {
|
else if (time_after(jiffies, entry->d_time)) {
|
||||||
int err;
|
int err;
|
||||||
int version;
|
|
||||||
struct fuse_entry_out outarg;
|
struct fuse_entry_out outarg;
|
||||||
struct inode *inode = entry->d_inode;
|
struct inode *inode = entry->d_inode;
|
||||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||||
@ -53,15 +52,19 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
|
|||||||
|
|
||||||
fuse_lookup_init(req, entry->d_parent->d_inode, entry, &outarg);
|
fuse_lookup_init(req, entry->d_parent->d_inode, entry, &outarg);
|
||||||
request_send_nonint(fc, req);
|
request_send_nonint(fc, req);
|
||||||
version = req->out.h.unique;
|
|
||||||
err = req->out.h.error;
|
err = req->out.h.error;
|
||||||
|
if (!err) {
|
||||||
|
if (outarg.nodeid != get_node_id(inode)) {
|
||||||
|
fuse_send_forget(fc, req, outarg.nodeid, 1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
fi->nlookup ++;
|
||||||
|
}
|
||||||
fuse_put_request(fc, req);
|
fuse_put_request(fc, req);
|
||||||
if (err || outarg.nodeid != get_node_id(inode) ||
|
if (err || (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
|
||||||
(outarg.attr.mode ^ inode->i_mode) & S_IFMT)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fuse_change_attributes(inode, &outarg.attr);
|
fuse_change_attributes(inode, &outarg.attr);
|
||||||
inode->i_version = version;
|
|
||||||
entry->d_time = time_to_jiffies(outarg.entry_valid,
|
entry->d_time = time_to_jiffies(outarg.entry_valid,
|
||||||
outarg.entry_valid_nsec);
|
outarg.entry_valid_nsec);
|
||||||
fi->i_time = time_to_jiffies(outarg.attr_valid,
|
fi->i_time = time_to_jiffies(outarg.attr_valid,
|
||||||
@ -78,7 +81,6 @@ static int fuse_lookup_iget(struct inode *dir, struct dentry *entry,
|
|||||||
struct inode **inodep)
|
struct inode **inodep)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
int version;
|
|
||||||
struct fuse_entry_out outarg;
|
struct fuse_entry_out outarg;
|
||||||
struct inode *inode = NULL;
|
struct inode *inode = NULL;
|
||||||
struct fuse_conn *fc = get_fuse_conn(dir);
|
struct fuse_conn *fc = get_fuse_conn(dir);
|
||||||
@ -93,13 +95,12 @@ static int fuse_lookup_iget(struct inode *dir, struct dentry *entry,
|
|||||||
|
|
||||||
fuse_lookup_init(req, dir, entry, &outarg);
|
fuse_lookup_init(req, dir, entry, &outarg);
|
||||||
request_send(fc, req);
|
request_send(fc, req);
|
||||||
version = req->out.h.unique;
|
|
||||||
err = req->out.h.error;
|
err = req->out.h.error;
|
||||||
if (!err) {
|
if (!err) {
|
||||||
inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
|
inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
|
||||||
&outarg.attr, version);
|
&outarg.attr);
|
||||||
if (!inode) {
|
if (!inode) {
|
||||||
fuse_send_forget(fc, req, outarg.nodeid, version);
|
fuse_send_forget(fc, req, outarg.nodeid, 1);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,6 +121,264 @@ static int fuse_lookup_iget(struct inode *dir, struct dentry *entry,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void fuse_invalidate_attr(struct inode *inode)
|
||||||
|
{
|
||||||
|
get_fuse_inode(inode)->i_time = jiffies - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fuse_invalidate_entry(struct dentry *entry)
|
||||||
|
{
|
||||||
|
d_invalidate(entry);
|
||||||
|
entry->d_time = jiffies - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req,
|
||||||
|
struct inode *dir, struct dentry *entry,
|
||||||
|
int mode)
|
||||||
|
{
|
||||||
|
struct fuse_entry_out outarg;
|
||||||
|
struct inode *inode;
|
||||||
|
struct fuse_inode *fi;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
req->in.h.nodeid = get_node_id(dir);
|
||||||
|
req->inode = dir;
|
||||||
|
req->out.numargs = 1;
|
||||||
|
req->out.args[0].size = sizeof(outarg);
|
||||||
|
req->out.args[0].value = &outarg;
|
||||||
|
request_send(fc, req);
|
||||||
|
err = req->out.h.error;
|
||||||
|
if (err) {
|
||||||
|
fuse_put_request(fc, req);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
|
||||||
|
&outarg.attr);
|
||||||
|
if (!inode) {
|
||||||
|
fuse_send_forget(fc, req, outarg.nodeid, 1);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
fuse_put_request(fc, req);
|
||||||
|
|
||||||
|
/* Don't allow userspace to do really stupid things... */
|
||||||
|
if ((inode->i_mode ^ mode) & S_IFMT) {
|
||||||
|
iput(inode);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->d_time = time_to_jiffies(outarg.entry_valid,
|
||||||
|
outarg.entry_valid_nsec);
|
||||||
|
|
||||||
|
fi = get_fuse_inode(inode);
|
||||||
|
fi->i_time = time_to_jiffies(outarg.attr_valid,
|
||||||
|
outarg.attr_valid_nsec);
|
||||||
|
|
||||||
|
d_instantiate(entry, inode);
|
||||||
|
fuse_invalidate_attr(dir);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode,
|
||||||
|
dev_t rdev)
|
||||||
|
{
|
||||||
|
struct fuse_mknod_in inarg;
|
||||||
|
struct fuse_conn *fc = get_fuse_conn(dir);
|
||||||
|
struct fuse_req *req = fuse_get_request(fc);
|
||||||
|
if (!req)
|
||||||
|
return -ERESTARTNOINTR;
|
||||||
|
|
||||||
|
memset(&inarg, 0, sizeof(inarg));
|
||||||
|
inarg.mode = mode;
|
||||||
|
inarg.rdev = new_encode_dev(rdev);
|
||||||
|
req->in.h.opcode = FUSE_MKNOD;
|
||||||
|
req->in.numargs = 2;
|
||||||
|
req->in.args[0].size = sizeof(inarg);
|
||||||
|
req->in.args[0].value = &inarg;
|
||||||
|
req->in.args[1].size = entry->d_name.len + 1;
|
||||||
|
req->in.args[1].value = entry->d_name.name;
|
||||||
|
return create_new_entry(fc, req, dir, entry, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fuse_create(struct inode *dir, struct dentry *entry, int mode,
|
||||||
|
struct nameidata *nd)
|
||||||
|
{
|
||||||
|
return fuse_mknod(dir, entry, mode, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode)
|
||||||
|
{
|
||||||
|
struct fuse_mkdir_in inarg;
|
||||||
|
struct fuse_conn *fc = get_fuse_conn(dir);
|
||||||
|
struct fuse_req *req = fuse_get_request(fc);
|
||||||
|
if (!req)
|
||||||
|
return -ERESTARTNOINTR;
|
||||||
|
|
||||||
|
memset(&inarg, 0, sizeof(inarg));
|
||||||
|
inarg.mode = mode;
|
||||||
|
req->in.h.opcode = FUSE_MKDIR;
|
||||||
|
req->in.numargs = 2;
|
||||||
|
req->in.args[0].size = sizeof(inarg);
|
||||||
|
req->in.args[0].value = &inarg;
|
||||||
|
req->in.args[1].size = entry->d_name.len + 1;
|
||||||
|
req->in.args[1].value = entry->d_name.name;
|
||||||
|
return create_new_entry(fc, req, dir, entry, S_IFDIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fuse_symlink(struct inode *dir, struct dentry *entry,
|
||||||
|
const char *link)
|
||||||
|
{
|
||||||
|
struct fuse_conn *fc = get_fuse_conn(dir);
|
||||||
|
unsigned len = strlen(link) + 1;
|
||||||
|
struct fuse_req *req;
|
||||||
|
|
||||||
|
if (len > FUSE_SYMLINK_MAX)
|
||||||
|
return -ENAMETOOLONG;
|
||||||
|
|
||||||
|
req = fuse_get_request(fc);
|
||||||
|
if (!req)
|
||||||
|
return -ERESTARTNOINTR;
|
||||||
|
|
||||||
|
req->in.h.opcode = FUSE_SYMLINK;
|
||||||
|
req->in.numargs = 2;
|
||||||
|
req->in.args[0].size = entry->d_name.len + 1;
|
||||||
|
req->in.args[0].value = entry->d_name.name;
|
||||||
|
req->in.args[1].size = len;
|
||||||
|
req->in.args[1].value = link;
|
||||||
|
return create_new_entry(fc, req, dir, entry, S_IFLNK);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fuse_unlink(struct inode *dir, struct dentry *entry)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct fuse_conn *fc = get_fuse_conn(dir);
|
||||||
|
struct fuse_req *req = fuse_get_request(fc);
|
||||||
|
if (!req)
|
||||||
|
return -ERESTARTNOINTR;
|
||||||
|
|
||||||
|
req->in.h.opcode = FUSE_UNLINK;
|
||||||
|
req->in.h.nodeid = get_node_id(dir);
|
||||||
|
req->inode = dir;
|
||||||
|
req->in.numargs = 1;
|
||||||
|
req->in.args[0].size = entry->d_name.len + 1;
|
||||||
|
req->in.args[0].value = entry->d_name.name;
|
||||||
|
request_send(fc, req);
|
||||||
|
err = req->out.h.error;
|
||||||
|
fuse_put_request(fc, req);
|
||||||
|
if (!err) {
|
||||||
|
struct inode *inode = entry->d_inode;
|
||||||
|
|
||||||
|
/* Set nlink to zero so the inode can be cleared, if
|
||||||
|
the inode does have more links this will be
|
||||||
|
discovered at the next lookup/getattr */
|
||||||
|
inode->i_nlink = 0;
|
||||||
|
fuse_invalidate_attr(inode);
|
||||||
|
fuse_invalidate_attr(dir);
|
||||||
|
} else if (err == -EINTR)
|
||||||
|
fuse_invalidate_entry(entry);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fuse_rmdir(struct inode *dir, struct dentry *entry)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct fuse_conn *fc = get_fuse_conn(dir);
|
||||||
|
struct fuse_req *req = fuse_get_request(fc);
|
||||||
|
if (!req)
|
||||||
|
return -ERESTARTNOINTR;
|
||||||
|
|
||||||
|
req->in.h.opcode = FUSE_RMDIR;
|
||||||
|
req->in.h.nodeid = get_node_id(dir);
|
||||||
|
req->inode = dir;
|
||||||
|
req->in.numargs = 1;
|
||||||
|
req->in.args[0].size = entry->d_name.len + 1;
|
||||||
|
req->in.args[0].value = entry->d_name.name;
|
||||||
|
request_send(fc, req);
|
||||||
|
err = req->out.h.error;
|
||||||
|
fuse_put_request(fc, req);
|
||||||
|
if (!err) {
|
||||||
|
entry->d_inode->i_nlink = 0;
|
||||||
|
fuse_invalidate_attr(dir);
|
||||||
|
} else if (err == -EINTR)
|
||||||
|
fuse_invalidate_entry(entry);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fuse_rename(struct inode *olddir, struct dentry *oldent,
|
||||||
|
struct inode *newdir, struct dentry *newent)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct fuse_rename_in inarg;
|
||||||
|
struct fuse_conn *fc = get_fuse_conn(olddir);
|
||||||
|
struct fuse_req *req = fuse_get_request(fc);
|
||||||
|
if (!req)
|
||||||
|
return -ERESTARTNOINTR;
|
||||||
|
|
||||||
|
memset(&inarg, 0, sizeof(inarg));
|
||||||
|
inarg.newdir = get_node_id(newdir);
|
||||||
|
req->in.h.opcode = FUSE_RENAME;
|
||||||
|
req->in.h.nodeid = get_node_id(olddir);
|
||||||
|
req->inode = olddir;
|
||||||
|
req->inode2 = newdir;
|
||||||
|
req->in.numargs = 3;
|
||||||
|
req->in.args[0].size = sizeof(inarg);
|
||||||
|
req->in.args[0].value = &inarg;
|
||||||
|
req->in.args[1].size = oldent->d_name.len + 1;
|
||||||
|
req->in.args[1].value = oldent->d_name.name;
|
||||||
|
req->in.args[2].size = newent->d_name.len + 1;
|
||||||
|
req->in.args[2].value = newent->d_name.name;
|
||||||
|
request_send(fc, req);
|
||||||
|
err = req->out.h.error;
|
||||||
|
fuse_put_request(fc, req);
|
||||||
|
if (!err) {
|
||||||
|
fuse_invalidate_attr(olddir);
|
||||||
|
if (olddir != newdir)
|
||||||
|
fuse_invalidate_attr(newdir);
|
||||||
|
} else if (err == -EINTR) {
|
||||||
|
/* If request was interrupted, DEITY only knows if the
|
||||||
|
rename actually took place. If the invalidation
|
||||||
|
fails (e.g. some process has CWD under the renamed
|
||||||
|
directory), then there can be inconsistency between
|
||||||
|
the dcache and the real filesystem. Tough luck. */
|
||||||
|
fuse_invalidate_entry(oldent);
|
||||||
|
if (newent->d_inode)
|
||||||
|
fuse_invalidate_entry(newent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fuse_link(struct dentry *entry, struct inode *newdir,
|
||||||
|
struct dentry *newent)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct fuse_link_in inarg;
|
||||||
|
struct inode *inode = entry->d_inode;
|
||||||
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||||
|
struct fuse_req *req = fuse_get_request(fc);
|
||||||
|
if (!req)
|
||||||
|
return -ERESTARTNOINTR;
|
||||||
|
|
||||||
|
memset(&inarg, 0, sizeof(inarg));
|
||||||
|
inarg.oldnodeid = get_node_id(inode);
|
||||||
|
req->in.h.opcode = FUSE_LINK;
|
||||||
|
req->inode2 = inode;
|
||||||
|
req->in.numargs = 2;
|
||||||
|
req->in.args[0].size = sizeof(inarg);
|
||||||
|
req->in.args[0].value = &inarg;
|
||||||
|
req->in.args[1].size = newent->d_name.len + 1;
|
||||||
|
req->in.args[1].value = newent->d_name.name;
|
||||||
|
err = create_new_entry(fc, req, newdir, newent, inode->i_mode);
|
||||||
|
/* Contrary to "normal" filesystems it can happen that link
|
||||||
|
makes two "logical" inodes point to the same "physical"
|
||||||
|
inode. We invalidate the attributes of the old one, so it
|
||||||
|
will reflect changes in the backing inode (link count,
|
||||||
|
etc.)
|
||||||
|
*/
|
||||||
|
if (!err || err == -EINTR)
|
||||||
|
fuse_invalidate_attr(inode);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
int fuse_do_getattr(struct inode *inode)
|
int fuse_do_getattr(struct inode *inode)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
@ -341,6 +600,91 @@ static int fuse_dir_release(struct inode *inode, struct file *file)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned iattr_to_fattr(struct iattr *iattr, struct fuse_attr *fattr)
|
||||||
|
{
|
||||||
|
unsigned ivalid = iattr->ia_valid;
|
||||||
|
unsigned fvalid = 0;
|
||||||
|
|
||||||
|
memset(fattr, 0, sizeof(*fattr));
|
||||||
|
|
||||||
|
if (ivalid & ATTR_MODE)
|
||||||
|
fvalid |= FATTR_MODE, fattr->mode = iattr->ia_mode;
|
||||||
|
if (ivalid & ATTR_UID)
|
||||||
|
fvalid |= FATTR_UID, fattr->uid = iattr->ia_uid;
|
||||||
|
if (ivalid & ATTR_GID)
|
||||||
|
fvalid |= FATTR_GID, fattr->gid = iattr->ia_gid;
|
||||||
|
if (ivalid & ATTR_SIZE)
|
||||||
|
fvalid |= FATTR_SIZE, fattr->size = iattr->ia_size;
|
||||||
|
/* You can only _set_ these together (they may change by themselves) */
|
||||||
|
if ((ivalid & (ATTR_ATIME | ATTR_MTIME)) == (ATTR_ATIME | ATTR_MTIME)) {
|
||||||
|
fvalid |= FATTR_ATIME | FATTR_MTIME;
|
||||||
|
fattr->atime = iattr->ia_atime.tv_sec;
|
||||||
|
fattr->mtime = iattr->ia_mtime.tv_sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fvalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fuse_setattr(struct dentry *entry, struct iattr *attr)
|
||||||
|
{
|
||||||
|
struct inode *inode = entry->d_inode;
|
||||||
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||||
|
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||||
|
struct fuse_req *req;
|
||||||
|
struct fuse_setattr_in inarg;
|
||||||
|
struct fuse_attr_out outarg;
|
||||||
|
int err;
|
||||||
|
int is_truncate = 0;
|
||||||
|
|
||||||
|
if (attr->ia_valid & ATTR_SIZE) {
|
||||||
|
unsigned long limit;
|
||||||
|
is_truncate = 1;
|
||||||
|
limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
|
||||||
|
if (limit != RLIM_INFINITY && attr->ia_size > (loff_t) limit) {
|
||||||
|
send_sig(SIGXFSZ, current, 0);
|
||||||
|
return -EFBIG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req = fuse_get_request(fc);
|
||||||
|
if (!req)
|
||||||
|
return -ERESTARTNOINTR;
|
||||||
|
|
||||||
|
memset(&inarg, 0, sizeof(inarg));
|
||||||
|
inarg.valid = iattr_to_fattr(attr, &inarg.attr);
|
||||||
|
req->in.h.opcode = FUSE_SETATTR;
|
||||||
|
req->in.h.nodeid = get_node_id(inode);
|
||||||
|
req->inode = inode;
|
||||||
|
req->in.numargs = 1;
|
||||||
|
req->in.args[0].size = sizeof(inarg);
|
||||||
|
req->in.args[0].value = &inarg;
|
||||||
|
req->out.numargs = 1;
|
||||||
|
req->out.args[0].size = sizeof(outarg);
|
||||||
|
req->out.args[0].value = &outarg;
|
||||||
|
request_send(fc, req);
|
||||||
|
err = req->out.h.error;
|
||||||
|
fuse_put_request(fc, req);
|
||||||
|
if (!err) {
|
||||||
|
if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
|
||||||
|
make_bad_inode(inode);
|
||||||
|
err = -EIO;
|
||||||
|
} else {
|
||||||
|
if (is_truncate) {
|
||||||
|
loff_t origsize = i_size_read(inode);
|
||||||
|
i_size_write(inode, outarg.attr.size);
|
||||||
|
if (origsize > outarg.attr.size)
|
||||||
|
vmtruncate(inode, outarg.attr.size);
|
||||||
|
}
|
||||||
|
fuse_change_attributes(inode, &outarg.attr);
|
||||||
|
fi->i_time = time_to_jiffies(outarg.attr_valid,
|
||||||
|
outarg.attr_valid_nsec);
|
||||||
|
}
|
||||||
|
} else if (err == -EINTR)
|
||||||
|
fuse_invalidate_attr(inode);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
|
static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
|
||||||
struct kstat *stat)
|
struct kstat *stat)
|
||||||
{
|
{
|
||||||
@ -373,6 +717,15 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
|
|||||||
|
|
||||||
static struct inode_operations fuse_dir_inode_operations = {
|
static struct inode_operations fuse_dir_inode_operations = {
|
||||||
.lookup = fuse_lookup,
|
.lookup = fuse_lookup,
|
||||||
|
.mkdir = fuse_mkdir,
|
||||||
|
.symlink = fuse_symlink,
|
||||||
|
.unlink = fuse_unlink,
|
||||||
|
.rmdir = fuse_rmdir,
|
||||||
|
.rename = fuse_rename,
|
||||||
|
.link = fuse_link,
|
||||||
|
.setattr = fuse_setattr,
|
||||||
|
.create = fuse_create,
|
||||||
|
.mknod = fuse_mknod,
|
||||||
.permission = fuse_permission,
|
.permission = fuse_permission,
|
||||||
.getattr = fuse_getattr,
|
.getattr = fuse_getattr,
|
||||||
};
|
};
|
||||||
@ -385,11 +738,13 @@ static struct file_operations fuse_dir_operations = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static struct inode_operations fuse_common_inode_operations = {
|
static struct inode_operations fuse_common_inode_operations = {
|
||||||
|
.setattr = fuse_setattr,
|
||||||
.permission = fuse_permission,
|
.permission = fuse_permission,
|
||||||
.getattr = fuse_getattr,
|
.getattr = fuse_getattr,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct inode_operations fuse_symlink_inode_operations = {
|
static struct inode_operations fuse_symlink_inode_operations = {
|
||||||
|
.setattr = fuse_setattr,
|
||||||
.follow_link = fuse_follow_link,
|
.follow_link = fuse_follow_link,
|
||||||
.put_link = fuse_put_link,
|
.put_link = fuse_put_link,
|
||||||
.readlink = generic_readlink,
|
.readlink = generic_readlink,
|
||||||
|
@ -30,6 +30,9 @@ struct fuse_inode {
|
|||||||
* and kernel */
|
* and kernel */
|
||||||
u64 nodeid;
|
u64 nodeid;
|
||||||
|
|
||||||
|
/** Number of lookups on this inode */
|
||||||
|
u64 nlookup;
|
||||||
|
|
||||||
/** The request used for sending the FORGET message */
|
/** The request used for sending the FORGET message */
|
||||||
struct fuse_req *forget_req;
|
struct fuse_req *forget_req;
|
||||||
|
|
||||||
@ -252,13 +255,13 @@ extern spinlock_t fuse_lock;
|
|||||||
* Get a filled in inode
|
* Get a filled in inode
|
||||||
*/
|
*/
|
||||||
struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
|
struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
|
||||||
int generation, struct fuse_attr *attr, int version);
|
int generation, struct fuse_attr *attr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send FORGET command
|
* Send FORGET command
|
||||||
*/
|
*/
|
||||||
void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req,
|
void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req,
|
||||||
unsigned long nodeid, int version);
|
unsigned long nodeid, u64 nlookup);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialise inode operations on regular files and special files
|
* Initialise inode operations on regular files and special files
|
||||||
|
@ -51,6 +51,7 @@ static struct inode *fuse_alloc_inode(struct super_block *sb)
|
|||||||
fi = get_fuse_inode(inode);
|
fi = get_fuse_inode(inode);
|
||||||
fi->i_time = jiffies - 1;
|
fi->i_time = jiffies - 1;
|
||||||
fi->nodeid = 0;
|
fi->nodeid = 0;
|
||||||
|
fi->nlookup = 0;
|
||||||
fi->forget_req = fuse_request_alloc();
|
fi->forget_req = fuse_request_alloc();
|
||||||
if (!fi->forget_req) {
|
if (!fi->forget_req) {
|
||||||
kmem_cache_free(fuse_inode_cachep, inode);
|
kmem_cache_free(fuse_inode_cachep, inode);
|
||||||
@ -74,10 +75,10 @@ static void fuse_read_inode(struct inode *inode)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req,
|
void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req,
|
||||||
unsigned long nodeid, int version)
|
unsigned long nodeid, u64 nlookup)
|
||||||
{
|
{
|
||||||
struct fuse_forget_in *inarg = &req->misc.forget_in;
|
struct fuse_forget_in *inarg = &req->misc.forget_in;
|
||||||
inarg->version = version;
|
inarg->nlookup = nlookup;
|
||||||
req->in.h.opcode = FUSE_FORGET;
|
req->in.h.opcode = FUSE_FORGET;
|
||||||
req->in.h.nodeid = nodeid;
|
req->in.h.nodeid = nodeid;
|
||||||
req->in.numargs = 1;
|
req->in.numargs = 1;
|
||||||
@ -91,7 +92,7 @@ static void fuse_clear_inode(struct inode *inode)
|
|||||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||||
if (fc) {
|
if (fc) {
|
||||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||||
fuse_send_forget(fc, fi->forget_req, fi->nodeid, inode->i_version);
|
fuse_send_forget(fc, fi->forget_req, fi->nodeid, fi->nlookup);
|
||||||
fi->forget_req = NULL;
|
fi->forget_req = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -156,9 +157,10 @@ static int fuse_inode_set(struct inode *inode, void *_nodeidp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
|
struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
|
||||||
int generation, struct fuse_attr *attr, int version)
|
int generation, struct fuse_attr *attr)
|
||||||
{
|
{
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
|
struct fuse_inode *fi;
|
||||||
struct fuse_conn *fc = get_fuse_conn_super(sb);
|
struct fuse_conn *fc = get_fuse_conn_super(sb);
|
||||||
int retried = 0;
|
int retried = 0;
|
||||||
|
|
||||||
@ -181,8 +183,9 @@ struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
|
|||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fi = get_fuse_inode(inode);
|
||||||
|
fi->nlookup ++;
|
||||||
fuse_change_attributes(inode, attr);
|
fuse_change_attributes(inode, attr);
|
||||||
inode->i_version = version;
|
|
||||||
return inode;
|
return inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,7 +392,7 @@ static struct inode *get_root_inode(struct super_block *sb, unsigned mode)
|
|||||||
|
|
||||||
attr.mode = mode;
|
attr.mode = mode;
|
||||||
attr.ino = FUSE_ROOT_ID;
|
attr.ino = FUSE_ROOT_ID;
|
||||||
return fuse_iget(sb, 1, 0, &attr, 0);
|
return fuse_iget(sb, 1, 0, &attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct super_operations fuse_super_operations = {
|
static struct super_operations fuse_super_operations = {
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
#include <asm/types.h>
|
#include <asm/types.h>
|
||||||
|
|
||||||
/** Version number of this interface */
|
/** Version number of this interface */
|
||||||
#define FUSE_KERNEL_VERSION 6
|
#define FUSE_KERNEL_VERSION 7
|
||||||
|
|
||||||
/** Minor version number of this interface */
|
/** Minor version number of this interface */
|
||||||
#define FUSE_KERNEL_MINOR_VERSION 1
|
#define FUSE_KERNEL_MINOR_VERSION 1
|
||||||
@ -52,12 +52,28 @@ struct fuse_kstatfs {
|
|||||||
__u32 namelen;
|
__u32 namelen;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define FATTR_MODE (1 << 0)
|
||||||
|
#define FATTR_UID (1 << 1)
|
||||||
|
#define FATTR_GID (1 << 2)
|
||||||
|
#define FATTR_SIZE (1 << 3)
|
||||||
|
#define FATTR_ATIME (1 << 4)
|
||||||
|
#define FATTR_MTIME (1 << 5)
|
||||||
|
#define FATTR_CTIME (1 << 6)
|
||||||
|
|
||||||
enum fuse_opcode {
|
enum fuse_opcode {
|
||||||
FUSE_LOOKUP = 1,
|
FUSE_LOOKUP = 1,
|
||||||
FUSE_FORGET = 2, /* no reply */
|
FUSE_FORGET = 2, /* no reply */
|
||||||
FUSE_GETATTR = 3,
|
FUSE_GETATTR = 3,
|
||||||
|
FUSE_SETATTR = 4,
|
||||||
FUSE_READLINK = 5,
|
FUSE_READLINK = 5,
|
||||||
|
FUSE_SYMLINK = 6,
|
||||||
FUSE_GETDIR = 7,
|
FUSE_GETDIR = 7,
|
||||||
|
FUSE_MKNOD = 8,
|
||||||
|
FUSE_MKDIR = 9,
|
||||||
|
FUSE_UNLINK = 10,
|
||||||
|
FUSE_RMDIR = 11,
|
||||||
|
FUSE_RENAME = 12,
|
||||||
|
FUSE_LINK = 13,
|
||||||
FUSE_STATFS = 17,
|
FUSE_STATFS = 17,
|
||||||
FUSE_INIT = 26
|
FUSE_INIT = 26
|
||||||
};
|
};
|
||||||
@ -66,6 +82,7 @@ enum fuse_opcode {
|
|||||||
#define FUSE_MAX_IN 8192
|
#define FUSE_MAX_IN 8192
|
||||||
|
|
||||||
#define FUSE_NAME_MAX 1024
|
#define FUSE_NAME_MAX 1024
|
||||||
|
#define FUSE_SYMLINK_MAX 4096
|
||||||
|
|
||||||
struct fuse_entry_out {
|
struct fuse_entry_out {
|
||||||
__u64 nodeid; /* Inode ID */
|
__u64 nodeid; /* Inode ID */
|
||||||
@ -79,7 +96,7 @@ struct fuse_entry_out {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct fuse_forget_in {
|
struct fuse_forget_in {
|
||||||
__u64 version;
|
__u64 nlookup;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct fuse_attr_out {
|
struct fuse_attr_out {
|
||||||
@ -93,6 +110,28 @@ struct fuse_getdir_out {
|
|||||||
__u32 fd;
|
__u32 fd;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct fuse_mknod_in {
|
||||||
|
__u32 mode;
|
||||||
|
__u32 rdev;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_mkdir_in {
|
||||||
|
__u32 mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_rename_in {
|
||||||
|
__u64 newdir;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_link_in {
|
||||||
|
__u64 oldnodeid;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_setattr_in {
|
||||||
|
__u32 valid;
|
||||||
|
struct fuse_attr attr;
|
||||||
|
};
|
||||||
|
|
||||||
struct fuse_statfs_out {
|
struct fuse_statfs_out {
|
||||||
struct fuse_kstatfs st;
|
struct fuse_kstatfs st;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user