aboutsummaryrefslogtreecommitdiffstats
path: root/dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'dir.c')
-rw-r--r--dir.c248
1 files changed, 232 insertions, 16 deletions
diff --git a/dir.c b/dir.c
index 6fa5959..7a3ccc2 100644
--- a/dir.c
+++ b/dir.c
@@ -6,65 +6,255 @@
See the file COPYING.
*/
-
#include "fuse_i.h"
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+
+static void change_attributes(struct inode *inode, struct fuse_attr *attr)
+{
+ inode->i_mode = attr->mode;
+ inode->i_nlink = attr->nlink;
+ inode->i_uid = attr->uid;
+ inode->i_gid = attr->gid;
+ inode->i_size = attr->size;
+ inode->i_blksize = attr->blksize;
+ inode->i_blocks = attr->blocks;
+ inode->i_atime = attr->atime;
+ inode->i_mtime = attr->mtime;
+ inode->i_ctime = attr->ctime;
+}
+
+static void init_inode(struct inode *inode, struct fuse_attr *attr)
+{
+ change_attributes(inode, attr);
+
+ if(S_ISREG(inode->i_mode))
+ fuse_file_init(inode);
+ else if(S_ISDIR(inode->i_mode))
+ fuse_dir_init(inode);
+ else if(S_ISLNK(inode->i_mode))
+ fuse_symlink_init(inode);
+ else
+ init_special_inode(inode, inode->i_mode, attr->rdev);
+}
static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry)
{
- printk(KERN_DEBUG "fuse_lookup: %li\n", dir->i_ino);
+ struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
+ struct fuse_inparam in;
+ struct fuse_outparam out;
+ struct inode *inode;
+
+ in.opcode = FUSE_LOOKUP;
+ in.ino = dir->i_ino;
+ strcpy(in.u.lookup.name, entry->d_name.name);
+
+ request_send(fc, &in, &out);
+
+ if(out.result)
+ return ERR_PTR(out.result);
+
+ inode = iget(dir->i_sb, out.u.lookup.ino);
+ if(!inode)
+ return ERR_PTR(-ENOMEM);
+
+ init_inode(inode, &out.u.lookup.attr);
+ d_add(entry, inode);
return NULL;
}
static int fuse_permission(struct inode *inode, int mask)
{
- printk(KERN_DEBUG "fuse_permission: %li, 0%o\n", inode->i_ino, mask);
return 0;
}
static int fuse_revalidate(struct dentry *dentry)
{
- printk(KERN_DEBUG "fuse_revalidate: %li\n",
- dentry->d_inode->i_ino);
+ struct inode *inode = dentry->d_inode;
+ struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
+ struct fuse_inparam in;
+ struct fuse_outparam out;
- return 0;
+ in.opcode = FUSE_GETATTR;
+ in.ino = inode->i_ino;
+
+ request_send(fc, &in, &out);
+
+ if(out.result == 0)
+ change_attributes(inode, &out.u.getattr.attr);
+
+ return out.result;
}
-static int fuse_readdir(struct file *file, void *dirent, filldir_t filldir)
+
+
+#define DIR_BUFSIZE 2048
+
+static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
{
- printk(KERN_DEBUG "fuse_readdir: %li\n",
- file->f_dentry->d_inode->i_ino);
+ struct file *cfile = file->private_data;
+ char *buf;
+ char *p;
+ int ret;
+ size_t nbytes;
+ buf = kmalloc(DIR_BUFSIZE, GFP_KERNEL);
+ if(!buf)
+ return -ENOMEM;
+
+ ret = kernel_read(cfile, file->f_pos, buf, DIR_BUFSIZE);
+ if(ret < 0) {
+ printk("fuse_readdir: failed to read container file\n");
+ goto out;
+ }
+ nbytes = ret;
+ p = buf;
+ ret = 0;
+ while(nbytes >= FUSE_NAME_OFFSET) {
+ struct fuse_dirent *dirent = (struct fuse_dirent *) p;
+ size_t reclen = FUSE_DIRENT_SIZE(dirent);
+ int err;
+ if(dirent->namelen > NAME_MAX) {
+ printk("fuse_readdir: name too long\n");
+ ret = -EPROTO;
+ goto out;
+ }
+ if(reclen > nbytes)
+ break;
+
+ err = filldir(dstbuf, dirent->name, dirent->namelen,
+ file->f_pos, dirent->ino, dirent->type);
+ if(err) {
+ ret = err;
+ break;
+ }
+ p += reclen;
+ file->f_pos += reclen;
+ nbytes -= reclen;
+ ret ++;
+ }
+
+ out:
+ kfree(buf);
+ return ret;
+}
+
+
+
+static int read_link(struct dentry *dentry, char **bufp)
+{
+ struct inode *inode = dentry->d_inode;
+ struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
+ struct fuse_in in;
+ struct fuse_out out;
+ unsigned long page;
+
+ page = __get_free_page(GFP_KERNEL);
+ if(!page)
+ return -ENOMEM;
+
+ in.c.opcode = FUSE_READLINK;
+ in.c.ino = inode->i_ino;
+ in.argsize = 0;
+ out.arg = (void *) page;
+ out.argsize = PAGE_SIZE;
+
+ request_send(fc, &in, &out);
+ if(out.c.result) {
+ __free_page(page);
+ return out.c.result;
+ }
+
+ *bufp = (char *) page;
+ (*bufp)[PAGE_SIZE - 1] = 0;
return 0;
}
+static void free_link(char *link)
+{
+ __free_page((unsigned long) link);
+}
+
+static int fuse_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+ int ret;
+ char *link;
+
+ ret = read_link(dentry, &link);
+ if(ret)
+ return ret;
+
+ ret = vfs_readlink(dentry, buffer, buflen, link);
+ free_link(link);
+ return ret;
+}
+
+static int fuse_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ int ret;
+ char *link;
+
+ ret = read_link(dentry, &link);
+ if(ret)
+ return ret;
+
+ ret = vfs_follow_link(nd, link);
+ free_link(link);
+ return ret;
+}
+
static int fuse_open(struct inode *inode, struct file *file)
{
struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
struct fuse_inparam in;
struct fuse_outparam out;
+ struct file *cfile = NULL;
- printk(KERN_DEBUG "fuse_open: %li\n", inode->i_ino);
-
in.opcode = FUSE_OPEN;
- in.u.open.ino = inode->i_ino;
+ in.ino = inode->i_ino;
in.u.open.flags = file->f_flags & ~O_EXCL;
- request_send(fc, &in, &out, 0);
+ request_send(fc, &in, &out);
+
+ if(out.result == 0) {
+ struct inode *inode;
+ cfile = out.u.open_internal.file;
+ if(!cfile) {
+ printk("fuse_open: invalid container file\n");
+ return -EPROTO;
+ }
+ inode = cfile->f_dentry->d_inode;
+ if(!S_ISREG(inode->i_mode)) {
+ printk("fuse_open: container is not a regular file\n");
+ fput(cfile);
+ return -EPROTO;
+ }
- printk(KERN_DEBUG " fuse_open: <%i> %i\n", out.result, out.u.open.fd);
+ file->private_data = cfile;
+ }
return out.result;
}
static int fuse_release(struct inode *inode, struct file *file)
{
- printk(KERN_DEBUG "fuse_release: %li\n", inode->i_ino);
+ struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
+ struct file *cfile = file->private_data;
+ struct fuse_inparam in;
+ struct fuse_outparam out;
- return 0;
+ if(cfile)
+ fput(cfile);
+
+ in.opcode = FUSE_RELEASE;
+ request_send(fc, &in, &out);
+
+ return out.result;
}
static struct inode_operations fuse_dir_inode_operations =
@@ -81,12 +271,38 @@ static struct file_operations fuse_dir_operations = {
release: fuse_release,
};
+static struct inode_operations fuse_file_inode_operations =
+{
+ permission: fuse_permission,
+ revalidate: fuse_revalidate,
+};
+
+static struct file_operations fuse_file_operations = {
+};
+
+static struct inode_operations fuse_symlink_inode_operations =
+{
+ readlink: fuse_readlink,
+ follow_link: fuse_follow_link,
+ revalidate: fuse_revalidate,
+};
+
void fuse_dir_init(struct inode *inode)
{
inode->i_op = &fuse_dir_inode_operations;
inode->i_fop = &fuse_dir_operations;
}
+void fuse_file_init(struct inode *inode)
+{
+ inode->i_op = &fuse_file_inode_operations;
+ inode->i_fop = &fuse_file_operations;
+}
+
+void fuse_symlink_init(struct inode *inode)
+{
+ inode->i_op = &fuse_symlink_inode_operations;
+}
/*
* Local Variables: