Hi!

I've been looking at the possibility of cleanly implementing virtual
files (podfuk/avfs style) with the multiple mount thing in 2.4
kernels. Here is how it would work:

1) There needs to be a global lookup hook in VFS. If a real lookup
fails, the global lookup is invoked. The cached lookup path is not
effected.

2) The global lookup checks whether the name refers to a virtual file
(contains the magic char & exists in /overlay). If it does, then the
file/dir from overlay is mounted on top of the negative dentry. The
dentry is not filled in, so the virtual file remains invisible.

For 1) the VFS needs to be modified, but with infinitesimal
performance effect. For 2) a kernel module could be used.

I've prepared a kernel patch against 2.4.0-test4 (just for comments on
this, NOT for inclusion in the main kernel), and a kernel module. I've
tested it with avfs, but it should also work for podfuk.

Problems:

 - The filesystem will be littered with these loopback mounts. This
   should be cleared upon unmount, and possibly when the dcache is
   shrunk. There was a similar requirement for new autofs IIRC.

 - Creation/removal of virtual files are not handled by this code.

Comments?

Miklos

===File /tmp/d==============================================
diff -ru linux/fs/namei.c /spare/src/linux/fs/namei.c
--- linux/fs/namei.c    Fri Jul  7 21:52:56 2000
+++ /spare/src/linux/fs/namei.c Tue Jul 18 14:52:28 2000
@@ -98,6 +98,9 @@
  * XEmacs seems to be relying on it...
  */
 
+/* for avfs */ 
+lookup_global_t lookup_global = NULL;
+
 /* 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,18 @@
                        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;
+                               /* for avfs */
+                               if (!dentry->d_inode && lookup_global)
+                                       lookup_global(nd, dentry);
+                       }
                }
-               up(&dir->i_sem);
                return result;
        }
 
@@ -487,7 +497,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;
@@ -550,7 +560,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 linux/fs/super.c /spare/src/linux/fs/super.c
--- linux/fs/super.c    Thu Jul  6 01:10:44 2000
+++ /spare/src/linux/fs/super.c Tue Jul 18 15:41:31 2000
@@ -297,9 +297,9 @@
  *     dentry (if any).
  */
 
-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 vfsmount *mnt;
        struct super_block *sb = root->d_inode->i_sb;
diff -ru linux/include/linux/fs.h /spare/src/linux/include/linux/fs.h
--- linux/include/linux/fs.h    Thu Jul 13 07:01:34 2000
+++ /spare/src/linux/include/linux/fs.h Tue Jul 18 15:42:45 2000
@@ -819,6 +819,8 @@
 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);
 
 
 extern int vfs_statfs(struct super_block *, struct statfs *);
@@ -1076,6 +1078,10 @@
 
 /* needed for stackable file system support */
 extern loff_t default_llseek(struct file *file, loff_t offset, int origin);
+
+/* for avfs */
+typedef struct dentry *(*lookup_global_t)(struct nameidata *, struct dentry *);
+extern lookup_global_t lookup_global;
 
 extern int __user_walk(const char *, unsigned, struct nameidata *);
 extern int path_init(const char *, unsigned, struct nameidata *);
diff -ru linux/kernel/ksyms.c /spare/src/linux/kernel/ksyms.c
--- linux/kernel/ksyms.c        Wed Jul 12 19:06:17 2000
+++ /spare/src/linux/kernel/ksyms.c     Tue Jul 18 15:44:17 2000
@@ -258,6 +258,9 @@
 EXPORT_SYMBOL(filemap_sync);
 EXPORT_SYMBOL(lock_page);
 
+/* for avfs */
+EXPORT_SYMBOL(lookup_global);
+
 #if !defined(CONFIG_NFSD) && defined(CONFIG_NFSD_MODULE)
 EXPORT_SYMBOL(do_nfsservctl);
 #endif
@@ -314,6 +317,7 @@
 EXPORT_SYMBOL(kern_mount);
 EXPORT_SYMBOL(kern_umount);
 EXPORT_SYMBOL(may_umount);
+EXPORT_SYMBOL(add_vfsmnt);
 
 /* executable format registration */
 EXPORT_SYMBOL(register_binfmt);
============================================================


===File ~/nredir/nredir.c===================================
#include <linux/module.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.1"

#define AVFS_MAGIC_CHAR '@'
#define OVERLAY_DIR "/overlay"
#define OVERLAY_DIR_LEN 8

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

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

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

        ovpath = kmalloc(OVERLAY_DIR_LEN + pathlen + 1 + 
                          strlen(dentry->d_name.name) + 1, GFP_USER);
        if(ovpath) {
                char *s = ovpath;

                strcpy(s, OVERLAY_DIR);
                s += OVERLAY_DIR_LEN;
                strcpy(s, path);
                s += pathlen;
                *s = '/';
                s++;
                strcpy(s, dentry->d_name.name);
        }

        free_page(page);
        return ovpath;
}

static void mount_overlay(struct nameidata *nd, struct dentry *dentry,
                          struct nameidata *ovnd)
{
        struct nameidata newnd = *nd;
        newnd.dentry = dentry;

        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);


        if(add_vfsmnt(&newnd, ovnd->dentry, ovnd->mnt->mnt_devname))
                printk(KERN_INFO "ov mount OK\n");
        else
                printk(KERN_INFO "ov mount FAILED\n");
}

static struct dentry *avfs_lookup_global(struct nameidata *nd, 
                                     struct dentry *dentry)
{
#if 0
        printk(KERN_INFO "avfs_lookup_global: %s\n", dentry->d_name.name);
#endif
        
        if(strchr(dentry->d_name.name, AVFS_MAGIC_CHAR) != 0 &&
           dentry->d_sb->s_magic != CODA_SUPER_MAGIC) {
                char *ovpath;
                struct nameidata ovnd;

                ovpath = get_ov_path(nd, dentry);
                if(ovpath) {
                        int error = 0;

                        printk(KERN_INFO "ovpath = '%s'\n", ovpath);

                        if(path_init(ovpath, LOOKUP_POSITIVE, &ovnd))
                                error = path_walk(ovpath, &ovnd);

                        if(!error) {
                                mount_overlay(nd, dentry, &ovnd);
                                path_release(&ovnd);
                        }
                        
                        kfree(ovpath);
                }
        }

        return dentry;
}

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

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

    lookup_global = avfs_lookup_global;

    return 0;
}


void cleanup_module(void)
{
    printk(KERN_INFO "nredir cleanup\n");

    lookup_global = 0;

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

============================================================

Reply via email to