Hi,

Here's a new version of the virtual file patch (against 2.4.0-test5),
and the kernel module for avfs/podfuk.

It now does:
   - Clean up automatic mounts when parent is umounted
   - Clear dcache upon module insert, so virtual files are always found
   - Correctly handle virtual files in multiply mounted filesystems

Missing:
   - SMP safeness in the nredir module
   - Virtual file creation (root node)


The kernel patch makes the following changes:

 1) namei.c: adds a 'lookup_virtual' hook function. This is invoked,
    when the real_lookup returns a negative dentry.

    The virtual file implementation can check if the name does belong
    to a virtual file, and act apropriately (e.g. by mounting
    something on top the dentry)

 2) namei.c: real_lookup() is passed the parent nameidata instead of
    dentry, so that the virtual lookup function can have the global
    path.

 3) mount.h: a new mount operations structure is added with the following
    operations:

    - release() -- called when the mount is removed
    - umount()  -- called when the parent mount is about to be umounted

 4) super.c: new function try_umount_children(), which traverses the
    child-list of the mount, and calls the umount method of the child.

    This function is called in do_umount(), before the mnt_count is
    checked. 

    In try_umount_children() the 'dcache_lock' is not held, since the
    umount method might do complex thinks (like call do_umount), but
    if I'm not mistaken, then mount_sem is enough to protect the child
    list...

 5) super.c: add_vfsmnt has an extra parameter -- the mount operations.

 6) various places: The following symbols are exported:

    - lookup_virtual
    - add_vfsmnt
    - do_umount
    - mount_sem

Alexander, could you please tell me, what is acceptable from these
changes! (not necessary for 2.4.0)

Thanks,
Miklos


===File ~/avfs/src/src/nredir/virtual-2.4.0-test5.patch=====
diff -ru /tmp/linux/linux-2.4.0-test5.tar.gz@/linux/fs/namei.c linux/fs/namei.c
--- /tmp/linux/linux-2.4.0-test5.tar.gz@/linux/fs/namei.c       Fri Jul 28 08:14:10 
2000
+++ linux/fs/namei.c    Fri Jul 28 08:18:53 2000
@@ -98,6 +98,9 @@
  * XEmacs seems to be relying on it...
  */
 
+/* lookup function for "virtual" files */ 
+struct dentry *(*lookup_virtual)(struct nameidata *, struct dentry *);
+
 /* In order to reduce some races, while at the same time doing additional
  * checking and hopefully speeding things up, we copy filenames to the
  * kernel data space before using them..
@@ -260,9 +263,10 @@
  * make sure that nobody added the entry to the dcache in the meantime..
  * SMP-safe
  */
-static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, int 
flags)
+static struct dentry * real_lookup(struct nameidata * nd, struct qstr * name, int 
+flags)
 {
        struct dentry * result;
+       struct dentry *parent = nd->dentry;
        struct inode *dir = parent->d_inode;
 
        down(&dir->i_sem);
@@ -281,12 +285,24 @@
                        lock_kernel();
                        result = dir->i_op->lookup(dir, dentry);
                        unlock_kernel();
-                       if (result)
+                       if (result) {
                                dput(dentry);
-                       else
+                               up(&dir->i_sem);
+                       }
+                       else {
+                               up(&dir->i_sem);
                                result = dentry;
-               }
-               up(&dir->i_sem);
+                               /* 
+                                * If the dentry is negative it might
+                                * refer to a 'virtual' file 
+                                */
+                               if (lookup_virtual && !dentry->d_inode)
+                                       lookup_virtual(nd, dentry);
+                       }
+               }
+               else
+                       up(&dir->i_sem);
+               
                return result;
        }
 
@@ -489,7 +505,7 @@
                /* This does the actual lookups.. */
                dentry = cached_lookup(nd->dentry, &this, LOOKUP_CONTINUE);
                if (!dentry) {
-                       dentry = real_lookup(nd->dentry, &this, LOOKUP_CONTINUE);
+                       dentry = real_lookup(nd, &this, LOOKUP_CONTINUE);
                        err = PTR_ERR(dentry);
                        if (IS_ERR(dentry))
                                break;
@@ -552,7 +568,7 @@
                }
                dentry = cached_lookup(nd->dentry, &this, 0);
                if (!dentry) {
-                       dentry = real_lookup(nd->dentry, &this, 0);
+                       dentry = real_lookup(nd, &this, 0);
                        err = PTR_ERR(dentry);
                        if (IS_ERR(dentry))
                                break;
diff -ru /tmp/linux/linux-2.4.0-test5.tar.gz@/linux/fs/super.c linux/fs/super.c
--- /tmp/linux/linux-2.4.0-test5.tar.gz@/linux/fs/super.c       Fri Jul 28 08:14:12 
2000
+++ linux/fs/super.c    Fri Jul 28 08:24:43 2000
@@ -48,7 +48,7 @@
  * unmounting a filesystem and re-mounting it (or something
  * else).
  */
-static DECLARE_MUTEX(mount_sem);
+DECLARE_MUTEX(mount_sem);
 
 extern void wait_for_keypress(void);
 
@@ -304,9 +304,10 @@
  *     support for such beasts we'll have to change prototype.
  */
 
-static struct vfsmount *add_vfsmnt(struct nameidata *nd,
-                               struct dentry *root,
-                               const char *dev_name)
+struct vfsmount *add_vfsmnt(struct nameidata *nd,
+                            struct dentry *root,
+                            const char *dev_name,
+                           struct mount_operations *mnt_op)
 {
        struct vfsmount *mnt;
        struct super_block *sb = root->d_inode->i_sb;
@@ -331,6 +332,7 @@
        mnt->mnt_owner = current->uid;
        atomic_set(&mnt->mnt_count,1);
        mnt->mnt_sb = sb;
+       mnt->mnt_op = mnt_op;
 
        spin_lock(&dcache_lock);
        if (nd && !IS_ROOT(nd->dentry) && d_unhashed(nd->dentry))
@@ -418,6 +420,8 @@
        list_del(&mnt->mnt_child);
        spin_unlock(&dcache_lock);
        /* Now we can work safely */
+       if(mnt->mnt_op && mnt->mnt_op->release)
+           mnt->mnt_op->release(mnt);
        if (mnt->mnt_parent != mnt)
                mntput(mnt->mnt_parent);
 
@@ -937,7 +941,7 @@
                put_unnamed_dev(dev);
                return ERR_PTR(-EINVAL);
        }
-       mnt = add_vfsmnt(NULL, sb->s_root, NULL);
+       mnt = add_vfsmnt(NULL, sb->s_root, NULL, NULL);
        if (!mnt) {
                kill_super(sb, 0);
                return ERR_PTR(-ENOMEM);
@@ -968,7 +972,23 @@
        return 0;
 }
 
-static int do_umount(struct vfsmount *mnt, int umount_root, int flags)
+static void try_umount_children(struct vfsmount *mnt)
+{
+       struct list_head *next, *curr;
+       struct vfsmount *child;
+
+       /* The mount list is protected by mount_sem */
+       curr = mnt->mnt_mounts.next;
+       while(curr != &mnt->mnt_mounts) {
+               next = curr->next;
+               child = list_entry(curr, struct vfsmount, mnt_child);
+               if(child->mnt_op && child->mnt_op->umount)
+                       child->mnt_op->umount(child);
+               curr = next;
+       }
+}
+
+int do_umount(struct vfsmount *mnt, int umount_root, int flags)
 {
        struct super_block * sb = mnt->mnt_sb;
 
@@ -994,6 +1014,9 @@
                return retval;
        }
 
+       /* umount any automatic mounts */
+       try_umount_children(mnt);
+
        spin_lock(&dcache_lock);
        if (atomic_read(&mnt->mnt_count) > 2) {
                spin_unlock(&dcache_lock);
@@ -1171,7 +1194,7 @@
        down(&new_nd.dentry->d_inode->i_zombie);
        if (IS_DEADDIR(new_nd.dentry->d_inode))
                err = -ENOENT;
-       else if (add_vfsmnt(&new_nd, old_nd.dentry, old_nd.mnt->mnt_devname))
+       else if (add_vfsmnt(&new_nd, old_nd.dentry, old_nd.mnt->mnt_devname, NULL))
                err = 0;
        up(&new_nd.dentry->d_inode->i_zombie);
        up(&mount_sem);
@@ -1343,7 +1366,7 @@
        down(&nd.dentry->d_inode->i_zombie);
        if (!IS_DEADDIR(nd.dentry->d_inode)) {
                retval = -ENOMEM;
-               mnt = add_vfsmnt(&nd, sb->s_root, dev_name);
+               mnt = add_vfsmnt(&nd, sb->s_root, dev_name, NULL);
        }
        up(&nd.dentry->d_inode->i_zombie);
        if (!mnt)
@@ -1544,10 +1567,10 @@
                devfs_mk_symlink (NULL, "root", DEVFS_FL_DEFAULT,
                                  path + 5 + path_start, NULL, NULL);
                memcpy (path + path_start, "/dev/", 5);
-               vfsmnt = add_vfsmnt(NULL, sb->s_root, path + path_start);
+               vfsmnt = add_vfsmnt(NULL, sb->s_root, path + path_start, NULL);
        }
        else
-               vfsmnt = add_vfsmnt(NULL, sb->s_root, "/dev/root");
+               vfsmnt = add_vfsmnt(NULL, sb->s_root, "/dev/root", NULL);
        /* FIXME: if something will try to umount us right now... */
        if (vfsmnt) {
                set_fs_root(current->fs, vfsmnt, sb->s_root);
diff -ru /tmp/linux/linux-2.4.0-test5.tar.gz@/linux/include/linux/fs.h 
linux/include/linux/fs.h
--- /tmp/linux/linux-2.4.0-test5.tar.gz@/linux/include/linux/fs.h       Fri Jul 28 
08:14:13 2000
+++ linux/include/linux/fs.h    Fri Jul 28 08:34:00 2000
@@ -815,6 +815,10 @@
 extern void kern_umount(struct vfsmount *);
 extern int may_umount(struct vfsmount *);
 extern long do_mount(char *, char *, char *, unsigned long, void *);
+extern struct vfsmount *add_vfsmnt(struct nameidata *nd, struct dentry *root,
+                                   const char *dev_name,
+                                  struct mount_operations *mnt_op);
+extern int do_umount(struct vfsmount *mnt, int umount_root, int flags);
 
 
 extern int vfs_statfs(struct super_block *, struct statfs *);
@@ -1074,6 +1078,9 @@
 
 /* needed for stackable file system support */
 extern loff_t default_llseek(struct file *file, loff_t offset, int origin);
+
+/* lookup function for "virtual" files */
+extern struct dentry *(*lookup_virtual)(struct nameidata *, struct dentry *);
 
 extern int __user_walk(const char *, unsigned, struct nameidata *);
 extern int path_init(const char *, unsigned, struct nameidata *);
diff -ru /tmp/linux/linux-2.4.0-test5.tar.gz@/linux/include/linux/mount.h 
linux/include/linux/mount.h
--- /tmp/linux/linux-2.4.0-test5.tar.gz@/linux/include/linux/mount.h    Fri Jul 28 
08:14:14 2000
+++ linux/include/linux/mount.h Fri Jul 28 08:18:53 2000
@@ -14,6 +14,8 @@
 
 #define MNT_VISIBLE    1
 
+extern struct semaphore mount_sem;
+
 struct vfsmount
 {
        struct dentry *mnt_mountpoint;  /* dentry of mountpoint */
@@ -28,10 +30,26 @@
        atomic_t mnt_count;
        int mnt_flags;
 
-  char *mnt_devname;                   /* Name of device e.g. /dev/dsk/hda1 */
+       char *mnt_devname;              /* Name of device e.g. /dev/dsk/hda1 */
        struct list_head mnt_list;
        uid_t mnt_owner;
+       
+       struct mount_operations *mnt_op; /* Operations on vfsmount */
 };
+
+/*
+ * Mount operations: 
+ *   release(node)          - called when this node has been umounted
+ *   umount(node)           - called when the parent is being umounted
+ *
+ * mount_sem is held in all
+ */
+
+struct mount_operations {
+       void (*release) (struct vfsmount *);
+       void (*umount) (struct vfsmount *);
+};
+
 
 static inline struct vfsmount *mntget(struct vfsmount *mnt)
 {
diff -ru /tmp/linux/linux-2.4.0-test5.tar.gz@/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- /tmp/linux/linux-2.4.0-test5.tar.gz@/linux/kernel/ksyms.c   Fri Jul 28 08:14:14 
2000
+++ linux/kernel/ksyms.c        Fri Jul 28 08:18:53 2000
@@ -265,6 +265,9 @@
 EXPORT_SYMBOL(filemap_sync);
 EXPORT_SYMBOL(lock_page);
 
+/* lookup function for "virtual" files */ 
+EXPORT_SYMBOL(lookup_virtual);
+
 #if !defined(CONFIG_NFSD) && defined(CONFIG_NFSD_MODULE)
 EXPORT_SYMBOL(do_nfsservctl);
 #endif
@@ -321,6 +324,9 @@
 EXPORT_SYMBOL(kern_mount);
 EXPORT_SYMBOL(kern_umount);
 EXPORT_SYMBOL(may_umount);
+EXPORT_SYMBOL(add_vfsmnt);
+EXPORT_SYMBOL(do_umount);
+EXPORT_SYMBOL(mount_sem);
 
 /* executable format registration */
 EXPORT_SYMBOL(register_binfmt);
============================================================


===File ~/avfs/src/src/nredir/nredir.c======================
/* 
   Redirection kernel module for avfs/podfuk.

   Copyright (C) 1998-1999  Miklos Szeredi ([EMAIL PROTECTED])

   This file can be distributed either under the GNU LGPL, or under
   the GNU GPL. See the file COPYING.LIB and COPYING. 

   Needs kernel 2.4.X and the virtual file kernel patch.
   
   Compile with:
      gcc -O2 -Wall -Wstrict-prototypes -fno-strict-aliasing -pipe -D__KERNEL__ 
-DMODULE -D_LOOSE_KERNEL_NAMES -c nredir.c

   For podfuk, add -DPODFUK
*/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/coda_psdev.h>

#define NREDIR_VERSION "0.2"

#ifndef PODFUK
#define MAGIC_CHAR '@'
#else
#define MAGIC_CHAR '#'
#endif

#define OVERLAY_DIR "/overlay"
#define OVERLAY_DIR_LEN 8

#define path_ok(pwd) (pwd->d_parent == pwd || !list_empty(&pwd->d_hash))

static void print_mount(struct vfsmount *mnt)
{
        char *path;
        unsigned long page;
        
        page = __get_free_page(GFP_USER);
        if(page) {
                path = d_path(mnt->mnt_mountpoint, mnt->mnt_parent,
                              (char *) page, PAGE_SIZE);
                printk("   %s\n", path);
                free_page(page);
        }
}

static void nredir_umount(struct vfsmount *mnt) 
{
        int error;
        
        printk("trying to umount child:\n");
        error = do_umount(mntget(mnt), 0, 0);
        if(!error) 
                printk("SUCCESS\n");
        else
                printk("FAILED: %i\n", error);
}

static void nredir_release(struct vfsmount *mnt)
{
        struct dentry *mountpoint;
        printk("umounted overlay:\n");
        print_mount(mnt);

        MOD_DEC_USE_COUNT;
        mountpoint = mnt->mnt_mountpoint;
        if(list_empty(&mountpoint->d_vfsmnt)) {
                printk("dropped mountpoint\n");
                d_drop(mountpoint);
        }
        
}

static struct mount_operations nredir_mount_operations = 
{
        umount:                 nredir_umount,
        release:                nredir_release,
};

static void mount_it(struct vfsmount *mnt, struct dentry *dentry,
                     struct nameidata *ovnd)
{
        struct vfsmount *newmnt;
        struct nameidata newnd;
        
        printk(KERN_INFO "Mounting overlay\n");
        printk(KERN_INFO "old (%s,%i), new (%s, %i)\n",
               ovnd->dentry->d_name.name, (int) ovnd->dentry->d_inode,
               dentry->d_name.name, (int) dentry->d_inode);

        down(&mount_sem);
        newnd.dentry = dentry;
        newnd.mnt = mnt;
        newmnt = add_vfsmnt(&newnd, ovnd->dentry, ovnd->mnt->mnt_devname,
                            &nredir_mount_operations);
        up(&mount_sem);
        if(newmnt) 
                MOD_INC_USE_COUNT;
        
        if(newmnt)
            printk(KERN_INFO "ov mount OK\n");
        else
            printk(KERN_INFO "ov mount FAILED\n");
}

static char *get_ov_path(struct vfsmount *mnt, struct dentry *dentry)
{
        char *ovpath;
        char *path;
        int pathlen;
        unsigned long page;

        if(!path_ok(dentry))
                return NULL;
        
        page = __get_free_page(GFP_USER);
        if(!page)
                return NULL;
        
        path = d_path(dentry, mnt, (char *) page, PAGE_SIZE);
        pathlen = page + PAGE_SIZE - (unsigned int) path - 1;

        ovpath = kmalloc(OVERLAY_DIR_LEN + pathlen + 1, GFP_USER);
        if(ovpath) {
                char *s = ovpath;

                strcpy(s, OVERLAY_DIR);
                s += OVERLAY_DIR_LEN;
                strcpy(s, path);
        }

        free_page(page);
        return ovpath;
}
                
static int mount_overlay(struct vfsmount *mnt, struct dentry *dentry)
{
        char *ovpath;
        struct nameidata ovnd;
        int result = 0;
        
        ovpath = get_ov_path(mnt, dentry);
        if(ovpath) {
                int error = 0;
                int old_link_count;
                
                printk(KERN_INFO "ovpath = '%s'\n", ovpath);
                
                old_link_count = current->link_count;
                current->link_count = 0;
                if(path_init(ovpath, LOOKUP_POSITIVE, &ovnd))
                        error = path_walk(ovpath, &ovnd);
                current->link_count = old_link_count;
                
                if(!error) {
                        mount_it(mnt, dentry, &ovnd);
                        path_release(&ovnd);
                        result = 1;
                }
                
                kfree(ovpath);
        }

        return result;
}

static int mnt_list_member(struct dentry *dentry, struct vfsmount *mnt)
{
        struct list_head *curr;
        
        curr = dentry->d_vfsmnt.next;
        while(curr != &dentry->d_vfsmnt) {
                struct vfsmount *mntentry;

                mntentry = list_entry(curr, struct vfsmount, mnt_clash);
                if(mntentry->mnt_parent == mnt)
                        return 1;
                curr = curr->next;
        }

        return 0;
}

static int nredir_revalidate(struct dentry *dentry, int flag)
{
        struct super_block *sb;
        struct list_head *curr;
        struct list_head *next;
        struct vfsmount *mnt;

        /* FIXME: this is not SMP safe */
        sb = dentry->d_sb;
        curr = sb->s_mounts.next;
        while(curr != &sb->s_mounts) {
                next = curr->next;
                mnt = list_entry(curr, struct vfsmount, mnt_instances);
                if(!mnt_list_member(dentry, mnt))
                        mount_overlay(mnt, dentry);
                curr = next;
        }
        
        return 1;
}

static void nredir_dentry_release(struct dentry *dentry)
{
        printk("nredir_dentry_release: %s\n", dentry->d_name.name);
        MOD_DEC_USE_COUNT;
}

static struct dentry_operations nredir_dentry_operations = 
{
        d_revalidate:           nredir_revalidate,
        d_release:              nredir_dentry_release,
};


static struct dentry *nredir_lookup_virtual(struct nameidata *nd, 
                                          struct dentry *dentry)
{
#if 0
        printk(KERN_INFO "nredir_lookup_virtual: %s\n", dentry->d_name.name);
#endif
        
        /* FIXME: test should not refer to coda */
        if(strchr(dentry->d_name.name, MAGIC_CHAR) != 0 &&
           dentry->d_sb->s_magic != CODA_SUPER_MAGIC) {
                if(mount_overlay(nd->mnt, dentry)) {
                        /* This dentry is MINE from now on... */
                        if(dentry->d_op && dentry->d_op->d_release)
                                dentry->d_op->d_release(dentry);

                        dentry->d_op = &nredir_dentry_operations;
                        MOD_INC_USE_COUNT;
                        nredir_revalidate(dentry, 0);
                }
        }

        return dentry;
}

static int __init init_nredir(void)
{
    printk(KERN_INFO "nredir init (version %s)\n", NREDIR_VERSION);

    printk("lookup_virtual: %x\n", (int) lookup_virtual);
    lookup_virtual = nredir_lookup_virtual;

    /* FIXME: This is a bit too brutal approach */
    printk("shrinking dcache...\n");
    shrink_dcache();
    printk("done\n");

    return 0;
}


static void __exit cleanup_nredir(void)
{
    printk(KERN_INFO "nredir cleanup\n");

    lookup_virtual = NULL;

    printk("lookup_virtual: %x\n", (int) lookup_virtual);
}

module_init(init_nredir);
module_exit(cleanup_nredir);
============================================================

Reply via email to