aboutsummaryrefslogtreecommitdiff
path: root/kernel/dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/dir.c')
-rw-r--r--kernel/dir.c384
1 files changed, 384 insertions, 0 deletions
diff --git a/kernel/dir.c b/kernel/dir.c
new file mode 100644
index 0000000..92ddc55
--- /dev/null
+++ b/kernel/dir.c
@@ -0,0 +1,384 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001 Miklos Szeredi (mszeredi@inf.bme.hu)
+
+ This program can be distributed under the terms of the GNU GPL.
+ 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 {
+ fuse_special_init(inode);
+ init_special_inode(inode, inode->i_mode, attr->rdev);
+ }
+}
+
+
+static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry)
+{
+ struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
+ struct fuse_in in = FUSE_IN_INIT;
+ struct fuse_out out = FUSE_OUT_INIT;
+ struct fuse_lookup_out arg;
+ struct inode *inode;
+
+ in.h.opcode = FUSE_LOOKUP;
+ in.h.ino = dir->i_ino;
+ in.argsize = entry->d_name.len + 1;
+ in.arg = entry->d_name.name;
+ out.argsize = sizeof(arg);
+ 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 = iget(dir->i_sb, arg.ino);
+ if(!inode)
+ return ERR_PTR(-ENOMEM);
+
+ init_inode(inode, &arg.attr);
+ d_add(entry, inode);
+ return NULL;
+}
+
+static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode,
+ int rdev)
+{
+ struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
+ struct fuse_in in = FUSE_IN_INIT;
+ struct fuse_out out = FUSE_OUT_INIT;
+ struct fuse_mknod_in *inarg;
+ unsigned int insize;
+ struct fuse_mknod_out outarg;
+ struct inode *inode;
+
+ insize = offsetof(struct fuse_mknod_in, name) + entry->d_name.len + 1;
+ inarg = kmalloc(insize, GFP_KERNEL);
+ if(!inarg)
+ return -ENOMEM;
+
+ inarg->mode = mode;
+ inarg->rdev = rdev;
+ strcpy(inarg->name, entry->d_name.name);
+
+ in.h.opcode = FUSE_MKNOD;
+ in.h.ino = dir->i_ino;
+ in.argsize = insize;
+ in.arg = inarg;
+ out.argsize = sizeof(outarg);
+ out.arg = &outarg;
+ request_send(fc, &in, &out);
+ kfree(inarg);
+ if(out.h.result)
+ return out.h.result;
+
+ inode = iget(dir->i_sb, outarg.ino);
+ if(!inode)
+ return -ENOMEM;
+
+ init_inode(inode, &outarg.attr);
+ d_add(entry, inode);
+ return 0;
+}
+
+static int fuse_create(struct inode *dir, struct dentry *entry, int mode)
+{
+ return fuse_mknod(dir, entry, mode, 0);
+}
+
+static int fuse_permission(struct inode *inode, int mask)
+{
+
+ return 0;
+}
+
+static int fuse_revalidate(struct dentry *dentry)
+{
+ struct inode *inode = dentry->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_getattr_out arg;
+
+ 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)
+ change_attributes(inode, &arg.attr);
+
+ return out.h.result;
+}
+
+
+#define DIR_BUFSIZE 2048
+
+static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
+{
+ 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 over;
+ if(dirent->namelen > NAME_MAX) {
+ printk("fuse_readdir: name too long\n");
+ ret = -EPROTO;
+ goto out;
+ }
+ if(reclen > nbytes)
+ break;
+
+ over = filldir(dstbuf, dirent->name, dirent->namelen,
+ file->f_pos, dirent->ino, dirent->type);
+ if(over)
+ break;
+
+ p += reclen;
+ file->f_pos += reclen;
+ nbytes -= reclen;
+ }
+
+ 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 = FUSE_IN_INIT;
+ struct fuse_out out = FUSE_OUT_INIT;
+ unsigned long page;
+
+ page = __get_free_page(GFP_KERNEL);
+ if(!page)
+ return -ENOMEM;
+
+ in.h.opcode = FUSE_READLINK;
+ in.h.ino = inode->i_ino;
+ out.arg = (void *) page;
+ out.argsize = PAGE_SIZE - 1;
+ out.argvar = 1;
+ request_send(fc, &in, &out);
+ if(out.h.result) {
+ free_page(page);
+ return out.h.result;
+ }
+
+ *bufp = (char *) page;
+ (*bufp)[out.argsize] = '\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_dir_open(struct inode *inode, struct file *file)
+{
+ 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_getdir_out outarg;
+
+ if(!(file->f_flags & O_DIRECTORY))
+ return -EISDIR;
+
+ in.h.opcode = FUSE_GETDIR;
+ in.h.ino = inode->i_ino;
+ out.argsize = sizeof(outarg);
+ out.arg = &outarg;
+ request_send(fc, &in, &out);
+ if(out.h.result == 0) {
+ struct file *cfile = outarg.file;
+ struct inode *inode;
+ if(!cfile) {
+ printk("fuse_getdir: invalid file\n");
+ return -EPROTO;
+ }
+ inode = cfile->f_dentry->d_inode;
+ if(!S_ISREG(inode->i_mode)) {
+ printk("fuse_getdir: not a regular file\n");
+ fput(cfile);
+ return -EPROTO;
+ }
+
+ file->private_data = cfile;
+ }
+
+ return out.h.result;
+}
+
+static int fuse_dir_release(struct inode *inode, struct file *file)
+{
+ struct file *cfile = file->private_data;
+
+ if(!cfile)
+ BUG();
+
+ fput(cfile);
+
+ return 0;
+}
+
+static struct inode_operations fuse_dir_inode_operations =
+{
+ lookup: fuse_lookup,
+ create: fuse_create,
+ mknod: fuse_mknod,
+#if 0
+
+ link: fuse_link,
+ unlink: fuse_unlink,
+ symlink: fuse_symlink,
+ mkdir: fuse_mkdir,
+ rmdir: fuse_rmdir,
+ rename: fuse_rename,
+#endif
+ permission: fuse_permission,
+ revalidate: fuse_revalidate,
+};
+
+static struct file_operations fuse_dir_operations = {
+ read: generic_read_dir,
+ readdir: fuse_readdir,
+ open: fuse_dir_open,
+ release: fuse_dir_release,
+};
+
+static struct inode_operations fuse_file_inode_operations = {
+ permission: fuse_permission,
+ revalidate: fuse_revalidate,
+};
+
+static struct inode_operations fuse_special_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;
+}
+
+void fuse_special_init(struct inode *inode)
+{
+ inode->i_op = &fuse_special_inode_operations;
+}
+
+/*
+ * Local Variables:
+ * indent-tabs-mode: t
+ * c-basic-offset: 8
+ * End:
+ */