aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorMiklos Szeredi <miklos@szeredi.hu>2001-11-06 12:03:23 +0000
committerMiklos Szeredi <miklos@szeredi.hu>2001-11-06 12:03:23 +0000
commita181e61ca0119b8c3fd2daa4b8d23add2cda3ed0 (patch)
tree57176d5ca8f96008180eac92518791ef22add726 /kernel
parent5e183482b09cec20f54ccb44e767a7fab51833e9 (diff)
downloadlibfuse-a181e61ca0119b8c3fd2daa4b8d23add2cda3ed0.tar.gz
bugfixes
Diffstat (limited to 'kernel')
-rw-r--r--kernel/dev.c19
-rw-r--r--kernel/dir.c79
-rw-r--r--kernel/file.c87
-rw-r--r--kernel/fuse_i.h17
-rw-r--r--kernel/inode.c72
-rw-r--r--kernel/redir_hack.c787
6 files changed, 942 insertions, 119 deletions
diff --git a/kernel/dev.c b/kernel/dev.c
index 6a8d373..8fdb37f 100644
--- a/kernel/dev.c
+++ b/kernel/dev.c
@@ -128,7 +128,7 @@ void request_send(struct fuse_conn *fc, struct fuse_in *inp,
spin_lock(&fuse_lock);
ret = -ENOTCONN;
- if(fc->file == NULL)
+ if(!fc->file)
goto out_unlock_free;
ih = (struct fuse_in_header *) req->in;
@@ -204,7 +204,7 @@ static ssize_t fuse_dev_read(struct file *file, char *buf, size_t nbytes,
req = list_entry(fc->pending.next, struct fuse_req, list);
size = req->insize;
if(nbytes < size) {
- printk("fuse_dev_read[%i]: buffer too small\n", fc->id);
+ printk("fuse_dev_read: buffer too small\n");
ret = -EIO;
goto err;
}
@@ -221,6 +221,7 @@ static ssize_t fuse_dev_read(struct file *file, char *buf, size_t nbytes,
if(copy_to_user(buf, tmpbuf, size))
return -EFAULT;
+ kfree(tmpbuf);
return size;
err:
@@ -259,12 +260,12 @@ static ssize_t fuse_dev_write(struct file *file, const char *buf,
ret = -EIO;
if(nbytes < OHSIZE || nbytes > OHSIZE + PAGE_SIZE) {
- printk("fuse_dev_write[%i]: write is short or long\n", fc->id);
+ printk("fuse_dev_write: write is short or long\n");
goto out;
}
ret = -ENOMEM;
- tmpbuf = kmalloc(nbytes, GFP_KERNEL);
+ tmpbuf = kmalloc(nbytes, GFP_NOFS);
if(!tmpbuf)
goto out;
@@ -320,7 +321,6 @@ static unsigned int fuse_dev_poll(struct file *file, poll_table *wait)
static struct fuse_conn *new_conn(void)
{
- static int connctr = 1;
struct fuse_conn *fc;
fc = kmalloc(sizeof(*fc), GFP_KERNEL);
@@ -331,12 +331,6 @@ static struct fuse_conn *new_conn(void)
INIT_LIST_HEAD(&fc->pending);
INIT_LIST_HEAD(&fc->processing);
fc->reqctr = 1;
- fc->cleared = NULL;
- fc->numcleared = 0;
-
- spin_lock(&fuse_lock);
- fc->id = connctr ++;
- spin_unlock(&fuse_lock);
}
return fc;
}
@@ -376,9 +370,6 @@ static int fuse_dev_release(struct inode *inode, struct file *file)
fc->file = NULL;
end_requests(&fc->pending);
end_requests(&fc->processing);
- kfree(fc->cleared);
- fc->cleared = NULL;
- fc->numcleared = 0;
fuse_release_conn(fc);
spin_unlock(&fuse_lock);
return 0;
diff --git a/kernel/dir.c b/kernel/dir.c
index 1dac0c8..f506816 100644
--- a/kernel/dir.c
+++ b/kernel/dir.c
@@ -16,7 +16,6 @@
static struct inode_operations fuse_dir_inode_operations;
static struct inode_operations fuse_file_inode_operations;
static struct inode_operations fuse_symlink_inode_operations;
-static struct inode_operations fuse_special_inode_operations;
static struct file_operations fuse_dir_operations;
@@ -24,6 +23,9 @@ static struct dentry_operations fuse_dentry_opertations;
static void change_attributes(struct inode *inode, struct fuse_attr *attr)
{
+ if(S_ISREG(inode->i_mode) && inode->i_size != attr->size)
+ invalidate_inode_pages(inode);
+
inode->i_mode = (inode->i_mode & S_IFMT) + (attr->mode & 07777);
inode->i_nlink = attr->nlink;
inode->i_uid = attr->uid;
@@ -36,9 +38,10 @@ static void change_attributes(struct inode *inode, struct fuse_attr *attr)
inode->i_ctime = attr->ctime;
}
-static void fuse_init_inode(struct inode *inode, int mode, int rdev)
+static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
{
- inode->i_mode = mode & S_IFMT;
+ inode->i_mode = attr->mode & S_IFMT;
+ inode->i_size = attr->size;
if(S_ISREG(inode->i_mode)) {
inode->i_op = &fuse_file_inode_operations;
fuse_init_file_inode(inode);
@@ -51,23 +54,24 @@ static void fuse_init_inode(struct inode *inode, int mode, int rdev)
inode->i_op = &fuse_symlink_inode_operations;
}
else {
- inode->i_op = &fuse_special_inode_operations;
- init_special_inode(inode, inode->i_mode, rdev);
+ inode->i_op = &fuse_file_inode_operations;
+ init_special_inode(inode, inode->i_mode, attr->rdev);
}
inode->u.generic_ip = inode;
}
struct inode *fuse_iget(struct super_block *sb, ino_t ino,
- struct fuse_attr *attr)
+ struct fuse_attr *attr, int version)
{
struct inode *inode;
inode = iget(sb, ino);
if(inode) {
if(!inode->u.generic_ip)
- fuse_init_inode(inode, attr->mode, attr->rdev);
+ fuse_init_inode(inode, attr);
change_attributes(inode, attr);
+ inode->i_version = version;
}
return inode;
@@ -75,12 +79,13 @@ struct inode *fuse_iget(struct super_block *sb, ino_t ino,
static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry)
{
+ int ret;
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 arg;
struct inode *inode;
-
+
in.h.opcode = FUSE_LOOKUP;
in.h.ino = dir->i_ino;
in.argsize = entry->d_name.len + 1;
@@ -91,16 +96,24 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry)
inode = NULL;
if(!out.h.error) {
- inode = fuse_iget(dir->i_sb, arg.ino, &arg.attr);
+ ret = -ENOMEM;
+ inode = fuse_iget(dir->i_sb, arg.ino, &arg.attr, out.h.unique);
if(!inode)
- return ERR_PTR(-ENOMEM);
+ goto err;
+ }
+ else if(out.h.error != -ENOENT) {
+ ret = out.h.error;
+ goto err;
}
- else if(out.h.error != -ENOENT)
- return ERR_PTR(out.h.error);
+ entry->d_time = jiffies;
entry->d_op = &fuse_dentry_opertations;
d_add(entry, inode);
+
return NULL;
+
+ err:
+ return ERR_PTR(ret);
}
/* create needs to return a positive entry, so this also does a lookup */
@@ -136,12 +149,11 @@ static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode,
if(out.h.error)
return out.h.error;
- inode = fuse_iget(dir->i_sb, outarg.ino, &outarg.attr);
+ inode = fuse_iget(dir->i_sb, outarg.ino, &outarg.attr, out.h.unique);
if(!inode)
return -ENOMEM;
d_instantiate(entry, inode);
-
return 0;
}
@@ -293,17 +305,16 @@ 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)
+static int fuse_revalidate(struct dentry *entry)
{
- struct inode *inode = dentry->d_inode;
+ struct inode *inode = entry->d_inode;
struct fuse_conn *fc = INO_FC(inode);
struct fuse_in in = FUSE_IN_INIT;
struct fuse_out out = FUSE_OUT_INIT;
struct fuse_getattr_out arg;
- if(inode->i_ino != FUSE_ROOT_INO)
+ if(inode->i_ino != FUSE_ROOT_INO &&
+ time_before_eq(jiffies, entry->d_time + HZ / 100))
return 0;
in.h.opcode = FUSE_GETATTR;
@@ -435,7 +446,7 @@ static int fuse_dir_open(struct inode *inode, struct file *file)
struct fuse_getdir_out outarg;
if(!(file->f_flags & O_DIRECTORY))
- return -EISDIR;
+ return 0;
in.h.opcode = FUSE_GETDIR;
in.h.ino = inode->i_ino;
@@ -465,7 +476,9 @@ static int fuse_dir_open(struct inode *inode, struct file *file)
static int fuse_dir_release(struct inode *inode, struct file *file)
{
struct file *cfile = file->private_data;
- fput(cfile);
+
+ if(cfile)
+ fput(cfile);
return 0;
}
@@ -502,16 +515,26 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
struct fuse_conn *fc = INO_FC(inode);
struct fuse_in in = FUSE_IN_INIT;
struct fuse_out out = FUSE_OUT_INIT;
- struct fuse_setattr_in arg;
+ struct fuse_setattr_in inarg;
+ struct fuse_setattr_out outarg;
- arg.valid = iattr_to_fattr(attr, &arg.attr);
+ inarg.valid = iattr_to_fattr(attr, &inarg.attr);
in.h.opcode = FUSE_SETATTR;
in.h.ino = inode->i_ino;
- in.argsize = sizeof(arg);
- in.arg = &arg;
+ in.argsize = sizeof(inarg);
+ in.arg = &inarg;
+ out.argsize = sizeof(outarg);
+ out.arg = &outarg;
request_send(fc, &in, &out);
+ if(!out.h.error && (attr->ia_valid & ATTR_SIZE)) {
+ if(outarg.newsize > attr->ia_size)
+ outarg.newsize = attr->ia_size;
+
+ vmtruncate(inode, outarg.newsize);
+ }
+
return out.h.error;
}
@@ -552,12 +575,6 @@ static struct inode_operations fuse_file_inode_operations = {
revalidate: fuse_revalidate,
};
-static struct inode_operations fuse_special_inode_operations = {
- setattr: fuse_setattr,
- permission: fuse_permission,
- revalidate: fuse_revalidate,
-};
-
static struct inode_operations fuse_symlink_inode_operations =
{
setattr: fuse_setattr,
diff --git a/kernel/file.c b/kernel/file.c
index df3a863..a677d4c 100644
--- a/kernel/file.c
+++ b/kernel/file.c
@@ -10,6 +10,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pagemap.h>
+#include <linux/slab.h>
static int fuse_open(struct inode *inode, struct file *file)
@@ -25,10 +26,13 @@ static int fuse_open(struct inode *inode, struct file *file)
in.argsize = sizeof(arg);
in.arg = &arg;
request_send(fc, &in, &out);
+ if(!out.h.error)
+ invalidate_inode_pages(inode);
return out.h.error;
}
+
static int fuse_readpage(struct file *file, struct page *page)
{
struct inode *inode = page->mapping->host;
@@ -65,13 +69,96 @@ static int fuse_readpage(struct file *file, struct page *page)
return out.h.error;
}
+static int write_buffer(struct inode *inode, struct page *page,
+ unsigned offset, size_t count)
+{
+ struct fuse_conn *fc = INO_FC(inode);
+ struct fuse_in in = FUSE_IN_INIT;
+ struct fuse_out out = FUSE_OUT_INIT;
+ struct fuse_write_in *arg;
+ size_t argsize;
+ char *buffer;
+
+ argsize = offsetof(struct fuse_write_in, buf) + count;
+ arg = kmalloc(argsize, GFP_KERNEL);
+ if(!arg)
+ return -ENOMEM;
+
+ arg->offset = (page->index << PAGE_CACHE_SHIFT) + offset;
+ arg->size = count;
+ buffer = kmap(page);
+ memcpy(arg->buf, buffer + offset, count);
+ kunmap(page);
+
+ in.h.opcode = FUSE_WRITE;
+ in.h.ino = inode->i_ino;
+ in.argsize = argsize;
+ in.arg = arg;
+ request_send(fc, &in, &out);
+ kfree(arg);
+
+ return out.h.error;
+}
+
+
+static int fuse_writepage(struct page *page)
+{
+ struct inode *inode = page->mapping->host;
+ unsigned count;
+ unsigned long end_index;
+ int err;
+
+ end_index = inode->i_size >> PAGE_CACHE_SHIFT;
+ if(page->index < end_index)
+ count = PAGE_CACHE_SIZE;
+ else {
+ count = inode->i_size & (PAGE_CACHE_SIZE - 1);
+ err = -EIO;
+ if(page->index > end_index || count == 0)
+ goto out;
+
+ }
+ err = write_buffer(inode, page, 0, count);
+ out:
+ UnlockPage(page);
+ return 0;
+}
+
+
+static int fuse_prepare_write(struct file *file, struct page *page,
+ unsigned offset, unsigned to)
+{
+ /* No op */
+ return 0;
+}
+
+static int fuse_commit_write(struct file *file, struct page *page,
+ unsigned offset, unsigned to)
+{
+ int err;
+ struct inode *inode = page->mapping->host;
+
+ err = write_buffer(inode, page, offset, to - offset);
+ if(!err) {
+ loff_t pos = (page->index << PAGE_CACHE_SHIFT) + to;
+ if(pos > inode->i_size)
+ inode->i_size = pos;
+ }
+ return err;
+}
+
static struct file_operations fuse_file_operations = {
open: fuse_open,
read: generic_file_read,
+ write: generic_file_write,
+ mmap: generic_file_mmap,
};
static struct address_space_operations fuse_file_aops = {
readpage: fuse_readpage,
+ writepage: fuse_writepage,
+ prepare_write: fuse_prepare_write,
+ commit_write: fuse_commit_write,
};
void fuse_init_file_inode(struct inode *inode)
diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h
index aaa395d..b738bed 100644
--- a/kernel/fuse_i.h
+++ b/kernel/fuse_i.h
@@ -10,6 +10,7 @@
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/spinlock.h>
+#include <linux/rwsem.h>
#define FUSE_VERSION "0.1"
@@ -38,15 +39,6 @@ struct fuse_conn {
/** The list of requests being processed */
struct list_head processing;
- /** The number of cleared inodes */
- unsigned int numcleared;
-
- /** The array of cleared inode numbers */
- unsigned long *cleared;
-
- /** Connnection number (for debuging) */
- int id;
-
/** The request id */
int reqctr;
};
@@ -70,8 +62,7 @@ struct fuse_req {
/** The request input */
char *in;
- /** The maximum request output size, if zero, then the request is
- asynchronous */
+ /** The maximum request output size */
unsigned int outsize;
/** The request output */
@@ -98,7 +89,7 @@ struct fuse_out {
void *arg;
};
-#define FUSE_IN_INIT { {0, 0, 0}, 0, 0 }
+#define FUSE_IN_INIT { {0, 0, 0, current->fsuid, current->fsgid}, 0, 0 }
#define FUSE_OUT_INIT { {0, 0}, 0, 0, 0 }
@@ -117,7 +108,7 @@ extern spinlock_t fuse_lock;
* Get a filled in inode
*/
struct inode *fuse_iget(struct super_block *sb, ino_t ino,
- struct fuse_attr *attr);
+ struct fuse_attr *attr, int version);
/**
diff --git a/kernel/inode.c b/kernel/inode.c
index ed4f760..77e8469 100644
--- a/kernel/inode.c
+++ b/kernel/inode.c
@@ -21,70 +21,22 @@ static void fuse_read_inode(struct inode *inode)
/* No op */
}
-static void send_forget(struct fuse_conn *fc, unsigned long *forget,
- unsigned int numforget)
+static void fuse_clear_inode(struct inode *inode)
{
+ struct fuse_conn *fc = INO_FC(inode);
struct fuse_in in = FUSE_IN_INIT;
+ struct fuse_forget_in arg;
+
+ arg.version = inode->i_version;
in.h.opcode = FUSE_FORGET;
- in.h.ino = 0;
- in.argsize = numforget * sizeof(unsigned long);
- in.arg = forget;
+ in.h.ino = inode->i_ino;
+ in.argsize = sizeof(arg);
+ in.arg = &arg;
request_send(fc, &in, NULL);
}
-static int alloc_cleared(struct fuse_conn *fc)
-{
- unsigned long *tmp;
-
- spin_unlock(&fuse_lock);
- tmp = kmalloc(sizeof(unsigned long) * MAX_CLEARED, GFP_NOFS);
- spin_lock(&fuse_lock);
-
- if(!fc->file || fc->cleared != NULL)
- kfree(tmp);
- else if(!tmp)
- printk("fuse_clear_inode: Cannot allocate memory\n");
- else
- fc->cleared = tmp;
-
- return fc->cleared != NULL;
-}
-
-static unsigned long *add_cleared(struct fuse_conn *fc, unsigned long ino)
-{
- if(!fc->file || (!fc->cleared && !alloc_cleared(fc)))
- return NULL;
-
- fc->cleared[fc->numcleared] = ino;
- fc->numcleared ++;
-
- if(fc->numcleared == MAX_CLEARED) {
- unsigned long *tmp = fc->cleared;
- fc->cleared = NULL;
- fc->numcleared = 0;
- return tmp;
- }
-
- return NULL;
-}
-
-static void fuse_clear_inode(struct inode *inode)
-{
- struct fuse_conn *fc = INO_FC(inode);
- unsigned long *forget;
-
- spin_lock(&fuse_lock);
- forget = add_cleared(fc, inode->i_ino);
- spin_unlock(&fuse_lock);
-
- if(forget) {
- send_forget(fc, forget, MAX_CLEARED);
- kfree(forget);
- }
-}
-
static void fuse_put_super(struct super_block *sb)
{
struct fuse_conn *fc = sb->u.generic_sbp;
@@ -143,7 +95,7 @@ static struct inode *get_root_inode(struct super_block *sb)
memset(&attr, 0, sizeof(attr));
attr.mode = S_IFDIR;
- return fuse_iget(sb, 1, &attr);
+ return fuse_iget(sb, 1, &attr, 0);
}
static struct super_block *fuse_read_super(struct super_block *sb,
@@ -169,8 +121,7 @@ static struct super_block *fuse_read_super(struct super_block *sb,
goto err;
if(fc->sb != NULL) {
- printk("fuse_read_super: connection %i already mounted\n",
- fc->id);
+ printk("fuse_read_super: connection already mounted\n");
goto err;
}
@@ -213,6 +164,5 @@ void fuse_fs_cleanup()
* Local Variables:
* indent-tabs-mode: t
* c-basic-offset: 8
- * End:
- */
+ * End: */
diff --git a/kernel/redir_hack.c b/kernel/redir_hack.c
new file mode 100644
index 0000000..4c91a4f
--- /dev/null
+++ b/kernel/redir_hack.c
@@ -0,0 +1,787 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/unistd.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/smp_lock.h>
+#include <asm/uaccess.h>
+#include <linux/coda_psdev.h>
+#include <linux/version.h>
+
+#if 0
+#define DEB(X) printk X
+#else
+#define DEB(X)
+#endif
+
+#define REDIR_VERSION "0.3"
+
+extern void *sys_call_table[];
+
+typedef asmlinkage int (*chdir_func) (const char *);
+typedef asmlinkage int (*stat_func) (const char *, struct stat *);
+typedef asmlinkage int (*access_func) (const char *, int);
+typedef asmlinkage int (*open_func) (const char *, int, int);
+typedef asmlinkage int (*readlink_func) (const char *, char *, int);
+typedef asmlinkage int (*getcwd_func) (char *, unsigned long);
+
+static chdir_func orig_chdir;
+static stat_func orig_stat;
+static stat_func orig_lstat;
+static access_func orig_access;
+static open_func orig_open;
+static readlink_func orig_readlink;
+static getcwd_func orig_getcwd;
+
+typedef asmlinkage long (*stat64_func) (const char *, struct stat64 *, long);
+
+static stat64_func orig_stat64;
+static stat64_func orig_lstat64;
+
+#ifdef __i386__
+typedef asmlinkage int (*execve_func) (struct pt_regs);
+
+static execve_func orig_execve;
+#endif
+
+#define AVFS_MAGIC_CHAR '#'
+
+#define PF_AVFS 0x00008000
+
+#define OVERLAY_BASE "/mnt/tmp"
+
+#define path_ok(pwd) (pwd->d_parent == pwd || !list_empty(&pwd->d_hash))
+
+static char *path_pwd(char *page)
+{
+ return d_path(current->fs->pwd, current->fs->pwdmnt, page, PAGE_SIZE);
+}
+
+static int a_path_walk(const char *pathname, int flags, struct nameidata *nd)
+{
+ int error;
+
+ error = 0;
+ if (path_init(pathname, flags, nd))
+ error = path_walk(pathname, nd);
+
+ return error;
+}
+
+static void a_path_release(struct nameidata *nd)
+{
+ dput(nd->dentry);
+ mntput(nd->mnt);
+}
+
+static char *resolv_virt(const char *pathname, int must_exist, int flags)
+{
+ struct nameidata root;
+ struct nameidata nd;
+ struct dentry *origroot;
+ struct vfsmount *origrootmnt;
+ char *newpathname = NULL;
+ char *page = NULL;
+ char *path = NULL;
+ int pathlen = 0;
+ int error;
+ int newflags;
+ char overlay_dir[128];
+ unsigned overlay_dir_len;
+
+ sprintf(overlay_dir, "%s/%u", OVERLAY_BASE, current->fsuid);
+ overlay_dir_len = strlen(overlay_dir);
+
+ lock_kernel();
+
+ DEB((KERN_INFO "resolve_virt pathname: '%s'\n",
+ pathname ? pathname : "(null)"));
+
+ error = a_path_walk(overlay_dir, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &root);
+ if(error)
+ goto out;
+
+ origroot = current->fs->root;
+ origrootmnt = current->fs->rootmnt;
+
+ current->fs->root = root.dentry;
+ current->fs->rootmnt = root.mnt;
+
+ newflags = flags;
+ if(must_exist)
+ newflags |= LOOKUP_POSITIVE;
+
+ error = a_path_walk(pathname, newflags, &nd);
+ if(!error) {
+ if(path_ok(nd.dentry)) {
+ page = (char *) __get_free_page(GFP_USER);
+ if(page) {
+ path = d_path(nd.dentry, nd.mnt, page,
+ PAGE_SIZE);
+ DEB((KERN_INFO "resolve_virt path = '%s'\n",
+ path));
+ pathlen = (unsigned int) page + PAGE_SIZE -
+ (unsigned int) path;
+ }
+ }
+ a_path_release(&nd);
+ }
+
+ current->fs->root = origroot;
+ current->fs->rootmnt = origrootmnt;
+
+ a_path_release(&root);
+
+ if(path) {
+ int isvirtual;
+
+ error = a_path_walk(path, flags, &nd);
+ if(!error) {
+ if(nd.dentry->d_inode)
+ isvirtual = 0;
+ else if(must_exist)
+ isvirtual = 1;
+ else if(strchr(path, AVFS_MAGIC_CHAR))
+ isvirtual = 1;
+ else
+ isvirtual = 0;
+
+ a_path_release(&nd);
+ }
+ else {
+ isvirtual = 1;
+ }
+
+ if(!isvirtual) {
+ newpathname = kmalloc(pathlen + 1, GFP_USER);
+ if(newpathname)
+ strncpy(newpathname, path, pathlen);
+ }
+ else {
+ newpathname = kmalloc(overlay_dir_len + pathlen + 1,
+ GFP_USER);
+
+ if(newpathname) {
+ strcpy(newpathname, overlay_dir);
+ strncat(newpathname, path, pathlen);
+ }
+ }
+ }
+
+ if(page)
+ free_page((unsigned long) page);
+
+
+ DEB((KERN_INFO "resolve_virt newpathname: '%s'\n",
+ newpathname ? newpathname : "(null)"));
+
+ out:
+ unlock_kernel();
+ return newpathname;
+}
+
+
+#define FUSE_SUPER_MAGIC 0x65735546
+
+#define cwd_virtual() \
+ (current->fs->pwd->d_sb->s_magic == FUSE_SUPER_MAGIC)
+
+static char *get_abs_path(const char *filename)
+{
+ char *cwd;
+ int cwdlen, fnamelen;
+ char *abspath, *s;
+ char *page;
+ char overlay_dir[128];
+ unsigned overlay_dir_len;
+
+ sprintf(overlay_dir, "/mnt/avfs/%010u", current->fsuid);
+ overlay_dir_len = strlen(overlay_dir);
+
+ if(!path_ok(current->fs->pwd))
+ return NULL;
+
+ page = (char *) __get_free_page(GFP_USER);
+ if(!page)
+ return NULL;
+
+ cwd = path_pwd(page);
+ cwdlen = (unsigned int) page + PAGE_SIZE - (unsigned int) cwd - 1;
+ if(cwd_virtual() && cwdlen > overlay_dir_len) {
+ cwd += overlay_dir_len;
+ cwdlen -= overlay_dir_len;
+ }
+
+
+ fnamelen = strlen(filename);
+
+ abspath = kmalloc(cwdlen + 1 + fnamelen + 1, GFP_USER);
+ if(abspath) {
+ s = abspath;
+ strncpy(s, cwd, cwdlen);
+ s += cwdlen;
+ *s++ = '/';
+ strncpy(s, filename, fnamelen + 1);
+ }
+ free_page((unsigned long) page);
+
+ return abspath;
+}
+
+static char *resolve_name(const char *kfilename, int must_exist, int flags)
+{
+ char *tmp;
+ char *newfilename;
+
+ tmp = getname(kfilename);
+ if(IS_ERR(tmp))
+ return tmp;
+
+
+ if((tmp[0] != '/' && cwd_virtual()) || strchr(tmp, AVFS_MAGIC_CHAR)) {
+ DEB((KERN_INFO "resolve_name: %s (%i/%s)\n", tmp,
+ current->pid,
+ (current->flags & PF_AVFS) ? "on" : "off"));
+
+ if(strcmp(tmp, "/#avfs-on") == 0) {
+ printk(KERN_INFO "AVFS ON (pid: %i)\n",
+ current->pid);
+ current->flags |= PF_AVFS;
+ newfilename = ERR_PTR(-EEXIST);
+ }
+ else if(!(current->flags & PF_AVFS))
+ newfilename = NULL;
+ else if(strcmp(tmp, "/#avfs-off") == 0) {
+ printk(KERN_INFO "AVFS OFF (pid: %i)\n",
+ current->pid);
+ current->flags &= ~PF_AVFS;
+ newfilename = ERR_PTR(-EEXIST);
+ }
+ else {
+ if(tmp[0] == '/') {
+ newfilename = resolv_virt(tmp, must_exist, flags);
+ }
+ else {
+ char *abspath;
+
+ abspath = get_abs_path(tmp);
+ if(abspath) {
+ newfilename = resolv_virt(abspath, must_exist, flags);
+ kfree(abspath);
+ }
+ else
+ newfilename = NULL;
+ }
+ }
+ }
+ else
+ newfilename = NULL;
+
+ putname(tmp);
+
+ return newfilename;
+}
+
+asmlinkage int virt_chdir(const char *filename)
+{
+ int ret;
+ mm_segment_t old_fs;
+ char *newfilename;
+
+ if(!cwd_virtual()) {
+ ret = (*orig_chdir)(filename);
+ if(ret != -ENOENT)
+ return ret;
+ }
+ else
+ ret = 0;
+
+ newfilename = resolve_name(filename, 1, 1);
+ if(!newfilename) {
+ if(ret)
+ return ret;
+ else
+ return (*orig_chdir)(filename);
+ }
+ if(IS_ERR(newfilename))
+ return PTR_ERR(newfilename);
+
+
+ DEB((KERN_INFO "CHDIR: trying '%s'\n", newfilename));
+
+ old_fs = get_fs();
+ set_fs(get_ds());
+ ret = (*orig_chdir)(newfilename);
+ set_fs(old_fs);
+ kfree(newfilename);
+
+ DEB((KERN_INFO "CHDIR: result %i\n", ret));
+
+ return ret;
+}
+
+static int do_orig_stat(stat_func sfunc, const char *filename,
+ struct stat *statbuf)
+{
+ int ret;
+ mm_segment_t old_fs;
+ struct stat locbuf;
+
+ old_fs = get_fs();
+ set_fs(get_ds());
+ ret = (*sfunc)(filename, &locbuf);
+ set_fs(old_fs);
+
+ if(ret == 0)
+ ret = (copy_to_user(statbuf, &locbuf, sizeof(locbuf)) ?
+ -EFAULT : 0);
+
+ return ret;
+}
+
+asmlinkage int virt_stat(const char *filename, struct stat *statbuf)
+{
+ int ret;
+ char *newfilename;
+
+ if(!cwd_virtual()) {
+ ret = (*orig_stat)(filename, statbuf);
+ if(ret != -ENOENT)
+ return ret;
+ }
+ else
+ ret = 0;
+
+ newfilename = resolve_name(filename, 1, 1);
+ if(!newfilename) {
+ if(ret)
+ return ret;
+ else
+ return (*orig_stat)(filename, statbuf);
+ }
+ if(IS_ERR(newfilename))
+ return PTR_ERR(newfilename);
+
+ DEB((KERN_INFO "STAT: trying '%s'\n", newfilename));
+
+ ret = do_orig_stat(orig_stat, newfilename, statbuf);
+ kfree(newfilename);
+
+ DEB((KERN_INFO "STAT: result %i\n", ret));
+
+ return ret;
+}
+
+asmlinkage int virt_lstat(const char *filename, struct stat *statbuf)
+{
+ int ret;
+ char *newfilename;
+
+ if(!cwd_virtual()) {
+ ret = (*orig_lstat)(filename, statbuf);
+ if(ret != -ENOENT)
+ return ret;
+ }
+ else
+ ret = 0;
+
+ newfilename = resolve_name(filename, 1, 0);
+ if(!newfilename) {
+ if(ret)
+ return ret;
+ else
+ return (*orig_lstat)(filename, statbuf);
+ }
+ if(IS_ERR(newfilename))
+ return PTR_ERR(newfilename);
+
+ DEB((KERN_INFO "LSTAT: trying '%s'\n", newfilename));
+
+ ret = do_orig_stat(orig_lstat, newfilename, statbuf);
+ kfree(newfilename);
+
+ DEB((KERN_INFO "LSTAT: result %i\n", ret));
+
+ return ret;
+}
+
+
+asmlinkage int virt_access(const char *filename, int mode)
+{
+ int ret;
+ mm_segment_t old_fs;
+ char *newfilename;
+
+ if(!cwd_virtual()) {
+ ret = (*orig_access)(filename, mode);
+ if(ret != -ENOENT)
+ return ret;
+ }
+ else
+ ret = 0;
+
+ newfilename = resolve_name(filename, 1, 1);
+ if(!newfilename) {
+ if(ret)
+ return ret;
+ else
+ return (*orig_access)(filename, mode);
+ }
+ if(IS_ERR(newfilename))
+ return PTR_ERR(newfilename);
+
+ DEB((KERN_INFO "ACCESS: trying '%s'\n", newfilename));
+
+ old_fs = get_fs();
+ set_fs(get_ds());
+ ret = (*orig_access)(newfilename, mode);
+ set_fs(old_fs);
+ kfree(newfilename);
+
+ DEB((KERN_INFO "ACCESS: result %i\n", ret));
+
+ return ret;
+}
+
+asmlinkage int virt_open(const char *filename, int flags, int mode)
+{
+ int ret;
+ mm_segment_t old_fs;
+ char *newfilename;
+
+ if(!cwd_virtual()) {
+ ret = (*orig_open)(filename, flags, mode);
+ if(ret != -ENOENT)
+ return ret;
+ }
+ else
+ ret = 0;
+
+ newfilename = resolve_name(filename, 1, 1);
+ if(!newfilename) {
+ if(ret)
+ return ret;
+ else
+ return (*orig_open)(filename, flags, mode);
+ }
+ if(IS_ERR(newfilename))
+ return PTR_ERR(newfilename);
+
+ DEB((KERN_INFO "OPEN: trying '%s'\n", newfilename));
+
+ old_fs = get_fs();
+ set_fs(get_ds());
+ ret = (*orig_open)(newfilename, flags, mode);
+ set_fs(old_fs);
+ kfree(newfilename);
+
+ DEB((KERN_INFO "OPEN: result %i\n", ret));
+
+ return ret;
+}
+
+asmlinkage int virt_readlink(const char *filename, char *buf, int bufsiz)
+{
+ int ret;
+ mm_segment_t old_fs;
+ char *newfilename;
+ char *locbuf;
+ int len;
+
+ if(!cwd_virtual()) {
+ ret = (*orig_readlink)(filename, buf, bufsiz);
+ if(ret != -ENOENT)
+ return ret;
+ }
+ else
+ ret = 0;
+
+ newfilename = resolve_name(filename, 1, 0);
+ if(!newfilename) {
+ if(ret)
+ return ret;
+ else
+ return (*orig_readlink)(filename, buf, bufsiz);
+ }
+ if(IS_ERR(newfilename))
+ return PTR_ERR(newfilename);
+
+ DEB((KERN_INFO "READLINK: trying '%s'\n", newfilename));
+
+ /* bufsiz is legal (already checked by sys_readlink) */
+ len = bufsiz;
+ if(bufsiz > PAGE_SIZE)
+ len = PAGE_SIZE;
+
+ locbuf = (char *) __get_free_page(GFP_USER);
+
+ ret = -ENOMEM;
+ if(locbuf) {
+ old_fs = get_fs();
+ set_fs(get_ds());
+ ret = (*orig_readlink)(newfilename, locbuf, len);
+ set_fs(old_fs);
+
+ if(ret >= 0)
+ if(copy_to_user(buf, locbuf, len))
+ ret = -EFAULT;
+ free_page((unsigned long) locbuf);
+ }
+ kfree(newfilename);
+
+ DEB((KERN_INFO "READLINK: result %i\n", ret));
+
+ return ret;
+}
+
+asmlinkage int virt_getcwd(char *buf, unsigned long size)
+{
+ int ret;
+ char *cwd;
+ unsigned long cwdlen;
+ char *page;
+ char *newcwd;
+ unsigned long newlen;
+ char overlay_dir[128];
+ unsigned overlay_dir_len;
+
+ ret = (*orig_getcwd)(buf, size);
+
+ if(!cwd_virtual() || ret < 0)
+ return ret;
+
+ if(!path_ok(current->fs->pwd))
+ return -ENOENT;
+
+ page = (char *) __get_free_page(GFP_USER);
+ if(!page)
+ return -ENOMEM;
+
+ cwd = path_pwd(page);
+ cwdlen = PAGE_SIZE + (page - cwd) - 1;
+
+ sprintf(overlay_dir, "/mnt/avfs/%010u", current->fsuid);
+ overlay_dir_len = strlen(overlay_dir);
+
+ if(cwdlen >= overlay_dir_len &&
+ strncmp(cwd, overlay_dir, overlay_dir_len) == 0) {
+ if(cwdlen == overlay_dir_len) {
+ newcwd = "/";
+ newlen = 1;
+ }
+ else {
+ newcwd = cwd + overlay_dir_len;
+ newlen = cwdlen - overlay_dir_len;
+ }
+
+ ret = -ERANGE;
+ if(newlen + 1 <= size) {
+ ret = newlen + 1;
+ if(copy_to_user(buf, newcwd, newlen + 1))
+ ret = -EFAULT;
+ }
+ }
+ free_page((unsigned long) page);
+
+ return ret;
+}
+
+
+static long do_orig_stat64(stat64_func sfunc, const char *filename,
+ struct stat64 * statbuf, long flags)
+{
+ long ret;
+ mm_segment_t old_fs;
+ struct stat64 locbuf;
+
+ old_fs = get_fs();
+ set_fs(get_ds());
+ ret = (*sfunc)(filename, &locbuf, flags);
+ set_fs(old_fs);
+
+ if(ret == 0)
+ ret = (copy_to_user(statbuf, &locbuf, sizeof(locbuf)) ?
+ -EFAULT : 0);
+
+ return ret;
+}
+
+asmlinkage long virt_stat64(char * filename, struct stat64 * statbuf, long flags)
+{
+ long ret;
+ char *newfilename;
+
+ if(!cwd_virtual()) {
+ ret = (*orig_stat64)(filename, statbuf, flags);
+ if(ret != -ENOENT)
+ return ret;
+ }
+ else
+ ret = 0;
+
+ newfilename = resolve_name(filename, 1, 1);
+ if(!newfilename) {
+ if(ret)
+ return ret;
+ else
+ return (*orig_stat64)(filename, statbuf, flags);
+ }
+ if(IS_ERR(newfilename))
+ return PTR_ERR(newfilename);
+
+ DEB((KERN_INFO "STAT64: trying '%s'\n", newfilename));
+
+ ret = do_orig_stat64(orig_stat64, newfilename, statbuf, flags);
+ kfree(newfilename);
+
+ DEB((KERN_INFO "STAT64: result %li\n", ret));
+
+ return ret;
+}
+
+asmlinkage long virt_lstat64(char * filename, struct stat64 * statbuf, long flags)
+{
+ long ret;
+ char *newfilename;
+
+ if(!cwd_virtual()) {
+ ret = (*orig_lstat64)(filename, statbuf, flags);
+ if(ret != -ENOENT)
+ return ret;
+ }
+ else
+ ret = 0;
+
+ newfilename = resolve_name(filename, 1, 0);
+ if(!newfilename) {
+ if(ret)
+ return ret;
+ else
+ return (*orig_lstat64)(filename, statbuf, flags);
+ }
+ if(IS_ERR(newfilename))
+ return PTR_ERR(newfilename);
+
+ DEB((KERN_INFO "LSTAT64: trying '%s'\n", newfilename));
+
+ ret = do_orig_stat64(orig_lstat64, newfilename, statbuf, flags);
+ kfree(newfilename);
+
+ DEB((KERN_INFO "LSTAT64: result %li\n", ret));
+
+ return ret;
+}
+
+#ifdef __i386__
+
+asmlinkage int real_execve(struct pt_regs *regs)
+{
+ int error;
+ char * filename;
+
+ filename = getname((char *) regs->ebx);
+ error = PTR_ERR(filename);
+ if (IS_ERR(filename))
+ goto out;
+ error = do_execve(filename, (char **) regs->ecx, (char **) regs->edx, regs);
+ if (error == 0)
+ current->ptrace &= ~PT_DTRACE;
+ putname(filename);
+
+out:
+ return error;
+}
+
+asmlinkage int virt_execve(struct pt_regs regs)
+{
+ int ret;
+ char *newfilename;
+ char *filename = (char *) regs.ebx;
+
+ if(!cwd_virtual()) {
+ ret = real_execve(&regs);
+ if(ret != -ENOENT)
+ return ret;
+ }
+ else
+ ret = 0;
+
+ newfilename = resolve_name(filename, 1, 1);
+ if(!newfilename) {
+ if(ret)
+ return ret;
+ else
+ return real_execve(&regs);
+ }
+ if(IS_ERR(newfilename))
+ return PTR_ERR(newfilename);
+
+ DEB((KERN_INFO "EXECVE: trying '%s'\n", newfilename));
+
+ ret = do_execve(newfilename, (char **) regs.ecx, (char **) regs.edx,
+ &regs);
+ if (ret == 0)
+ current->ptrace &= ~PT_DTRACE;
+ kfree(newfilename);
+
+ DEB((KERN_INFO "EXECVE: result %i\n", ret));
+
+ return ret;
+}
+#endif /* __i386__ */
+
+void *replace_syscall(int index, void *new_syscall)
+{
+ void *orig_syscall = sys_call_table[index];
+
+ printk(KERN_INFO "replacing syscall nr. %3i [%p] with [%p]\n",
+ index, orig_syscall, new_syscall);
+ sys_call_table[index] = new_syscall;
+
+ return orig_syscall;
+}
+
+int init_module(void)
+{
+ printk(KERN_INFO "redir init (version %s)\n", REDIR_VERSION);
+
+ orig_chdir = replace_syscall(__NR_chdir, virt_chdir);
+ orig_stat = replace_syscall(__NR_stat, virt_stat);
+ orig_lstat = replace_syscall(__NR_lstat, virt_lstat);
+ orig_access = replace_syscall(__NR_access, virt_access);
+ orig_open = replace_syscall(__NR_open, virt_open);
+ orig_readlink = replace_syscall(__NR_readlink, virt_readlink);
+ orig_getcwd = replace_syscall(__NR_getcwd, virt_getcwd);
+
+ orig_stat64 = replace_syscall(__NR_stat64, virt_stat64);
+ orig_lstat64 = replace_syscall(__NR_lstat64, virt_lstat64);
+
+#ifdef __i386__
+ orig_execve = replace_syscall(__NR_execve, virt_execve);
+#endif
+
+ return 0;
+}
+
+
+void cleanup_module(void)
+{
+ printk(KERN_INFO "redir cleanup\n");
+
+ replace_syscall(__NR_chdir, orig_chdir);
+ replace_syscall(__NR_stat, orig_stat);
+ replace_syscall(__NR_lstat, orig_lstat);
+ replace_syscall(__NR_access, orig_access);
+ replace_syscall(__NR_open, orig_open);
+ replace_syscall(__NR_readlink, orig_readlink);
+ replace_syscall(__NR_getcwd, orig_getcwd);
+
+ replace_syscall(__NR_stat64, orig_stat64);
+ replace_syscall(__NR_lstat64, orig_lstat64);
+
+#ifdef __i386__
+ replace_syscall(__NR_execve, orig_execve);
+#endif
+
+}