2007-09-28 18:16:39

by Schmidt, Kenneth P

[permalink] [raw]
Subject: [patch 01/02] vfs: variant symlinks

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;
+}
+


2007-09-28 18:22:35

by Jan Dittmer

[permalink] [raw]
Subject: Re: [patch 01/02] vfs: variant symlinks

Ken Schmidt wrote:
> 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.)

Could you elaborate why this is needed and what part cannot
be solved in userspace (linkfarm on tmpfs or intelligent
scripts)?

Thanks,

Jan

2007-09-28 18:30:41

by Al Viro

[permalink] [raw]
Subject: Re: [patch 01/02] vfs: variant symlinks

On Fri, Sep 28, 2007 at 10:16:01AM -0700, Ken Schmidt wrote:
> 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.)

NAK - it's too ugly to live.