Variant symlinks add the ability to embed variables in to the contents of 
symbolic links so their targets can change based on outside sources (user 
environment, uts, filesystems, etc.)

Signed-off-by: Ken Schmidt <[EMAIL PROTECTED]>
---
diff -urN linux-2.6.22.5.orig/include/linux/variant.h 
linux-2.6.22.5/include/linux/variant.h
--- linux-2.6.22.5.orig/include/linux/variant.h 1969-12-31 16:00:00.000000000 
-0800
+++ linux-2.6.22.5/include/linux/variant.h      2007-08-28 09:35:40.000000000 
-0700
@@ -0,0 +1,10 @@
+#ifndef _LINUX_VARIANT_H
+#define _LINUX_VARIANT_H
+
+int variant_func_register(int ( *variant_func)(const char *, int, char *, 
int), int priority, char *name);
+void variant_func_unregister(int id);
+int do_variant_lookup(const char *path, char **newpath);
+
+extern int variant_links_enabled;
+
+#endif /* _LINUX_VARIANT_H */
diff -urN linux-2.6.22.5.orig/fs/Kconfig linux-2.6.22.5/fs/Kconfig
--- linux-2.6.22.5.orig/fs/Kconfig      2007-08-22 16:23:54.000000000 -0700
+++ linux-2.6.22.5/fs/Kconfig   2007-09-23 09:16:04.000000000 -0700
@@ -524,6 +524,14 @@
 
          If unsure, say Y.
 
+config VARIANT
+       bool "Variant symlink support (EXPERIMENTAL)"
+       depends on EXPERIMENTAL
+       help
+         If you say Y here, all filesystems will have the ability to
+         dynamically redirect their symbolic link targets based on embedded
+         variables. If unsure, say N
+
 config QUOTA
        bool "Quota support"
        help
diff -urN linux-2.6.22.5.orig/fs/Makefile linux-2.6.22.5/fs/Makefile
--- linux-2.6.22.5.orig/fs/Makefile     2007-08-22 16:23:54.000000000 -0700
+++ linux-2.6.22.5/fs/Makefile  2007-09-23 09:15:25.000000000 -0700
@@ -63,6 +63,8 @@
 
 obj-$(CONFIG_PROFILING)                += dcookies.o
 obj-$(CONFIG_DLM)              += dlm/
+
+obj-$(CONFIG_VARIANT)          += variant/
  
 # Do not add any filesystems before this line
 obj-$(CONFIG_REISERFS_FS)      += reiserfs/
diff -urN linux-2.6.22.5.orig/fs/variant/Makefile 
linux-2.6.22.5/fs/variant/Makefile
--- linux-2.6.22.5.orig/fs/variant/Makefile     1969-12-31 16:00:00.000000000 
-0800
+++ linux-2.6.22.5/fs/variant/Makefile  2007-09-23 09:17:04.000000000 -0700
@@ -0,0 +1,5 @@
+#
+# Makefile for the variant symlinks
+#
+
+obj-$(CONFIG_VARIANT) += variant.o
diff -urN linux-2.6.22.5.orig/fs/namei.c linux-2.6.22.5/fs/namei.c
--- linux-2.6.22.5.orig/fs/namei.c      2007-08-22 16:23:54.000000000 -0700
+++ linux-2.6.22.5/fs/namei.c   2007-09-23 07:15:52.000000000 -0700
@@ -31,6 +31,7 @@
 #include <linux/file.h>
 #include <linux/fcntl.h>
 #include <linux/namei.h>
+#include <linux/variant.h>
 #include <asm/namei.h>
 #include <asm/uaccess.h>
 
@@ -539,10 +540,25 @@
 static __always_inline int __vfs_follow_link(struct nameidata *nd, const char 
*link)
 {
        int res = 0;
+#ifdef CONFIG_VARIANT
+       char *newlink = NULL;
+       int err;
+#endif
        char *name;
        if (IS_ERR(link))
                goto fail;
 
+#ifdef CONFIG_VARIANT
+       if (variant_links_enabled && strchr(link, '$')) {
+               err = do_variant_lookup(link, &newlink);
+               if (err) {
+                       path_release(nd);
+                       return err;
+               }
+               link = newlink;
+       }
+#endif
+
        if (*link == '/') {
                path_release(nd);
                if (!walk_init_root(link, nd))
@@ -551,6 +565,11 @@
        }
        res = link_path_walk(link, nd);
 out:
+#ifdef CONFIG_VARIANT
+       if (newlink == link)
+               kfree(newlink);
+#endif
+
        if (nd->depth || res || nd->last_type!=LAST_NORM)
                return res;
        /*
--- linux-2.6.22.5.unpacked/fs/variant/variant.c        1969-12-31 
16:00:00.000000000 -0800
+++ linux-2.6.22.5/fs/variant/variant.c 2007-09-24 04:26:57.000000000 -0700
@@ -0,0 +1,183 @@
+/*
+ *   Copyright (C) Pacific Northwest National Laboratory., 2007
+ *
+ *   This program 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.
+ *
+ *   This program 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
+ *
+ *   written by Kenneth P Schmidt (kenneth.schmidt <at> pnl.gov) for the
+ *   Department of Energy
+ */
+
+#include<linux/kernel.h>
+#include<linux/slab.h>
+#include<linux/string.h>
+#include<linux/limits.h>
+#include<linux/rwsem.h>
+#include<linux/module.h>
+#include<linux/variant.h>
+
+struct variant_func_module {
+       int id;
+
+       char *name;     /* name for possible reporting to user space (sysfs) */
+
+       /* priority lists the order for variable lookups. 0 - highest priority*/
+       int priority;
+
+       /*
+        * the callback function for each registered module.
+        * key points to the key in the original path
+        * keylen is the length of the key
+        * dest is the buffer to copy directly into
+        * destlen is the length allowed to write into the dest buffer
+        */
+       int ( *variant_lookup_func)(const char *key, int keylen, char *dest, 
int destlen);
+
+       struct list_head list;
+};
+
+static DECLARE_RWSEM(variant_module_rwsem);
+static LIST_HEAD(variant_func_list);
+static int variant_func_uniq;
+
+int variant_links_enabled = 1;
+
+int variant_func_register(int ( *variant_func)(const char *, int, char *, 
int), int priority, char *name)
+{
+       int retval;
+       int inserted = 0;
+       struct variant_func_module *new_module, *cur_module;
+       struct list_head *cur;
+
+       down_write(&variant_module_rwsem);
+       new_module = (struct variant_func_module *)kmalloc(
+                                       sizeof(struct variant_func_module),
+                                       GFP_KERNEL);
+       if (new_module) {
+               new_module->variant_lookup_func = variant_func;
+               retval = (new_module->id) = ++variant_func_uniq;
+               new_module->priority = priority;
+               new_module->name = (char *)kmalloc(strlen(name)+1, GFP_KERNEL);
+               strcpy(new_module->name, name);
+               list_for_each(cur, &variant_func_list) {
+                       cur_module = list_entry(cur, struct 
variant_func_module, list);
+                       if (cur_module->priority < priority) {
+                               list_add_tail(&new_module->list, cur);
+                               inserted = 1;
+                       }
+               }
+               if (!inserted)
+                       list_add_tail(&new_module->list, &variant_func_list);
+       } else {
+               retval = -1;
+       }
+       up_write(&variant_module_rwsem);
+       return retval;
+}
+EXPORT_SYMBOL(variant_func_register);
+
+void variant_func_unregister(int id)
+{
+       struct list_head *cur, *tmp;
+
+       down_write(&variant_module_rwsem);
+       list_for_each_safe(cur, tmp, &variant_func_list) {
+               if (list_entry(cur, struct variant_func_module, list)->id == 
id) {
+                       list_del(cur);
+                       kfree(list_entry(cur, struct variant_func_module, 
list));
+               }
+       }
+       up_write(&variant_module_rwsem);
+}
+EXPORT_SYMBOL(variant_func_unregister);
+
+static int variant_var_lookup(const char *key, int keylen, char *dest, int 
destlen)
+{
+       struct list_head *cur;
+       int modsize = 0;
+       struct variant_func_module *cur_module;
+
+       if (keylen == 0)
+               return 0;
+
+       down_read(&variant_module_rwsem);
+       list_for_each(cur, &variant_func_list) {
+               /*
+                * call each modules' lookup function
+                * until one of them returns something
+                */
+               cur_module = list_entry(cur, struct variant_func_module, list);
+               modsize = (*(cur_module)->variant_lookup_func)(key, keylen, 
dest, destlen);
+               if (modsize != 0)
+                       break;
+       }
+       up_read(&variant_module_rwsem);
+       return modsize;
+}
+
+/*make sure to free the returned string*/
+int do_variant_lookup(const char *path, char **newpath)
+{
+       const char *beg, *end, *cur;
+       char *tmppath;
+       int varsize;
+
+       *newpath = (char *)kmalloc(PATH_MAX, GFP_KERNEL);
+       if (*newpath == NULL) {
+               return -ENOMEM;
+       }
+
+       cur = beg = path;
+       tmppath = *newpath;
+       while ((cur = strchr(cur, '$'))) {
+               if ((cur[1] == '{') && (end = strchr(cur+2, '}'))) {
+
+                       /*
+                        * Copy everything before the variable into our new
+                        * buffer, making sure we didn't already overrun it
+                        */
+                       varsize = cur - beg;
+                       if (varsize + (tmppath - *newpath) > PATH_MAX) {
+                               kfree(*newpath);
+                               return -ENAMETOOLONG;
+                       }
+                       strncpy(tmppath, beg, varsize);
+                       tmppath += varsize;
+
+                       varsize = variant_var_lookup(cur + 2, end - cur - 2, 
tmppath, PATH_MAX - (tmppath - *newpath));
+                       /*
+                        * if zero was returned, then there was no variable
+                        * replacement. Leave the variable in the path
+                        */
+                       if (varsize == 0) {
+                               varsize = end - cur + 1;
+                               if (varsize + (tmppath - *newpath) > PATH_MAX) {
+                                       kfree(*newpath);
+                                       return -ENAMETOOLONG;
+                               }
+                               strncpy(tmppath, cur, varsize);
+                       }
+                       tmppath += varsize;
+
+                       cur = beg = end;
+                       beg++;
+               }
+               cur++;
+       }
+       /* copy in the rest of the path */
+       strncpy(tmppath, beg, PATH_MAX - (tmppath - *newpath));
+       (*newpath)[PATH_MAX - 1] = '\0';
+       return 0;
+}
+
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to