This commit was manufactured by cvs2svn to create branch 'captive'.
authorjankratochvil <>
Thu, 13 Nov 2003 20:17:24 +0000 (20:17 +0000)
committerjankratochvil <>
Thu, 13 Nov 2003 20:17:24 +0000 (20:17 +0000)
Cherrypick from master 2003-11-13 20:17:21 UTC short 'http://prdownloads.sourceforge.net/lufs/lufs-0.9.7.tar.gz?download':
    kernel/Linux/2.6/Makefile
    kernel/Linux/2.6/dir.c
    kernel/Linux/2.6/file.c
    kernel/Linux/2.6/inode.c
    kernel/Linux/2.6/lufs.h
    kernel/Linux/2.6/proc.c
    kernel/Linux/2.6/proc.h
    kernel/Linux/2.6/symlink.c

kernel/Linux/2.6/Makefile [new file with mode: 0644]
kernel/Linux/2.6/dir.c [new file with mode: 0644]
kernel/Linux/2.6/file.c [new file with mode: 0644]
kernel/Linux/2.6/inode.c [new file with mode: 0644]
kernel/Linux/2.6/lufs.h [new file with mode: 0644]
kernel/Linux/2.6/proc.c [new file with mode: 0644]
kernel/Linux/2.6/proc.h [new file with mode: 0644]
kernel/Linux/2.6/symlink.c [new file with mode: 0644]

diff --git a/kernel/Linux/2.6/Makefile b/kernel/Linux/2.6/Makefile
new file mode 100644 (file)
index 0000000..7dfc663
--- /dev/null
@@ -0,0 +1,12 @@
+ifneq ($(KERNELRELEASE),)
+obj-m          := lufs.o
+lufs-objs      := proc.o inode.o dir.o file.o symlink.o
+
+else
+KDIR=/lib/modules/`uname -r`/build
+PWD=`pwd`
+
+.DEFAULT:
+       $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
+
+endif
diff --git a/kernel/Linux/2.6/dir.c b/kernel/Linux/2.6/dir.c
new file mode 100644 (file)
index 0000000..0835ad8
--- /dev/null
@@ -0,0 +1,582 @@
+/*
+ * dir.c
+ * Copyright (C) 2002 Florin Malita <mali@go.ro>
+ *
+ * This file is part of LUFS, a free userspace filesystem implementation.
+ * See http://lufs.sourceforge.net/ for updates.
+ *
+ * LUFS is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * LUFS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/socket.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/smp_lock.h>
+
+#include "lufs.h"
+#include "proc.h"
+
+
+extern struct inode* lu_iget(struct super_block*, struct lufs_fattr*);
+extern int lufs_notify_change(struct dentry*, struct iattr*);
+
+static int lu_readdir(struct file*, void*, filldir_t);
+
+static struct dentry *lu_lookup(struct inode*, struct dentry*);
+static int lu_mkdir(struct inode*, struct dentry*, int);
+static int lu_create(struct inode*, struct dentry*, int);
+static int lu_rmdir(struct inode*, struct dentry*);
+static int lu_rename(struct inode*, struct dentry*, struct inode*, struct dentry*);
+static int lu_unlink(struct inode*, struct dentry*);
+static int lu_link(struct dentry*, struct inode*, struct dentry*);
+static int lu_symlink(struct inode*, struct dentry*, const char*);
+
+struct file_operations lu_dir_operations = {
+    .read      = generic_read_dir,
+    .readdir   = lu_readdir,
+};
+
+struct inode_operations lu_dir_inode_operations = {
+    .create    = lu_create,
+    .lookup    = lu_lookup,
+    .link      = lu_link,
+    .unlink    = lu_unlink,
+    .symlink   = lu_symlink,
+    .mkdir     = lu_mkdir,
+    .rmdir     = lu_rmdir,
+    .rename    = lu_rename,
+    .setattr   = lufs_notify_change,
+};
+
+static int lu_lookup_validate(struct dentry *dentry, int flags)
+{
+    struct inode *inode = dentry->d_inode;
+    unsigned long age = jiffies - dentry->d_time;
+    int res;
+    
+    TRACE("in\n");
+    
+    res = (age <= LU_MAXAGE);
+    TRACE("age: %lu, valid: %d\n", age, res);
+
+    if(!res)
+       res = (lu_revalidate_inode(dentry) == 0);
+
+    
+    if(inode){
+       lock_kernel();
+
+       if(is_bad_inode(inode))
+           res = 0;
+       unlock_kernel();
+    }else
+       TRACE("no inode?!\n");
+
+    TRACE("out(res=%d)\n", res);
+
+    return res;
+}
+
+static int lu_delete_dentry(struct dentry *dentry)
+{
+    
+    TRACE("in\n");
+    if(dentry->d_inode && is_bad_inode(dentry->d_inode)){
+       WARN("bad inode, unhashing \n");
+       return 1;
+    }
+
+    TRACE("out\n");
+    return 0;
+}
+
+struct dentry_operations lufs_dentry_operations = {
+    .d_revalidate      = lu_lookup_validate,
+    .d_delete          = lu_delete_dentry,
+};
+
+static int lu_readdir(struct file *f, void *dirent, filldir_t filldir)
+{
+    int res = -1;
+    char *c;
+    struct qstr qname;
+    unsigned long ino;
+    struct iovec siov[2], riov;
+    struct server_slot *slot;
+    unsigned short offset;
+    
+    TRACE("in\n");
+    
+    if((slot = lu_getslot(GET_INFO(f->f_dentry->d_sb))) == NULL)
+       return -ERESTARTSYS;
+
+    if(lu_getname(f->f_dentry, slot->s_buf, LU_MAXDATA) < 0){
+       WARN("lu_getname failed!\n");
+       goto out;
+    }
+
+    TRACE("reading %s, offset %u...\n", slot->s_buf, (unsigned)f->f_pos);
+    res = 0;
+    
+    switch((unsigned int)f->f_pos){
+
+    case 0:
+       if(filldir(dirent, ".", 1, 0, f->f_dentry->d_inode->i_ino, DT_DIR) < 0)
+           goto out;
+       f->f_pos++;
+
+    case 1:
+       if(filldir(dirent, "..", 2, 1, f->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0)
+           goto out;
+       f->f_pos++;
+
+    default:
+       offset = f->f_pos;
+       siov[0].iov_base = &offset;
+       siov[0].iov_len = sizeof(unsigned short);
+       siov[1].iov_base = slot->s_buf;
+       siov[1].iov_len = strlen(slot->s_buf) + 1;
+       riov.iov_base = slot->s_buf;
+       riov.iov_len = LU_MAXDATA;
+
+       if((res = lu_execute(GET_INFO(f->f_dentry->d_inode->i_sb), slot, PTYPE_READDIR, siov, 2, &riov, 1)) < 0){
+           WARN("could not read directory content!\n");
+           if(res == -ERESTARTSYS)
+               res = -EINTR;
+           goto out;
+       }
+       if(PIS_ERROR(res)){
+           WARN("server failure!\n");
+           res = PERROR(res);
+           goto out;
+       }
+       for(qname.name = slot->s_buf, c = strchr(slot->s_buf, '\n'); c != NULL; qname.name = c+1, c = strchr(c+1, '\n')){
+           *c = 0;
+           TRACE("direntry: %s.\n", qname.name);
+           qname.len = strlen(qname.name);
+           if((ino = find_inode_number(f->f_dentry, &qname)) == 0)
+               ino = iunique(f->f_dentry->d_sb, 2);
+           if(filldir(dirent, qname.name, qname.len, f->f_pos, ino, DT_UNKNOWN) < 0)
+               break;
+           f->f_pos++;     
+       }
+    }
+
+    TRACE("out\n");
+ out:
+    lu_putslot(slot);
+    return res;
+}
+
+static struct dentry* lu_lookup(struct inode *dir, struct dentry *dentry)
+{
+    int res;
+    struct lufs_fattr fattr;
+    struct iovec siov, riov;
+    struct inode *inode;
+    struct server_slot *slot;
+
+    TRACE("in\n");
+
+    if((slot = lu_getslot(GET_INFO(dir->i_sb))) == NULL)
+       return ERR_PTR(-ERESTARTSYS);
+    
+    if((res = lu_getname(dentry, slot->s_buf, LU_MAXDATA)) < 0){
+       WARN("lu_getname failed!\n");
+       goto out;
+    }
+
+    TRACE("looking up %s\n", slot->s_buf);
+    
+    siov.iov_base = slot->s_buf;
+    siov.iov_len = strlen(slot->s_buf) + 1;
+    riov.iov_base = &fattr;
+    riov.iov_len = sizeof(struct lufs_fattr);
+
+    if((res = lu_execute(GET_INFO(dir->i_sb), slot, PTYPE_STAT, &siov, 1, &riov, 1)) < 0)
+       goto out;
+
+    if(PIS_ERROR(res)){
+       TRACE("File not found...\n");
+       dentry->d_op = &lufs_dentry_operations;
+       dentry->d_time = jiffies;
+       d_add(dentry, NULL);
+       lu_putslot(slot);
+       return NULL;
+    }
+
+    lu_fixattrs(GET_INFO(dir->i_sb), &fattr);
+
+    if(dentry == dentry->d_parent)
+       fattr.f_ino = 2;
+    else 
+       fattr.f_ino = iunique(dentry->d_sb, 2);
+
+    if((inode = lu_iget(dir->i_sb, &fattr))){
+       dentry->d_op = &lufs_dentry_operations;
+       dentry->d_time = jiffies;
+       d_add(dentry, inode);
+    }
+    res = 0;
+
+ out:
+    lu_putslot(slot);
+
+    TRACE("out\n");
+    return ERR_PTR(res);
+}
+
+static int lu_instantiate(struct dentry *dentry, char *name, struct server_slot *slot)
+{
+    int res;
+    struct lufs_fattr fattr;
+    struct iovec siov, riov;
+    struct inode *inode;
+
+    TRACE("in\n");
+
+    TRACE("instantiating %s\n", name);
+    
+    siov.iov_base = name;
+    siov.iov_len = strlen(name) + 1;
+    riov.iov_base = &fattr;
+    riov.iov_len = sizeof(struct lufs_fattr);
+
+    if((res = lu_execute(GET_INFO(dentry->d_sb), slot, PTYPE_STAT, &siov, 1, &riov, 1)) < 0)
+       goto out;
+
+    if(PIS_ERROR(res)){
+       TRACE("File not found...\n");
+       res = PERROR(res);
+       goto out;
+    }
+
+    lu_fixattrs(GET_INFO(dentry->d_sb), &fattr);
+
+    fattr.f_ino = iunique(dentry->d_sb, 2);
+    inode = lu_iget(dentry->d_sb, &fattr);
+
+    if(!inode){
+       res = -EACCES;
+       goto out;
+    }
+
+    d_instantiate(dentry, inode);
+    res = 0;
+
+  out:
+    TRACE("out\n");
+    return res;
+}
+
+static int lu_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+    int res;
+    struct server_slot *slot;
+    struct iovec iov[2];
+
+    TRACE("in\n");
+
+    if((slot = lu_getslot(GET_INFO(dentry->d_sb))) == NULL)
+       return -ERESTARTSYS;
+
+    if((res = lu_getname(dentry, slot->s_buf, LU_MAXDATA)) < 0){
+       WARN("lu_getname failed!\n");
+       goto out;
+    }
+    
+    iov[0].iov_base = &mode;
+    iov[0].iov_len = sizeof(mode);
+    iov[1].iov_base = slot->s_buf;
+    iov[1].iov_len = strlen(slot->s_buf) + 1;
+
+    if((res = lu_execute(GET_INFO(dentry->d_sb), slot, PTYPE_MKDIR, iov, 2, NULL, 0)) < 0)
+       goto out;
+
+    if(PIS_ERROR(res)){
+       TRACE("Could not create directory.\n");
+       res = PERROR(res);
+       goto out;
+    }
+
+    res = lu_instantiate(dentry, slot->s_buf, slot);
+
+  out:
+    lu_putslot(slot);
+    
+    TRACE("out\n");
+    return res;
+}
+
+static int lu_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+    int res;
+    struct server_slot *slot;
+    struct iovec iov[2];
+
+    TRACE("in\n");
+
+    if((slot = lu_getslot(GET_INFO(dentry->d_sb))) == NULL)
+       return -ERESTARTSYS;
+
+    if((res = lu_getname(dentry, slot->s_buf, LU_MAXDATA)) < 0){
+       WARN("lu_getname failed!\n");
+       goto out;
+    }
+    
+    iov[0].iov_base = &mode;
+    iov[0].iov_len = sizeof(mode);
+    iov[1].iov_base = slot->s_buf;
+    iov[1].iov_len = strlen(slot->s_buf) + 1;
+
+    if((res = lu_execute(GET_INFO(dentry->d_sb), slot, PTYPE_CREATE, iov, 2, NULL, 0)) < 0)
+       goto out;
+
+    if(PIS_ERROR(res)){
+       TRACE("Could not create file.\n");
+       res = PERROR(res);
+       goto out;
+    }
+
+    res = lu_instantiate(dentry, slot->s_buf, slot);
+    
+  out:
+    lu_putslot(slot);
+
+    TRACE("out\n");
+    return res;
+}
+
+static int lu_rmdir(struct inode *dir, struct dentry *dentry)
+{
+    int res;
+    struct server_slot *slot;
+    struct iovec iov;
+
+    if(!d_unhashed(dentry))
+       return -EBUSY;    
+
+    TRACE("in\n");
+    
+    if((slot = lu_getslot(GET_INFO(dentry->d_sb))) == NULL)
+       return -ERESTARTSYS;
+
+    if((res = lu_getname(dentry, slot->s_buf, LU_MAXDATA)) < 0){
+       WARN("lu_getname failed!");
+       goto out;
+    }
+    
+    iov.iov_base = slot->s_buf;
+    iov.iov_len = strlen(slot->s_buf) + 1;
+
+    if((res = lu_execute(GET_INFO(dentry->d_sb), slot, PTYPE_RMDIR, &iov, 1, NULL, 0)) < 0)
+       goto out;
+
+    if(PIS_ERROR(res)){
+       TRACE("rmdir failed!\n");
+       res = PERROR(res);
+       goto out;
+    }
+    res = 0;
+
+  out:
+    lu_putslot(slot);
+
+    TRACE("out\n");
+    return res;
+}
+
+static int lu_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry)
+{
+    struct server_slot *slot;
+    int res;
+    struct iovec iov[2];
+
+    TRACE("in\n");
+
+    if((slot = lu_getslot(GET_INFO(old_dentry->d_sb))) == NULL)
+       return -ERESTARTSYS;
+
+    if((res = lu_getname(old_dentry, slot->s_buf, LU_MAXPATHLEN)) < 0 ||
+       (res = lu_getname(new_dentry, &(slot->s_buf[LU_MAXPATHLEN]), LU_MAXPATHLEN)) < 0){
+       WARN("lu_getname failed!\n");
+       goto out;
+    }
+
+    iov[0].iov_base = slot->s_buf;
+    iov[0].iov_len = strlen(slot->s_buf) + 1;
+    iov[1].iov_base = &(slot->s_buf[LU_MAXPATHLEN]);
+    iov[1].iov_len = strlen(&(slot->s_buf[LU_MAXPATHLEN])) + 1;
+
+    if((res = lu_execute(GET_INFO(old_dentry->d_sb), slot, PTYPE_RENAME, iov, 2, NULL, 0)) < 0)
+       goto out;
+
+    if(PIS_ERROR(res)){
+       TRACE("rename failed!\n");
+       res = PERROR(res);
+       goto out;
+    }
+    res = 0;
+
+  out:
+    lu_putslot(slot);
+
+    TRACE("out\n");
+    return res;
+}
+
+static int lu_unlink(struct inode *dir, struct dentry *dentry)
+{
+    int res;
+    struct server_slot *slot;
+    struct iovec iov;
+
+    TRACE("in\n");
+
+    if((slot = lu_getslot(GET_INFO(dentry->d_sb))) == NULL)
+       return -ERESTARTSYS;
+
+    if((res = lu_getname(dentry, slot->s_buf, LU_MAXPATHLEN)) < 0){
+       WARN("lu_getname failed!");
+       goto out;
+    }
+    
+    iov.iov_base = slot->s_buf;
+    iov.iov_len = strlen(slot->s_buf) + 1;
+
+    if((res = lu_execute(GET_INFO(dentry->d_sb), slot, PTYPE_UNLINK, &iov, 1, NULL, 0)) < 0)
+       goto out;
+
+    if(PIS_ERROR(res)){
+       TRACE("unlink failed!\n");
+       res = PERROR(res);
+       goto out;
+    }
+    res = 0;
+
+  out:
+    lu_putslot(slot);
+
+    TRACE("out\n");
+    return res;
+}
+
+
+static int lu_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
+{
+    int res;
+    struct server_slot *slot;
+    struct iovec iov[2];
+
+    TRACE("in\n");
+
+    if(S_ISDIR(old_dentry->d_inode->i_mode))
+       return -EPERM;
+
+    if(!(slot = lu_getslot(GET_INFO(old_dentry->d_sb))))
+       return -ERESTARTSYS;
+
+    if((res = lu_getname(old_dentry, slot->s_buf, LU_MAXPATHLEN)) < 0){
+       WARN("lu_getname failed!\n");
+       goto out;
+    }
+
+    if((res = lu_getname(dentry, &slot->s_buf[LU_MAXPATHLEN], LU_MAXPATHLEN)) < 0){
+       WARN("lu_getname failed!\n");
+       goto out;
+    }
+
+    iov[0].iov_base = slot->s_buf;
+    iov[0].iov_len = strlen(slot->s_buf) + 1;
+    iov[1].iov_base = &slot->s_buf[LU_MAXPATHLEN];
+    iov[1].iov_len = strlen(&slot->s_buf[LU_MAXPATHLEN]) + 1;
+
+    d_drop(dentry);
+
+    if((res = lu_execute(GET_INFO(old_dentry->d_sb), slot, PTYPE_LINK, iov, 2, NULL, 0)) < 0)
+       goto out;
+
+    if(PIS_ERROR(res)){
+       TRACE("link failed!\n");
+       res = PERROR(res);
+       goto out;
+    }
+
+    res = 0;
+
+  out:
+    lu_putslot(slot);
+    TRACE("out\n");
+    return res;
+}
+
+static int lu_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
+{
+    int res;
+    struct server_slot *slot;
+    struct iovec iov[2];
+
+    TRACE("in\n");
+    TRACE("symlink: %s\n", symname);
+    
+    if(strlen(symname) > LU_MAXPATHLEN - 1)
+       return -ENAMETOOLONG;
+
+    if(!(slot = lu_getslot(GET_INFO(dentry->d_sb))))
+       return -ERESTARTSYS;
+
+    if((res = lu_getname(dentry, slot->s_buf, LU_MAXPATHLEN)) < 0){
+       WARN("lu_getname failed!\n");
+       goto out;
+    }
+
+    TRACE("fname: %s\n", slot->s_buf);
+
+    strcpy(&slot->s_buf[LU_MAXPATHLEN], symname);
+
+    iov[0].iov_base = slot->s_buf;
+    iov[0].iov_len = strlen(slot->s_buf) + 1;
+    iov[1].iov_base = &slot->s_buf[LU_MAXPATHLEN];
+    iov[1].iov_len = strlen(&slot->s_buf[LU_MAXPATHLEN]) + 1;
+
+    d_drop(dentry);
+
+    if((res = lu_execute(GET_INFO(dentry->d_sb), slot, PTYPE_SYMLINK, iov, 2, NULL, 0)) < 0)
+       goto out;
+
+    if(PIS_ERROR(res)){
+       TRACE("symlink failed!\n");
+       res = PERROR(res);
+       goto out;
+    }
+
+    res = 0;
+
+  out:
+    lu_putslot(slot);
+    TRACE("out\n");
+    return res;
+}
+
+
+
diff --git a/kernel/Linux/2.6/file.c b/kernel/Linux/2.6/file.c
new file mode 100644 (file)
index 0000000..4441ef5
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ * file.c
+ * Copyright (C) 2002 Florin Malita <mali@go.ro>
+ *
+ * This file is part of LUFS, a free userspace filesystem implementation.
+ * See http://lufs.sourceforge.net/ for updates.
+ *
+ * LUFS is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * LUFS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/socket.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/smp_lock.h>
+
+#include "lufs.h"
+#include "proc.h"
+
+extern int lufs_notify_change(struct dentry*, struct iattr*);
+extern int lu_revalidate_inode(struct dentry*);
+
+static int lu_file_open(struct inode *inode, struct file *file)
+{
+    int res, gres;
+    struct server_slot *slot;
+    struct iovec iov[2];
+    unsigned flags;
+
+    TRACE("in\n");
+
+    if((gres = generic_file_open(inode, file)) < 0)
+       return gres;
+
+    TRACE("f_mode: %u, i_mode: %u\n", file->f_mode, inode->i_mode);
+    TRACE("f_flags: %u, i_flags: %u\n", file->f_flags, inode->i_flags);
+
+    if((slot = lu_getslot(GET_INFO(file->f_dentry->d_sb))) == NULL)
+       return gres;
+
+    if((res = lu_getname(file->f_dentry, slot->s_buf, LU_MAXDATA)) < 0){
+       WARN("lu_getname failed!\n");
+       goto out;
+    }
+
+    flags = file->f_flags & O_ACCMODE;
+    iov[0].iov_base = &flags;
+    iov[0].iov_len = sizeof(flags);
+    iov[1].iov_base = slot->s_buf;
+    iov[1].iov_len = strlen(slot->s_buf) + 1;
+
+    lu_execute(GET_INFO(file->f_dentry->d_sb), slot, PTYPE_OPEN, iov, 2, NULL, 0);
+
+out:
+    lu_putslot(slot);
+
+    TRACE("out\n");
+    return gres;
+}
+
+static int lu_file_release(struct inode *inode, struct file *file)
+{
+    int res;
+    struct server_slot *slot;
+    struct iovec iov;
+
+    TRACE("in\n");
+
+    if((slot = lu_getslot(GET_INFO(file->f_dentry->d_sb))) == NULL)
+       return -ERESTARTSYS;
+
+    if((res = lu_getname(file->f_dentry, slot->s_buf, LU_MAXPATHLEN)) < 0){
+       WARN("lu_getname failed!\n");
+       goto out;
+    }
+    
+    iov.iov_base = slot->s_buf;
+    iov.iov_len = strlen(slot->s_buf) + 1;
+
+    if((res = lu_execute(GET_INFO(file->f_dentry->d_sb), slot, PTYPE_RELEASE, &iov, 1, NULL, 0)) < 0)
+       goto out;
+
+    if(PIS_ERROR(res)){
+       TRACE("release failed\n");
+       res = PERROR(res);
+       goto out;
+    }
+    
+    res = 0;
+
+out:
+    lu_putslot(slot);
+
+    TRACE("out\n");
+    return res;
+}
+
+static int lu_file_readpage(struct file *f, struct page *p)
+{
+    int res;
+    struct iovec siov[3], riov;
+    long long offset;
+    unsigned long count;
+    struct server_slot *slot;
+
+    TRACE("in\n");
+
+    if((slot = lu_getslot(GET_INFO(f->f_dentry->d_sb))) == NULL)
+       return -ERESTARTSYS;
+
+    get_page(p);
+
+    if((res = lu_getname(f->f_dentry, slot->s_buf, LU_MAXDATA)) < 0){
+       WARN("lu_getname failed!\n");
+       goto out;
+    }
+
+    offset = p->index << PAGE_CACHE_SHIFT;
+    count = PAGE_SIZE;
+
+    siov[0].iov_base = &offset;
+    siov[0].iov_len = sizeof(offset);
+    siov[1].iov_base = &count;
+    siov[1].iov_len = sizeof(count);
+    siov[2].iov_base = slot->s_buf;
+    siov[2].iov_len = strlen(slot->s_buf) + 1;
+
+    riov.iov_base = page_address(p);
+    riov.iov_len = count;
+
+    if((res = lu_execute(GET_INFO(f->f_dentry->d_sb), slot, PTYPE_READ, siov, 3, &riov, 1)) < 0)
+       goto out;
+
+    if(PIS_ERROR(res)){
+       TRACE("read failed\n");
+       res = PERROR(res);
+       goto out;
+    }
+    
+    flush_dcache_page(p);
+    SetPageUptodate(p);
+    res = 0;
+    
+  out:
+    lu_putslot(slot);
+    unlock_page(p);
+    put_page(p);
+        
+    TRACE("out\n");
+    return res;
+}
+
+static int lu_file_writepage(struct page *p, struct writeback_control *wbc)
+{
+    TRACE("in\n");
+
+    TRACE("out\n");
+    return -1;
+}
+
+static int lu_file_preparewrite(struct file *f, struct page *p, unsigned offset, unsigned to)
+{
+    TRACE("in\n");
+
+    TRACE("out\n");
+
+    return 0;
+}
+
+static int lu_file_commitwrite(struct file *f, struct page *p, unsigned offset, unsigned to)
+{
+    int res;
+    struct server_slot *slot;
+    struct iovec iov[4];
+    char *buf;
+    long long off;
+    unsigned long cnt;
+
+    TRACE("in\n");
+
+    if((slot = lu_getslot(GET_INFO(f->f_dentry->d_sb))) == NULL)
+       return -ERESTARTSYS;
+
+    if((res = lu_getname(f->f_dentry, slot->s_buf, LU_MAXDATA)) < 0){
+       WARN("lu_getname failed!\n");
+       goto out2;
+    }
+
+    lock_kernel();
+
+    buf = kmap(p) + offset;
+    cnt = to - offset;
+    off = offset + (((long long)p->index) << PAGE_CACHE_SHIFT);
+
+    iov[0].iov_base = &off;
+    iov[0].iov_len = sizeof(off);
+    iov[1].iov_base = &cnt;
+    iov[1].iov_len = sizeof(cnt);
+    iov[2].iov_base = slot->s_buf;
+    iov[2].iov_len = strlen(slot->s_buf) + 1;
+    iov[3].iov_base = buf;
+    iov[3].iov_len = cnt;
+
+    TRACE("write %s, offset %Ld, count %d\n", slot->s_buf, off, (int)cnt);
+
+    if((res = lu_execute(GET_INFO(f->f_dentry->d_sb), slot, PTYPE_WRITE, iov, 4, NULL, 0)) < 0)
+       goto out1;
+
+
+    if(PIS_ERROR(res)){
+       TRACE("write failed\n");
+       res = PERROR(res);
+       goto out1;
+    }
+
+    f->f_dentry->d_inode->i_mtime = f->f_dentry->d_inode->i_atime = CURRENT_TIME;
+    if(off + cnt > f->f_dentry->d_inode->i_size)
+       f->f_dentry->d_inode->i_size = off + cnt;
+
+    res = cnt;
+
+  out1:
+    kunmap(p);
+    unlock_kernel();
+  out2:
+    lu_putslot(slot);
+    TRACE("out\n");
+    return res;
+}
+
+static int lu_file_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
+{
+    struct dentry *dentry = filp->f_dentry;
+    int res;
+
+    TRACE("in\n");
+
+    if(!(res = lu_revalidate_inode(dentry)))
+       res = generic_file_read(filp, buf, count, ppos);
+
+    TRACE("out\n");
+    
+    return res;
+}
+
+static int lu_file_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+    struct dentry *dentry = filp->f_dentry;
+    int res;
+
+    TRACE("in\n");
+
+    if(!(res = lu_revalidate_inode(dentry)))
+       res = generic_file_mmap(filp, vma);
+
+    TRACE("out\n");
+
+    return res;
+}
+
+static ssize_t lu_file_write(struct file *filp, const char *buf, size_t count, loff_t *ppos)
+{
+    struct dentry *dentry = filp->f_dentry;
+    ssize_t res;
+
+    TRACE("in\n");
+
+    if(!(res = lu_revalidate_inode(dentry)) && (count > 0))
+       res = generic_file_write(filp, buf, count, ppos);
+
+    TRACE("out\n");
+
+    return res;
+}
+
+static int lu_file_fsync(struct file *filp, struct dentry *dentryp, int datasync)
+{    
+    return 0;
+}
+
+struct file_operations lu_file_operations = {
+    .llseek    = generic_file_llseek,
+    .read      = lu_file_read,
+    .write     = lu_file_write,
+    .mmap      = lu_file_mmap,
+    .open      = lu_file_open,
+    .release   = lu_file_release,
+    .fsync     = lu_file_fsync,
+};
+
+struct inode_operations lu_file_inode_operations = {
+    .setattr   = lufs_notify_change,
+};
+
+struct address_space_operations lu_file_aops = {
+    .readpage          = lu_file_readpage,
+    .writepage         = lu_file_writepage,
+    .prepare_write     = lu_file_preparewrite,
+    .commit_write      = lu_file_commitwrite,
+};
+
+
+
diff --git a/kernel/Linux/2.6/inode.c b/kernel/Linux/2.6/inode.c
new file mode 100644 (file)
index 0000000..ee4f13f
--- /dev/null
@@ -0,0 +1,535 @@
+/*
+ * inode.c
+ * Copyright (C) 2002-2003 Florin Malita <mali@go.ro>
+ *
+ * This file is part of LUFS, a free userspace filesystem implementation.
+ * See http://lufs.sourceforge.net/ for updates.
+ *
+ * LUFS is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * LUFS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/smp_lock.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/socket.h>
+#include <linux/string.h>
+#include <linux/vfs.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include "lufs.h"
+#include "proc.h"
+
+MODULE_AUTHOR("Florin Malita <mali@go.ro>");
+MODULE_DESCRIPTION("Linux Userland Filesystem");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
+
+extern struct file_operations lu_dir_operations, lu_file_operations;
+extern struct inode_operations lu_dir_inode_operations, lu_file_inode_operations, lu_symlink_inode_operations;
+extern struct address_space_operations lu_file_aops;
+extern struct dentry_operations lufs_dentry_operations;
+
+static void lu_delete_inode(struct inode*);
+static void lu_put_super(struct super_block*);
+static int  lu_statfs(struct super_block*, struct statfs*);
+
+static struct super_operations lu_sops = {
+    .drop_inode                = generic_delete_inode,
+    .delete_inode      = lu_delete_inode,
+    .put_super         = lu_put_super,
+    .statfs            = lu_statfs,
+};
+
+
+/*
+ * Ignore unknown options, they're probably for the userspace daemon.
+ */
+static void parse_options(struct lufs_sb_info *server, char *opts)
+{
+    char *p, *q;
+    int len;
+
+    if(!opts)
+       return;
+
+    len = strlen(opts);
+
+    while((p = strsep(&opts, ","))){
+       if(strncmp(p, "server_socket=", 14) == 0){
+           if(strlen(p+14) > UNIX_PATH_MAX)
+               goto ugly_opts;
+           strcpy(server->server_socket, p+14);
+           TRACE("server_socket: %s\n", server->server_socket);
+       }else 
+       if(strncmp(p, "uid=", 4) == 0){
+           if(current->uid)
+               ERROR("only root can use uid option!\n");
+           else{
+               if(strlen(p+4) > 5)
+                   goto ugly_opts;
+               q = p + 4;
+               server->config.uid = simple_strtoul(q, &q, 0);
+               TRACE("uid: %d\n", server->config.uid); 
+           }
+       }else
+       if(strncmp(p, "gid=", 4) == 0){
+           if(current->uid)
+               ERROR("only root can use gid option!\n");
+           else{
+               if(strlen(p+4) > 5)
+                   goto ugly_opts;
+               q = p + 4;
+               server->config.gid = simple_strtoul(q, &q, 0);
+               TRACE("gid: %d\n", server->config.gid); 
+           }
+       }else
+       if(strncmp(p, "fmask=", 6) == 0){
+           if(strlen(p + 6) > 3)
+               goto ugly_opts;
+           q = p + 6;
+           server->config.fmode = (((q[0] - '0') << 6) + ((q[1] - '0') << 3) + (q[2] - '0')) & (S_IRWXU | S_IRWXG | S_IRWXO);
+           TRACE("fmode: %d\n", server->config.fmode);
+       }else
+       if(strncmp(p, "dmask=", 6) == 0){
+           if(strlen(p + 6) > 3)
+               goto ugly_opts;
+           q = p + 6;
+           server->config.dmode = (((q[0] - '0') << 6) + ((q[1] - '0') << 3) + (q[2] - '0')) & (S_IRWXU | S_IRWXG | S_IRWXO);
+           TRACE("dmode: %d\n", server->config.dmode);
+       }else
+       if(strncmp(p, "root=", 5) == 0){
+           if(strlen(p+5) >= UNIX_PATH_MAX - 1)
+               goto ugly_opts;
+           strcpy(server->root, p+5);
+           server->rootlen = strlen(server->root);
+           
+           if(server->root[server->rootlen - 1] == '/'){
+               server->root[server->rootlen - 1] = 0;
+               server->rootlen--;
+           }
+                           
+           TRACE("remote root: %s, len: %u\n", server->root, server->rootlen);
+       }else
+       if(strncmp(p, "channels=", 9) == 0){
+           if(strlen(p+9) > 5)
+               goto ugly_opts;
+           q = p + 9;
+           server->config.channels = simple_strtoul(q, &q, 0);
+           
+           TRACE("channels: %u\n", server->config.channels);
+       }else
+       if(strncmp(p, "own_fs", 6) == 0){
+           server->config.own_fs = 1;
+           TRACE("forcing ownership\n");
+       }else
+       if(strncmp(p, "server_pid=", 11) == 0){
+           if(strlen(p+11) > 7)
+               goto ugly_opts;
+           q = p + 11;
+           server->server_pid = simple_strtoul(q, &q, 0);
+
+           TRACE("server_pid: %u\n", server->server_pid);
+       }
+    }
+
+    return;
+
+  ugly_opts:
+    WARN("evil options!\n");
+}
+
+/*
+ * Fill in inode attributes. 
+ * Ivalidate the page_cache pages if the inode has been modified.
+ */
+static void set_inode_attr(struct inode *inode, struct lufs_fattr *fattr)
+{
+    time_t last_time = inode->i_mtime.tv_sec;
+    loff_t last_sz = inode->i_size;
+
+    TRACE("in\n");
+    
+    inode->i_mode = fattr->f_mode;
+    inode->i_nlink = fattr->f_nlink;
+    inode->i_uid = fattr->f_uid;
+    inode->i_gid = fattr->f_gid;
+    inode->i_ctime.tv_sec = fattr->f_ctime;
+    inode->i_mtime.tv_sec = fattr->f_mtime;
+    inode->i_atime.tv_sec = fattr->f_atime;
+    inode->i_blksize = fattr->f_blksize;
+    inode->i_blocks = fattr->f_blocks;
+    inode->i_size = fattr->f_size;
+
+    if(inode->i_mtime.tv_sec != last_time || inode->i_size != last_sz){
+       TRACE("inode changed...\n");
+       if(!S_ISDIR(inode->i_mode))
+           invalidate_inode_pages(inode->i_mapping);
+    }
+
+    TRACE("out\n");
+}
+
+static int lu_do_stat(struct dentry *dentry, struct lufs_fattr *fattr)
+{
+    struct server_slot *slot;
+    struct iovec siov, riov;
+    int res;
+
+    TRACE("in\n");
+
+    if((slot = lu_getslot(GET_INFO(dentry->d_sb))) == NULL)
+       return -ERESTARTSYS;
+
+    if((res = lu_getname(dentry, slot->s_buf, LU_MAXDATA)) < 0){
+       WARN("lu_getname failed!\n");
+       goto out;
+    }
+
+    TRACE("stating %s...\n", slot->s_buf);
+
+    siov.iov_base = slot->s_buf;
+    siov.iov_len = strlen(slot->s_buf) + 1;
+    riov.iov_base = fattr;
+    riov.iov_len = sizeof(struct lufs_fattr);
+
+    if((res = lu_execute(GET_INFO(dentry->d_sb), slot, PTYPE_STAT, &siov, 1, &riov, 1)) < 0)
+       goto out;
+
+    if(PIS_ERROR(res)){
+       WARN("stat failed!\n");
+       res = PERROR(res);
+       goto out;
+    }
+
+    lu_fixattrs(GET_INFO(dentry->d_sb), fattr);
+
+    res = 0;
+
+  out:
+    TRACE("out\n");
+    lu_putslot(slot);
+    return res;
+}
+
+/*
+ * Reload inode attributes.
+ */
+static int lu_refresh_inode(struct dentry *dentry)
+{
+    struct inode *inode = dentry->d_inode;
+    struct lufs_fattr fattr;
+    int res;
+
+    TRACE("in\n");
+
+    if((res = lu_do_stat(dentry, &fattr)) < 0)
+       return res;
+
+    dentry->d_time = jiffies;
+
+    if(!inode)
+       return 0;
+
+    if((inode->i_mode & S_IFMT) == (fattr.f_mode & S_IFMT))
+       set_inode_attr(inode, &fattr);
+    else{
+       WARN("inode changed mode, %x to %x\n", inode->i_mode, (unsigned int)fattr.f_mode);
+       TRACE("oops!\n");
+       
+       fattr.f_mode = inode->i_mode;
+       make_bad_inode(inode);
+       inode->i_mode = fattr.f_mode;
+
+       if(!S_ISDIR(inode->i_mode))
+           invalidate_inode_pages(inode->i_mapping);
+           
+       return -EIO;
+    }
+
+    TRACE("out\n");
+    return 0;
+}
+
+int lu_revalidate_inode(struct dentry *dentry)
+{
+    int res = 0;
+
+    TRACE("in\n");
+    
+    lock_kernel();
+    
+    if(time_before(jiffies, dentry->d_time + LU_MAXAGE))
+       goto out;
+
+    res = lu_refresh_inode(dentry);
+
+  out:
+    TRACE("out\n");
+    unlock_kernel();
+    return res;
+}
+
+int lufs_notify_change(struct dentry *dentry, struct iattr *iattr)
+{
+    struct server_slot *slot;
+    struct iovec iov[2];
+    struct lufs_fattr fattr;
+    int res;
+
+    TRACE("in\n");
+
+    if((res = lu_do_stat(dentry, &fattr)) < 0)
+       return res;
+
+    if((slot = lu_getslot(GET_INFO(dentry->d_sb))) == NULL)
+       return -ERESTARTSYS;
+
+    if((res = lu_getname(dentry, slot->s_buf, LU_MAXDATA)) < 0){
+       WARN("lu_getname failed!\n");
+       goto out;
+    }
+    
+    if(iattr->ia_valid & ATTR_MODE)
+       fattr.f_mode = iattr->ia_mode;
+    if(iattr->ia_valid & ATTR_UID)
+       fattr.f_uid  = iattr->ia_uid;
+    if(iattr->ia_valid & ATTR_GID)
+       fattr.f_gid  = iattr->ia_gid;
+    if(iattr->ia_valid & ATTR_SIZE)
+       fattr.f_size = iattr->ia_size;
+    if(iattr->ia_valid & ATTR_ATIME)
+       fattr.f_atime= iattr->ia_atime.tv_sec;
+    if(iattr->ia_valid & ATTR_MTIME)
+       fattr.f_mtime= iattr->ia_mtime.tv_sec;
+    if(iattr->ia_valid & ATTR_CTIME)
+       fattr.f_ctime= iattr->ia_ctime.tv_sec;
+
+    iov[0].iov_base = &fattr;
+    iov[0].iov_len = sizeof(struct lufs_fattr);
+    iov[1].iov_base = slot->s_buf;
+    iov[1].iov_len = strlen(slot->s_buf) + 1;
+
+    if((res = lu_execute(GET_INFO(dentry->d_sb), slot, PTYPE_SETATTR, iov, 2, NULL, 0)) < 0)
+       goto out;
+
+    if(PIS_ERROR(res)){
+       WARN("setattr failed!\n");
+       res = PERROR(res);
+       goto out;
+    }
+
+    res = 0;
+
+    lu_refresh_inode(dentry);
+
+  out:
+    TRACE("out\n");
+    lu_putslot(slot);
+    return res;
+}
+
+/*
+ * We always create a new inode here.
+ */
+struct inode* lu_iget(struct super_block *sb, struct lufs_fattr *fattr)
+{
+    struct inode *res;
+
+    TRACE("in\n");
+    
+    res = new_inode(sb);
+    if(!res)
+       return NULL;
+    res->i_ino = fattr->f_ino;
+    set_inode_attr(res, fattr);
+
+    if(S_ISDIR(res->i_mode)){
+       TRACE("it's a dir.\n");
+       res->i_op = &lu_dir_inode_operations;
+       res->i_fop = &lu_dir_operations;
+    }else if(S_ISLNK(res->i_mode)){
+       TRACE("it's a link.\n");
+       res->i_op = &lu_symlink_inode_operations;
+    }else{
+       TRACE("it's a file.\n");
+       res->i_op = &lu_file_inode_operations;
+       res->i_fop = &lu_file_operations;
+       res->i_data.a_ops = &lu_file_aops;
+    }
+       
+    insert_inode_hash(res);
+    return res;
+}
+
+static int lu_statfs(struct super_block *sb, struct statfs *attr)
+{
+    TRACE("in\n");
+    
+    attr->f_type = LU_MAGIC;
+    attr->f_bsize = LU_BLOCKSIZE;
+    attr->f_blocks = 0;
+    attr->f_namelen = LU_MAXPATHLEN;
+    attr->f_files = -1;
+    attr->f_bavail = -1;
+
+    TRACE("out\n");
+    return 0;
+}
+
+static void lu_put_super(struct super_block *sb)
+{
+    struct siginfo info;
+
+    TRACE("in\n");
+
+    info.si_signo = SIGUSR1;
+    info.si_errno = 0;
+    info.si_code = SI_USER;
+    info.si_pid = current->pid;
+    info.si_uid = current->uid;
+    
+    /* notify the daemon that we're going bye-bye */
+    kill_proc_info(SIGUSR1, &info, GET_INFO(sb)->server_pid);
+
+    lu_empty_slots(GET_INFO(sb));
+    kfree(GET_INFO(sb));
+    TRACE("out\n");
+}
+
+static void lu_delete_inode(struct inode *in)
+{
+    TRACE("in\n");
+    clear_inode(in);
+    TRACE("out\n");
+}
+
+static int lu_fill_super(struct super_block *sb, void *opts, int silent)
+{
+    struct lufs_sb_info *info;
+    struct server_slot *slot;
+    struct lufs_fattr root_attr;
+    struct inode *root_inode;
+
+    int i;
+
+    TRACE("in\n");
+    
+    if(!opts){
+       ERROR("need some options here!\n");
+       goto out;
+    }
+    
+    if((info = (struct lufs_sb_info*)kmalloc(sizeof(struct lufs_sb_info), GFP_KERNEL)) == NULL){
+       ERROR("kmalloc error!\n");
+       goto out;
+    }
+    memset(info, 0, sizeof(struct lufs_sb_info));
+    info->lock = RW_LOCK_UNLOCKED;
+    INIT_LIST_HEAD(&info->slots);
+
+    info->config.uid = current->uid;
+    info->config.gid = current->gid;    
+    info->config.channels = LU_NRSLOTS;
+    
+    parse_options(info, opts);
+    
+    if(!info->server_socket[0]){
+       ERROR("no server_socket specified!\n");
+       goto out_info;
+    }
+    
+    for(i = 0; i < info->config.channels; i++){
+       if((slot = kmalloc(sizeof(struct server_slot), GFP_KERNEL)) == NULL){
+           ERROR("kmalloc error!\n");
+           goto out_slots;
+       }
+       memset(slot, 0, sizeof(struct server_slot));
+       init_MUTEX(&slot->s_lock);
+       if((slot->s_buf = kmalloc(LU_MAXDATA, GFP_KERNEL)) == NULL){
+           ERROR("kmalloc error!\n");
+           goto out_slots;
+       }
+       list_add(&slot->s_list, &info->slots);
+    }
+
+    sb->s_fs_info = info;
+    sb->s_blocksize = LU_BLOCKSIZE;
+    sb->s_blocksize_bits = LU_BLOCKSIZEBITS;
+    sb->s_magic = LU_MAGIC;
+    sb->s_op = &lu_sops;
+    sb->s_flags = 0;
+    sb->s_maxbytes = ((((long long)1) << 32) << LU_BLOCKSIZEBITS) - 1;
+    TRACE("sb->s_maxbytes=%Ld\n",sb->s_maxbytes);
+
+    lu_lookup_root(info, &root_attr);
+    root_inode = lu_iget(sb, &root_attr);
+    if(!root_inode)
+       goto out_slots;
+    sb->s_root = d_alloc_root(root_inode);
+    if(!sb->s_root)
+       goto out_slots;
+
+    sb->s_root->d_op = &lufs_dentry_operations;
+    sb->s_root->d_time = jiffies;
+
+    TRACE("mount succeded: %s\n", info->server_socket);
+    return 0;
+
+ out_slots:
+    lu_empty_slots(info);
+ out_info:
+    kfree(info);
+ out:
+    ERROR("mount failed!\n");
+    return -EINVAL;
+}
+
+static struct super_block *lu_get_sb(struct file_system_type *fs_type, int flags, char *dev_name, void *data)
+{
+    return get_sb_nodev(fs_type, flags, data, lu_fill_super);
+}
+
+static struct file_system_type lu_fs_type = {
+    .owner     = THIS_MODULE,
+    .name      = "lufs",
+    .get_sb    = lu_get_sb,
+    .kill_sb   = kill_anon_super,
+};
+
+static int __init lu_init(void)
+{
+    VERBOSE("UserLand File System\n");
+    VERBOSE("Copyright (c) 2002, Florin Malita\n");
+    return register_filesystem(&lu_fs_type);
+}
+
+static void __exit lu_release(void)
+{
+    VERBOSE("Unregistering lufs...\n");
+    unregister_filesystem(&lu_fs_type);
+}
+
+module_init(lu_init);
+module_exit(lu_release);
diff --git a/kernel/Linux/2.6/lufs.h b/kernel/Linux/2.6/lufs.h
new file mode 100644 (file)
index 0000000..1275099
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * lufs.h
+ * Copyright (C) 2002 Florin Malita <mali@go.ro>
+ *
+ * This file is part of LUFS, a free userspace filesystem implementation.
+ * See http://lufs.sourceforge.net/ for updates.
+ *
+ * LUFS is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * LUFS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _LUFS_H_
+#define _LUFS_H_
+
+#include <linux/list.h>
+#include <linux/un.h>
+#include <linux/spinlock.h>
+
+#include "../../../include/lufs/proto.h"
+
+#undef TRACE
+#undef WARN
+#undef VERBOSE
+#undef ERROR
+
+#ifdef LUFS_DEBUG
+#define TRACE(x...)    do { printk(KERN_INFO "(%s) - ", __func__); printk(x); } while(0)
+#define WARN(x...)     do { printk(KERN_ERR "(%s) - ", __func__); printk(x); } while(0)
+#else
+#define TRACE(x...)    do {} while(0)
+#define WARN(x...)     do {} while(0)
+#endif
+
+#ifdef LUFS_VERBOSE
+#define VERBOSE(x...)  do { printk(KERN_INFO "(%s) - ", __func__); printk(x); } while(0)
+#else
+#define VERBOSE(x...)  do {} while(0)
+#endif
+
+#define ERROR(x...)    do { printk(KERN_ERR "(%s) - ", __func__); printk(x); } while(0)
+
+#define GET_INFO(sb)   ((struct lufs_sb_info*)sb->s_fs_info)
+
+#define LU_MAXPATHLEN  1024
+#define LU_MAXTRIES    10
+#define LU_MAXIOVEC    5
+#define LU_NRSLOTS     3
+#define LU_MAGIC       0xfade
+#define LU_MAXAGE      HZ*5
+
+#define LU_DEF_UID     2
+#define LU_DEF_GID     2
+
+#define LU_BLOCKSIZE   512
+#define LU_BLOCKSIZEBITS       9
+
+struct lufs_config{
+    __kernel_uid_t     uid;
+    __kernel_gid_t     gid;
+    __kernel_mode_t    fmode;
+    __kernel_mode_t    dmode;
+    unsigned           channels;
+    int                        own_fs;
+};
+
+struct lufs_sb_info{
+    struct list_head   slots;
+    struct lufs_config config;
+    rwlock_t           lock;
+    char               server_socket[UNIX_PATH_MAX];
+    pid_t              server_pid;
+    char               root[UNIX_PATH_MAX];
+    unsigned           rootlen;
+};
+
+#endif
diff --git a/kernel/Linux/2.6/proc.c b/kernel/Linux/2.6/proc.c
new file mode 100644 (file)
index 0000000..cb0b2e7
--- /dev/null
@@ -0,0 +1,505 @@
+/*
+ * proc.c
+ * Copyright (C) 2002 Florin Malita <mali@go.ro>
+ *
+ * This file is part of LUFS, a free userspace filesystem implementation.
+ * See http://lufs.sourceforge.net/ for updates.
+ *
+ * LUFS is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * LUFS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/socket.h>
+#include <linux/un.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/smp_lock.h>
+#include <linux/net.h>
+#include <linux/vfs.h>
+#include <linux/mount.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include "lufs.h"
+#include "proc.h"
+
+static int sock_send(struct socket *sock, struct iovec *iov, int len)
+{
+    struct msghdr msg = {
+       .msg_name       = NULL,
+       .msg_namelen    = 0,
+       .msg_iov        = iov,
+       .msg_iovlen     = len,
+       .msg_control    = NULL,
+       .msg_controllen = 0,
+       .msg_flags      = 0
+    };
+    int res, i, size;
+    mm_segment_t fs;
+
+    for(i = 0, size = 0; i < len; i++)
+       size += iov[i].iov_len;
+    
+    fs = get_fs();
+    set_fs(get_ds());
+    res = sock_sendmsg(sock, &msg, size);
+    set_fs(fs);
+
+    return res;
+}
+
+static int sock_recv(struct socket *sock, struct iovec *iov, int len, int rsize, unsigned flags)
+{
+    struct msghdr msg = {
+       .msg_flags      = flags,
+       .msg_name       = NULL,
+       .msg_namelen    = 0,
+       .msg_iov        = iov,
+       .msg_iovlen     = len,
+       .msg_control    = NULL,
+       .msg_controllen = 0
+    };
+    mm_segment_t fs;
+    int res, i, size;
+
+    for(i = 0, size = 0; i < len; i++)
+       size += iov[i].iov_len;
+
+    if(size < rsize){
+       VERBOSE("Trying to overflow old me?! Truncating...\n");
+       rsize = size;
+    }
+
+    fs = get_fs();
+    set_fs(get_ds());
+    res = sock_recvmsg(sock, &msg, rsize, flags);
+    set_fs(fs);
+
+    return res;
+}
+
+static int sock_connect(char *path, struct socket **s)
+{
+    struct sockaddr_un addr;
+    int res;
+
+    if(strlen(path) > UNIX_PATH_MAX - 1){
+       WARN("unix domain path too long: %s", path);
+       return -1;
+    }
+
+    addr.sun_family = AF_UNIX;
+    strcpy(addr.sun_path, path);
+
+    if((res = sock_create(PF_UNIX, SOCK_STREAM, 0, s)) < 0){
+       WARN("failed to create a unix domain socket!\n");
+       return res;
+    }
+
+    if((res = (*s)->ops->connect(*s, (struct sockaddr*)&addr, sizeof(addr), 0)) < 0){
+       WARN("failed to connect the socket: %d!\n", res);
+       return res;
+    }
+    return 0;
+}
+
+static int slot_reconnect(struct lufs_sb_info *info, struct server_slot *slot)
+{
+    int res = 0, tries = 0;
+
+    if(slot->s_sock){
+       TRACE("closing socket.\n");
+       sock_release(slot->s_sock);
+       slot->s_sock = NULL;
+    }
+
+    while(tries++ < LU_MAXTRIES && (res = sock_connect(info->server_socket, &slot->s_sock)) < 0){
+       TRACE("retrying...\n");
+       sock_release(slot->s_sock);
+       slot->s_sock = NULL;
+    }
+
+    if(res >= 0){
+       TRACE("successfully reconnected.\n");
+    }
+
+    return res;
+}
+
+void lu_empty_slots(struct lufs_sb_info *info)
+{
+    struct server_slot *slot;
+
+    while(!list_empty(&info->slots)){
+       slot = list_entry(info->slots.next, struct server_slot, s_list);
+       if(slot->s_sock)
+           sock_release(slot->s_sock);
+       list_del(&slot->s_list);
+       if(slot->s_buf)
+           kfree(slot->s_buf);
+       kfree(slot);
+    }
+}
+
+static int do_execute(struct socket *sock, unsigned short cmd, unsigned short msglen, struct iovec *siov, unsigned short slen, struct iovec *riov, unsigned short rlen)
+{
+    struct lu_msg msg;
+    struct iovec iov;
+    int res;
+
+    TRACE("msg_len: %d\n", msglen);
+    
+    msg.msg_version = PVERSION;
+    msg.msg_type = cmd;
+    msg.msg_datalen = msglen;
+    msg.msg_pid = current->pid;
+
+    iov.iov_base = &msg;
+    iov.iov_len = sizeof(struct lu_msg);
+
+    if((res = sock_send(sock, &iov, 1)) < 0){
+       WARN("sock_send failed!\n");
+       return res;
+    }
+    if((res = sock_send(sock, siov, slen)) < 0){
+       WARN("sock_send failed!\n");
+       return res;
+    }
+
+    iov.iov_base = &msg;
+    iov.iov_len = sizeof(struct lu_msg);
+    if((res = sock_recv(sock, &iov, 1, sizeof(struct lu_msg), 0)) < 0){
+       WARN("sock_recv failed!\n");
+       return res;
+    }
+    if(res != sizeof(struct lu_msg)){
+       WARN("Ayeeee, didn't read a whole header!\n");
+       return -EBUSY;
+    }
+    
+    if((msg.msg_datalen == 0))
+       return msg.msg_type;
+
+    if(riov == NULL){
+       WARN("Unexpected data!!! Getting out of sync...\n");
+       return -1;
+    }
+       
+    if((res = sock_recv(sock, riov, rlen, msg.msg_datalen, 0)) < 0){
+       WARN("sock_recv failed!\n");
+       return res;
+    }
+
+    return msg.msg_type;
+}
+
+struct server_slot* lu_getslot(struct lufs_sb_info *info)
+{
+    struct list_head *p, *nd_best = NULL;
+    struct server_slot *slot;
+    int gotlock = 0;
+
+    /* Look for a slot used by this process before */
+    read_lock(&info->lock);
+    list_for_each(p, &info->slots)
+       if(list_entry(p, struct server_slot, s_list)->s_lastpid == current->pid){
+           TRACE("found a previous used slot for %u.\n", current->pid);
+           if(down_trylock(&list_entry(p, struct server_slot, s_list)->s_lock) == 0){
+               gotlock = 1;
+               break;
+           }
+           TRACE("oops! I still hold the lock! forget this one...\n");
+       }else 
+           if(!nd_best){
+               nd_best = p;
+           }
+
+    /* if we couldn't find one, take the first not locked by us */     
+    if(p == &info->slots){
+       if(!nd_best){
+           ERROR("deadlock: all locks owned by us!\n");
+           read_unlock(&info->lock);
+           return NULL;
+       }else
+           p = nd_best;
+       
+    }
+    read_unlock(&info->lock);
+
+    slot = list_entry(p, struct server_slot, s_list);
+    
+    /* Get the lock on that slot */
+    if(!gotlock)
+       if(down_interruptible(&slot->s_lock))
+           return NULL;
+
+    slot->s_lastpid = current->pid;
+
+    /* Move it to the tail */
+    write_lock(&info->lock);
+    list_del(p);
+    list_add_tail(p, &info->slots);
+    write_unlock(&info->lock);
+
+    return slot;
+}
+
+void lu_putslot(struct server_slot *slot)
+{
+    up(&slot->s_lock);
+}
+
+int lu_execute(struct lufs_sb_info *info, struct server_slot *slot, unsigned short cmd, struct iovec *siov, unsigned short slen, struct iovec *riov, unsigned short rlen)
+{
+    int res, i, msglen;
+    struct iovec bkup[LU_MAXIOVEC];
+
+    for(i = 0, msglen = 0; i < slen; i++){
+       bkup[i] = siov[i];
+       msglen += siov[i].iov_len;
+    }
+
+    if(slot->s_sock == NULL){
+       TRACE("slot not connected.\n");
+       if((res = slot_reconnect(info, slot)) < 0){
+           ERROR("failed to connect!\n");
+           goto out;
+       }
+    }
+
+    if((res = do_execute(slot->s_sock, cmd, msglen, siov, slen, riov, rlen)) < 0){
+       TRACE("do_execute failed!\n");
+
+       if(signal_pending(current) && (!sigismember(&current->pending.signal, SIGPIPE))){
+           TRACE("interrupted by a signal. disconnecting this slot...\n");
+           sock_release(slot->s_sock);
+           slot->s_sock = NULL;
+           goto out;
+       }
+       
+       if(sigismember(&current->pending.signal, SIGPIPE)){
+           TRACE("got a SIGPIPE\n");
+           sigdelset(&current->pending.signal, SIGPIPE);
+       }
+
+       if((res = slot_reconnect(info, slot)) < 0){
+           ERROR("could't reconnect!\n");
+           goto out;
+       }
+           
+       for(i = 0; i < slen; i++)
+           siov[i] = bkup[i];
+               
+       if((res = do_execute(slot->s_sock, cmd, msglen, siov, slen, riov, rlen)) < 0){
+           ERROR("error executing command!\n");
+           goto out;
+       }
+    }
+    
+ out:
+    return res;
+}
+
+int lu_getname(struct dentry *d, char *name, int max)
+{
+    int len = 0;
+    struct dentry *p;
+    struct lufs_sb_info *info = GET_INFO(d->d_sb);
+    
+    for(p = d; p != p->d_parent; p = p->d_parent)
+       len += p->d_name.len + 1;
+
+    TRACE("root: %s, rootlen: %d, namelen: %d\n", info->root, info->rootlen, len);
+    
+    if(len + info->rootlen > max)
+       return -1;
+
+    strcpy(name, info->root);
+
+    if(len + info->rootlen == 0){
+       strcat(name, "/");
+       goto out;
+    }
+    
+    len += info->rootlen;
+
+    name[len] = 0;
+    for(p = d; p != p->d_parent; p = p->d_parent){
+       len -= p->d_name.len;
+       strncpy(&(name[len]), p->d_name.name, p->d_name.len);
+       name[--len] = '/';
+    }
+
+out:
+    TRACE("name resolved to %s\n", name);
+    return 0;
+}
+
+int lu_getname_dumb(struct dentry *d, char *name, int max)
+{
+    int len = 0;
+    struct dentry *p;
+
+    for(p = d; p != p->d_parent; p = p->d_parent)
+       len += p->d_name.len + 1;
+
+    if(len > max)
+       return -1;
+
+    if(len == 0){
+       name[0] = '/';
+       name[1] = 0;
+       goto out;
+    }
+
+    name[len] = 0;
+    for(p = d; p != p->d_parent; p = p->d_parent){
+       len -= p->d_name.len;
+       strncpy(&(name[len]), p->d_name.name, p->d_name.len);
+       name[--len] = '/';
+    }
+
+out:
+    return 0;
+}
+
+static void init_root_dirent(struct lufs_sb_info *server, struct lufs_fattr *fattr)
+{
+    memset(fattr, 0, sizeof(struct lufs_fattr));
+    fattr->f_nlink = 1;
+    fattr->f_uid = server->config.uid;
+    fattr->f_gid = server->config.gid;
+    fattr->f_blksize = 512;
+    fattr->f_ino = 2;
+    fattr->f_mtime = CURRENT_TIME.tv_sec;
+    fattr->f_mode = S_IRUSR | S_IRGRP | S_IROTH | S_IXUSR | S_IXGRP | S_IXOTH | S_IFDIR | server->config.dmode;
+    fattr->f_size = 512;
+    fattr->f_blocks = 1;
+}
+
+void lu_lookup_root(struct lufs_sb_info *server, struct lufs_fattr *fattr)
+{
+    struct server_slot *slot;
+    struct iovec siov, riov;
+    int res;
+
+    TRACE("in\n");
+
+    if((slot = lu_getslot(server)) == NULL){
+       init_root_dirent(server, fattr);
+       return;
+    }
+    
+    if(server->rootlen)
+       strcpy(slot->s_buf, server->root);
+    else
+       strcpy(slot->s_buf, "/");
+       
+    TRACE("stating root %s\n", slot->s_buf);
+
+    siov.iov_base = slot->s_buf;
+    siov.iov_len = strlen(slot->s_buf) + 1;
+    riov.iov_base = fattr;
+    riov.iov_len = sizeof(struct lufs_fattr);
+
+    if((res = lu_execute(server, slot, PTYPE_STAT, &siov, 1, &riov, 1)) < 0){
+       init_root_dirent(server, fattr);
+       goto out;
+    }
+
+    if(PIS_ERROR(res)){
+       WARN("stat failed!\n");
+       init_root_dirent(server, fattr);
+       goto out;
+    }
+
+    lu_fixattrs(server, fattr);
+
+    fattr->f_ino = 2;
+
+  out:
+    TRACE("out\n");
+    lu_putslot(slot);
+}
+
+void lu_fixattrs(struct lufs_sb_info *info, struct lufs_fattr *fattr)
+{
+
+    fattr->f_blksize = LU_BLOCKSIZE;
+    
+    if(S_ISREG(fattr->f_mode) || S_ISDIR(fattr->f_mode))
+       fattr->f_blocks = (fattr->f_size + LU_BLOCKSIZE - 1) / LU_BLOCKSIZE;
+    else
+       fattr->f_blocks = 0;
+
+    if(info->config.own_fs){
+
+       if(!fattr->f_uid)
+           fattr->f_mode = (fattr->f_mode & ~S_IRWXU) | ((fattr->f_mode & S_IRWXO)*(S_IRWXU/S_IRWXO));
+
+       if(!fattr->f_gid)
+           fattr->f_mode = (fattr->f_mode & ~S_IRWXG) | ((fattr->f_mode & S_IRWXO)*(S_IRWXG/S_IRWXO));
+       
+       fattr->f_uid = info->config.uid;
+       fattr->f_gid = info->config.gid;
+
+    }else{
+       
+       if(fattr->f_uid)
+           fattr->f_uid = info->config.uid;
+       else
+           fattr->f_uid = LU_DEF_UID;
+
+       if(fattr->f_gid)
+           fattr->f_gid = info->config.gid;
+       else
+           fattr->f_gid = LU_DEF_GID;
+    }
+
+    if(fattr->f_mode & S_IFDIR)
+       fattr->f_mode |= info->config.dmode;
+    else
+       fattr->f_mode |= info->config.fmode;
+}
+
+void lu_xlate_symlink(char *link, char *target, char *buf)
+{
+    int i;
+    char *c1, *c2 = link;
+
+    TRACE("translating %s->%s\n", link, target);
+
+    for(c1 = strchr(link, '/'); c1 && !strncmp(link, target, c1 - link); c2 = c1, c1 = strchr(c1 + 1, '/'));
+
+    TRACE("disjoint paths: %s, %s\n", c2, target + (c2 - link));
+
+    for(i = 0, c1 = c2; (c1 = strchr(c1 + 1, '/')); i++);
+
+    strcpy(buf, "./");
+    
+    for(; i > 0; i--)
+       strcat(buf, "../");
+
+    strcat(buf, target + (c2 - link) + 1);
+    
+    TRACE("absolute link resolved to %s\n", buf);
+   
+}
+
diff --git a/kernel/Linux/2.6/proc.h b/kernel/Linux/2.6/proc.h
new file mode 100644 (file)
index 0000000..83a598d
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * proc.h
+ * Copyright (C) 2002 Florin Malita <mali@go.ro>
+ *
+ * This file is part of LUFS, a free userspace filesystem implementation.
+ * See http://lufs.sourceforge.net/ for updates.
+ *
+ * LUFS is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * LUFS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _LU_PROC_H_
+#define _LU_PROC_H_
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/socket.h>
+
+struct server_slot{
+    struct socket      *s_sock;
+    struct semaphore   s_lock;
+    struct list_head   s_list;
+    pid_t              s_lastpid;
+    char               *s_buf;
+};
+
+struct lufs_fattr;
+
+int lu_execute(struct lufs_sb_info*, struct server_slot*, unsigned short, struct iovec*, unsigned short, struct iovec*, unsigned short);
+void lu_empty_slots(struct lufs_sb_info*);
+int lu_getname(struct dentry*, char*, int);
+int lu_getname_dumb(struct dentry*, char*, int);
+struct server_slot* lu_getslot(struct lufs_sb_info*);
+void lu_putslot(struct server_slot*);
+int lu_revalidate_inode(struct dentry*);
+void lu_lookup_root(struct lufs_sb_info*, struct lufs_fattr*);
+void lu_fixattrs(struct lufs_sb_info*, struct lufs_fattr*);
+void lu_xlate_symlink(char*, char*, char*);
+
+#endif
diff --git a/kernel/Linux/2.6/symlink.c b/kernel/Linux/2.6/symlink.c
new file mode 100644 (file)
index 0000000..c60570b
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * symlink.c
+ * Copyright (C) 2002 Florin Malita <mali@go.ro>
+ *
+ * This file is part of LUFS, a free userspace filesystem implementation.
+ * See http://lufs.sourceforge.net/ for updates.
+ *
+ * LUFS is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * LUFS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/socket.h>
+#include <linux/smp_lock.h>
+#include <linux/fs.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+
+#include "lufs.h"
+#include "proc.h"
+
+static char failed_link[] = "invalid";
+
+static int lu_readlink(struct dentry *dentry, char *buffer, int bufflen)
+{
+    struct server_slot *slot;
+    struct iovec siov, riov;
+    int res;
+    char *cc = failed_link;
+
+    TRACE("in\n");
+
+    if((slot = lu_getslot(GET_INFO(dentry->d_sb))) == NULL)
+       return vfs_readlink(dentry, buffer, bufflen, cc);
+
+    if((res = lu_getname(dentry, slot->s_buf, LU_MAXDATA)) < 0){
+       WARN("lu_getname failed!\n");
+       goto out;
+    }
+
+    siov.iov_base = slot->s_buf;
+    siov.iov_len = strlen(slot->s_buf) + 1;
+    riov.iov_base = &slot->s_buf[LU_MAXPATHLEN];
+    riov.iov_len = LU_MAXPATHLEN;
+
+    if((res = lu_execute(GET_INFO(dentry->d_sb), slot, PTYPE_READLINK, &siov, 1, &riov, 1)) < 0)
+       goto out;
+
+    if(PIS_ERROR(res)){
+       TRACE("read_link failed.\n");
+       res = PERROR(res);
+       goto out;
+    }
+
+    cc = &slot->s_buf[LU_MAXPATHLEN];
+    
+    TRACE("response: %s\n", cc);
+    
+    if(*cc == '/'){
+       if(GET_INFO(dentry->d_sb)->rootlen){
+           if(strncmp(GET_INFO(dentry->d_sb)->root, cc, GET_INFO(dentry->d_sb)->rootlen)){
+               WARN("symlink outside mounted root!");
+               cc = failed_link;
+               goto out;
+           }
+           cc += GET_INFO(dentry->d_sb)->rootlen;
+       }
+
+       lu_xlate_symlink(slot->s_buf, slot->s_buf + LU_MAXPATHLEN, slot->s_buf);
+
+       cc = slot->s_buf;
+
+    }
+
+
+
+  out:
+    res = vfs_readlink(dentry, buffer, bufflen, cc);
+
+    lu_putslot(slot);
+
+    TRACE("out\n");
+    return res;
+}
+
+static int lu_followlink(struct dentry *dentry, struct nameidata *nd)
+{
+    struct server_slot *slot;
+    struct iovec siov, riov;
+    int res;
+    char *cc = failed_link;
+    char *tmp;
+    
+    TRACE("in\n");
+
+    if((slot = lu_getslot(GET_INFO(dentry->d_sb))) == NULL)
+       return vfs_follow_link(nd, cc);
+
+
+    if((res = lu_getname(dentry, slot->s_buf, LU_MAXDATA)) < 0){
+       WARN("lu_getname failed!\n");
+       goto out;
+    }
+
+    siov.iov_base = slot->s_buf;
+    siov.iov_len = strlen(slot->s_buf) + 1;
+    riov.iov_base = &slot->s_buf[LU_MAXPATHLEN];
+    riov.iov_len = LU_MAXPATHLEN;
+
+    if((res = lu_execute(GET_INFO(dentry->d_sb), slot, PTYPE_READLINK, &siov, 1, &riov, 1)) < 0)
+       goto out;
+
+    if(PIS_ERROR(res)){
+       TRACE("read_link failed.\n");
+       res = PERROR(res);
+       goto out;
+    }
+
+    cc = &slot->s_buf[LU_MAXPATHLEN];
+
+    if(*cc == '/'){
+       if(GET_INFO(dentry->d_sb)->rootlen){
+           if(strncmp(GET_INFO(dentry->d_sb)->root, cc, GET_INFO(dentry->d_sb)->rootlen)){
+               WARN("symlink outside mounted root!");
+               cc = failed_link;
+               goto out;
+           }
+           cc += GET_INFO(dentry->d_sb)->rootlen;
+       }
+
+       lu_xlate_symlink(slot->s_buf, slot->s_buf + LU_MAXPATHLEN, slot->s_buf);
+
+       cc = slot->s_buf;
+
+    }
+
+  out:
+
+    /* vfs_follow_link somehow manages to call lookup_validate, so we need to 
+       release the slot, in case it's the only one, otherwise lu_lookup will 
+       fail (avoid a deadlock). bad, bad vfs_follow_link! you break the overall
+       beauty of no kmallocs... */
+
+    if((tmp = kmalloc(strlen(cc) + 1, GFP_KERNEL)) == NULL){
+       WARN("out of mem!\n");
+       tmp = failed_link;
+    }else    
+       strcpy(tmp, cc);
+
+    lu_putslot(slot);
+    res = vfs_follow_link(nd, tmp);
+
+    if(tmp != failed_link)
+       kfree(tmp);
+    
+    TRACE("out\n");
+    return res;
+}
+
+struct inode_operations lu_symlink_inode_operations = {
+    .readlink          = lu_readlink,
+    .follow_link       = lu_followlink,
+};
+
+
+
+
+