Back to index

avfs  1.0.1
redir2.c
Go to the documentation of this file.
00001 #include <linux/module.h>
00002 #include <linux/fs.h>
00003 #include <linux/namei.h>
00004 #include <linux/mount.h>
00005 #include <linux/string.h>
00006 #include <linux/init.h>
00007 #include <linux/proc_fs.h>
00008 #include <asm/uaccess.h>
00009 
00010 #define REDIR2_VERSION "0.0"
00011 
00012 #define AVFS_MAGIC_CHAR '#'
00013 #define OVERLAY_DIR "/overlay"
00014 #define OVERLAY_DIR_LEN 8
00015 
00016 #define FUSE_MAGIC 0x65735546
00017 
00018 static struct dentry *(*orig_lookup)(struct inode *, struct dentry *,
00019                                  struct nameidata *);
00020 
00021 static struct dentry *(**orig_lookup_ptr)(struct inode *, struct dentry *,
00022                                  struct nameidata *);
00023 
00024 
00025 static struct vfsmount *orig_mount;
00026 static struct semaphore redir2_sem;
00027 static int mount_pid = -1;
00028 static int lookup_pid = -1;
00029 static LIST_HEAD(redir2_mounts);
00030 static struct proc_dir_entry *redir2_proc_dir;
00031 
00032 
00033 
00034 struct redir2_mount {
00035        struct list_head list;
00036        struct vfsmount *mnt;
00037 };
00038 
00039 static struct super_operations redir2_dummy_super_operations;
00040 static struct super_block redir2_dummy_sb = {
00041        .s_op = &redir2_dummy_super_operations,
00042 };
00043 
00044 
00045 static int is_avfs(const unsigned char *name, unsigned int len)
00046 {
00047        for (; len--; name++)
00048               if (*name == AVFS_MAGIC_CHAR)
00049                      return 1;
00050        return 0;
00051 }
00052 
00053 static char * my_d_path( struct dentry *dentry, struct vfsmount *vfsmnt,
00054                      struct dentry *root, struct vfsmount *rootmnt,
00055                      char *buffer, int buflen)
00056 {
00057        char * end = buffer+buflen;
00058        char * retval;
00059        int namelen;
00060 
00061        *--end = '\0';
00062        buflen--;
00063        if (!IS_ROOT(dentry) && d_unhashed(dentry)) 
00064               return ERR_PTR(-ENOENT);
00065 
00066        if (buflen < 1)
00067               goto Elong;
00068        /* Get '/' right */
00069        retval = end-1;
00070        *retval = '/';
00071 
00072        for (;;) {
00073               struct dentry * parent;
00074 
00075               if (dentry == root && vfsmnt == rootmnt)
00076                      break;
00077               if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
00078                      /* Global root? */
00079                      spin_lock(&vfsmount_lock);
00080                      if (vfsmnt->mnt_parent == vfsmnt) {
00081                             spin_unlock(&vfsmount_lock);
00082                             goto global_root;
00083                      }
00084                      dentry = vfsmnt->mnt_mountpoint;
00085                      vfsmnt = vfsmnt->mnt_parent;
00086                      spin_unlock(&vfsmount_lock);
00087                      continue;
00088               }
00089               parent = dentry->d_parent;
00090               prefetch(parent);
00091               namelen = dentry->d_name.len;
00092               buflen -= namelen + 1;
00093               if (buflen < 0)
00094                      goto Elong;
00095               end -= namelen;
00096               memcpy(end, dentry->d_name.name, namelen);
00097               *--end = '/';
00098               retval = end;
00099               dentry = parent;
00100        }
00101 
00102        return retval;
00103 
00104 global_root:
00105        namelen = dentry->d_name.len;
00106        buflen -= namelen;
00107        if (buflen < 0)
00108               goto Elong;
00109        retval -= namelen-1; /* hit the slash */
00110        memcpy(retval, dentry->d_name.name, namelen);
00111        return retval;
00112 Elong:
00113        return ERR_PTR(-ENAMETOOLONG);
00114 }
00115 
00116 static int redir2_permission(struct inode *inode, int mask,
00117                           struct nameidata *nd)
00118 {
00119        return -ENOENT;
00120 }
00121 
00122 static int redir2_getattr(struct vfsmount *mnt, struct dentry *entry,
00123                        struct kstat *stat)
00124 {
00125        return -ENOENT;
00126 }
00127 
00128 static struct dentry *redir2_dummy_lookup(struct inode *dir, struct dentry *entry,
00129                                struct nameidata *nd)
00130 {
00131        return ERR_PTR(-ENOENT);
00132 }
00133 
00134 
00135 static struct inode_operations redir2_inode_operations = {
00136        .permission   = redir2_permission,
00137        .getattr      = redir2_getattr,
00138        .lookup              = redir2_dummy_lookup,
00139 };
00140 
00141 static int mount_avfs(struct dentry *dentry, struct vfsmount *mnt,
00142                     char *path, int mode)
00143 {
00144        struct inode *inode;
00145        struct redir2_mount *newmnt;
00146 
00147        newmnt = kmalloc(sizeof(struct redir2_mount), GFP_KERNEL);
00148        if (!newmnt)
00149               return -ENOMEM;
00150        
00151        inode = new_inode(&redir2_dummy_sb);
00152        if (!inode) {
00153               kfree(newmnt);
00154               return -ENOMEM;
00155        }
00156 
00157        inode->i_mode = mode;
00158        inode->i_op = &redir2_inode_operations;
00159        d_instantiate(dentry, inode);
00160        
00161        char *argv[] = { "/usr/local/bin/redir2mount",
00162                       path, path + OVERLAY_DIR_LEN, NULL };
00163        char *envp[] = { NULL };
00164        int ret;
00165        ret = call_usermodehelper(argv[0], argv, envp, 1);
00166        printk("mount ret: %i\n", ret);
00167        if (ret) {
00168               kfree(newmnt);
00169               return -EINVAL;
00170        }
00171        newmnt->mnt = lookup_mnt(mnt, dentry);
00172        if (!newmnt->mnt) {
00173               printk("not mounted\n");
00174               kfree(newmnt);
00175               return -EINVAL;
00176        }
00177 
00178        __module_get(THIS_MODULE);
00179        list_add(&newmnt->list, &redir2_mounts);
00180        printk("new mount: %p\n", newmnt->mnt);
00181 
00182        return 0;
00183 }
00184 
00185 static int exists_avfs(char *path, int *modep)
00186 {
00187        int err;
00188        struct nameidata avfsnd;
00189 
00190        printk("lookup_avfs: '%s'\n", path);
00191 
00192        avfsnd.last_type = LAST_ROOT;
00193        avfsnd.flags = 0;
00194        avfsnd.mnt = mntget(orig_mount);
00195        avfsnd.dentry = dget(orig_mount->mnt_sb->s_root);
00196        err = path_walk(path, &avfsnd);
00197        if (err)
00198               return 0;
00199 
00200        if(!avfsnd.dentry->d_inode) {
00201               path_release(&avfsnd);
00202               return 0;
00203        }
00204        *modep = avfsnd.dentry->d_inode->i_mode;
00205        path_release(&avfsnd);
00206        return 1;
00207 }
00208 
00209 static int lookup_avfs(struct dentry *dentry, struct vfsmount *mnt)
00210 {
00211        char *page;
00212        char *path;
00213        int err;
00214        
00215        err = -ENOMEM;
00216        page = (char *) __get_free_page(GFP_KERNEL);
00217        if (page) {
00218               spin_lock(&dcache_lock);
00219               path = my_d_path(dentry, mnt, mnt->mnt_sb->s_root, mnt, page, PAGE_SIZE);
00220               spin_unlock(&dcache_lock);
00221               err = -ENAMETOOLONG;
00222               if (!IS_ERR(path) && page + OVERLAY_DIR_LEN < path) {
00223                      int mode;
00224                      path -= OVERLAY_DIR_LEN;
00225                      memcpy(path, OVERLAY_DIR, OVERLAY_DIR_LEN);
00226                      
00227                      if (exists_avfs(path, &mode))
00228                             err = mount_avfs(dentry, mnt, path, mode);
00229                      else
00230                             err = -ENOENT;
00231               }
00232               free_page((unsigned long) page);
00233        }
00234        return err;
00235 }
00236 
00237 static int redir2_dentry_revalidate(struct dentry *dentry,
00238                                 struct nameidata *nd)
00239 {
00240        //printk("redir2_dentry_revalidate\n");
00241        if (dentry->d_flags & DCACHE_AUTOFS_PENDING) {
00242               if (current->pid == mount_pid)
00243                      return 1;
00244 
00245               printk("redir2_dentry_revalidate: still pending\n");
00246               down(&redir2_sem);
00247               printk("redir2_dentry_revalidate: OK\n");
00248               up(&redir2_sem);
00249        }
00250        if (dentry->d_flags & DCACHE_AUTOFS_PENDING)
00251               BUG();
00252        if (!dentry->d_inode || d_unhashed(dentry))
00253               return 0;
00254        return 1;
00255 }
00256 
00257 static int redir2_dentry_delete(struct dentry *dentry)
00258 {
00259        printk("redir2_dentry_delete %p '%.*s'\n", dentry,
00260               dentry->d_name.len, dentry->d_name.name);
00261        
00262        module_put(THIS_MODULE);
00263        return 1;
00264 }
00265 
00266 static struct dentry_operations redir2_dentry_operations = {
00267        .d_revalidate = redir2_dentry_revalidate,
00268        .d_delete     = redir2_dentry_delete,
00269 };
00270 
00271 static inline int is_create(struct nameidata *nd)
00272 {
00273        if (!nd)
00274               return 1;
00275        if ((nd->flags & LOOKUP_CREATE) && !(nd->flags & LOOKUP_CONTINUE))
00276               return 1;
00277        return 0;
00278 }
00279 
00280 static int lookup_maybeavfs(struct inode *dir, struct dentry *dentry,
00281                          struct nameidata *nd)
00282 {
00283        int err;
00284 
00285        if (!try_module_get(THIS_MODULE))
00286               return -EBUSY;
00287 
00288        down(&redir2_sem);
00289        lookup_pid = current->pid;
00290        printk("redir2_dentry_add %p '%.*s'\n", dentry,
00291               dentry->d_name.len, dentry->d_name.name);
00292        mount_pid = -1;
00293        dentry->d_op = &redir2_dentry_operations;
00294        dentry->d_flags |= DCACHE_AUTOFS_PENDING;
00295        d_add(dentry, NULL);
00296        up(&dir->i_sem);
00297        err = lookup_avfs(dentry, nd->mnt);
00298        if (err)
00299               d_drop(dentry);
00300        dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
00301        lookup_pid = -1;
00302        up(&redir2_sem);
00303        down(&dir->i_sem);
00304        return err;
00305 }
00306 
00307 static struct dentry *redir2_lookup(struct inode *dir, struct dentry *dentry,
00308                                 struct nameidata *nd)
00309 {
00310        int err;
00311        //printk("lookup %.*s\n", dentry->d_name.len, dentry->d_name.name);
00312 
00313        if (current->pid == lookup_pid || is_create(nd) || 
00314            !is_avfs(dentry->d_name.name, dentry->d_name.len))
00315               return orig_lookup(dir, dentry, nd);
00316 
00317        err = lookup_maybeavfs(dir, dentry, nd);
00318        if (err)
00319               return ERR_PTR(err);
00320        return NULL;
00321 }
00322 
00323 static void redir2_release_mount(struct redir2_mount *mnt)
00324 {
00325        printk("releasing mount: %p\n", mnt->mnt);
00326        mntput(mnt->mnt);
00327        list_del(&mnt->list);
00328        kfree(mnt);
00329        module_put(THIS_MODULE);
00330 }
00331 
00332 static int umount_avfs(struct redir2_mount *mnt, char *path)
00333 {
00334        char *argv[] = { "/usr/local/bin/redir2mount", "-", path, NULL };
00335        char *envp[] = { NULL };
00336        int ret;
00337        redir2_release_mount(mnt);
00338        printk("umount\n");
00339        ret = call_usermodehelper(argv[0], argv, envp, 1);
00340        printk("umount ret: %i\n", ret);
00341        if (ret)
00342               return -EINVAL;
00343 
00344        return 0;
00345 }
00346 
00347 static void redir2_try_umount(struct redir2_mount *mnt)
00348 {
00349        char *page;
00350        char *path;
00351        struct dentry *dentry;
00352        struct vfsmount *pmnt;
00353 
00354        page = (char *) __get_free_page(GFP_KERNEL);
00355        if (!page)
00356               return;
00357 
00358        spin_lock(&vfsmount_lock);
00359        if (mnt->mnt->mnt_parent == mnt->mnt) {
00360               /* Already unmounted */
00361               spin_unlock(&vfsmount_lock);
00362               redir2_release_mount(mnt);
00363               free_page((unsigned long) page);
00364               return;
00365        }
00366        pmnt = mntget(mnt->mnt->mnt_parent);
00367        dentry = dget(mnt->mnt->mnt_mountpoint);
00368        spin_unlock(&vfsmount_lock);
00369 
00370        spin_lock(&dcache_lock);
00371        path = my_d_path(dentry, pmnt, pmnt->mnt_sb->s_root, pmnt, page, PAGE_SIZE);
00372        spin_unlock(&dcache_lock);
00373        if (!IS_ERR(path))
00374               umount_avfs(mnt, path);
00375        free_page((unsigned long) page);
00376        dput(dentry);
00377        mntput(pmnt);
00378 }
00379 
00380 static int redir2_flush(struct file *file, const char __user *buffer,
00381                      unsigned long count, void *data)
00382 {
00383        struct redir2_mount *mnt;
00384        struct redir2_mount *next;
00385        printk("redir2_flush (%i)\n", current->pid);
00386        down(&redir2_sem);
00387        list_for_each_entry_safe (mnt, next, &redir2_mounts, list) {
00388               int cnt = atomic_read(&mnt->mnt->mnt_count);
00389               printk("mount %p has count %u\n", mnt->mnt, cnt);
00390               if (cnt <= 2) {
00391                      redir2_try_umount(mnt);
00392 //                   break;
00393               }
00394        }
00395        up(&redir2_sem);
00396        return count;
00397 }
00398 
00399 static int mount_pid_write(struct file *file, const char __user *buffer,
00400                      unsigned long count, void *data)
00401 {
00402        char buf[32];
00403        if(count > sizeof(buf))
00404               return -EINVAL;
00405         if(copy_from_user(buf, buffer, count))
00406                 return -EFAULT;
00407         mount_pid = simple_strtol(buf, NULL, 10);
00408         return count;
00409 
00410 }
00411 
00412 static void redir2_init_proc(void)
00413 {
00414        redir2_proc_dir = proc_mkdir("redir2", proc_root_fs);
00415        if (redir2_proc_dir) {
00416               struct proc_dir_entry *e;
00417               redir2_proc_dir->owner = THIS_MODULE;
00418               e = create_proc_entry("mount_pid", S_IFREG | 0200, redir2_proc_dir);
00419               if (e) {
00420                      e->owner = THIS_MODULE;
00421                      e->write_proc = mount_pid_write;
00422               }
00423               e = create_proc_entry("flush", S_IFREG | 0200, redir2_proc_dir);
00424               if (e) {
00425                      e->owner = THIS_MODULE;
00426                      e->write_proc = redir2_flush;
00427               }
00428        }
00429 }
00430 
00431 static int __init init_redir2(void)
00432 {
00433        printk(KERN_INFO "redir2 init (version %s)\n", REDIR2_VERSION);
00434 
00435        sema_init(&redir2_sem, 1);
00436        redir2_init_proc();
00437        read_lock(&current->fs->lock);
00438        orig_mount = mntget(current->fs->rootmnt);
00439        orig_lookup_ptr = &current->fs->root->d_inode->i_op->lookup;
00440        orig_lookup = *orig_lookup_ptr;
00441        *orig_lookup_ptr = redir2_lookup;
00442        read_unlock(&current->fs->lock);
00443 
00444        /* FIXME: This is a bit too brutal approach */
00445        printk("shrinking dcache...\n");
00446        shrink_dcache_sb(orig_mount->mnt_sb);
00447        printk("done\n");
00448 
00449        return 0;
00450 }
00451 
00452 static void __exit exit_redir2(void)
00453 {
00454        printk(KERN_INFO "redir2 cleanup\n");
00455 
00456        if (orig_lookup_ptr)
00457               *orig_lookup_ptr = orig_lookup;
00458        mntput(orig_mount);
00459        if (redir2_proc_dir) {
00460               remove_proc_entry("mount_pid", redir2_proc_dir);
00461               remove_proc_entry("flush", redir2_proc_dir);
00462               remove_proc_entry("redir2", proc_root_fs);
00463        }
00464 }
00465 
00466 
00467 module_init(init_redir2)
00468 module_exit(exit_redir2)
00469 
00470 
00471 MODULE_LICENSE("GPL");
00472 
00473 /* 
00474  * Local Variables:
00475  * indent-tabs-mode: t
00476  * c-basic-offset: 8
00477  * End:
00478  */