diff -ur linux.orig/Documentation/Configure.help linux/Documentation/Configure.help
--- linux.orig/Documentation/Configure.help	Fri Jun  7 15:28:46 1996
+++ linux/Documentation/Configure.help	Wed Jul  3 11:55:41 1996
@@ -2674,6 +2674,119 @@
   servers. At the time of writing none of these are available. Unless
   you need this feature say N.
 
+Filename translation support
+CONFIG_TRANS_NAMES
+  Normally used only when you want diskless clients to mount the root
+  filesystem of the server. If unsure, or if you don't have clients, select N.
+  When selected, filenames, directory names etc become context-sensitive.
+  If you have a file named "/etc/config#host=banana#", it will appear
+  (by default) as hardlinked to "/etc/config" on host "banana", while on host
+  "mango" another file "/etc/config#host=mango#" will appear as been
+  hardlinked to "/etc/config". The default behaviour can be changed
+  by setting the _first_ environment variable NAMETRANS to a colon-separated
+  list of suffixes which are tried in the specified order. For example,
+  in 'env - NAMETRANS=#host=mango#:#ktype=diskless# "`env`" command ...' the
+  command will see the same files as if it had been executed on host "mango"
+  with a diskless kernel. Using NAMETRANS supersedes _all_ default
+  translations. Thus translations can be generally switched off by an
+  empty list, e.g. 'env - NAMETRANS= "`env`" command ...'.
+  Note that some system utililies like tar, dump, restore should
+  be used with translation switched off, in order to avoid doubled
+  space in archive files and when extracting from them. Also, make sure
+  that nfsd, mountd (and similar ones like samba daemons) run without
+  translation, in order to avoid doubled (or even wrong) translation
+  at the server and at the client. You can automatically force the creation
+  of context-dependent filenames if there exists a template filename like
+  "/etc/mtab#host=CREATE#". As soon as a process running on "mango" tries
+  to create a file "/etc/mtab", the version "/etc/mtab#host=mango#" is
+  created instead (which appears as hardlinked to "/etc/mtab").
+  Note that if you want to make "/etc/fstab" context-dependend, you should
+  execute "touch /etc/mtab#host=CREATE#" and
+  "touch /etc/mtab.tmp#host=CREATE#", because mount, umount and others
+  running on different hosts would otherwise try to create one shared
+  /etc/mtab which would result in a clash. Also one should execute
+  "touch /etc/nologin#host=CREATE#" to prevent global side effects from
+  shutdown resp. runlevel.
+
+Failproof translation
+CONFIG_TRANS_PROOF
+  When selected, context-sensitive files will take precedence over
+  normal files. For example, if you have an ordinary file "/etc/config"
+  and you later create another file "/etc/config#host=banana#", both
+  the pseudo-hardlinked version "/etc/config" whose contents is identical
+  with "/etc/config#host=banana#" and your original "/etc/config" will
+  appear in your directory listing, showing the name "config" twice
+  (but perhaps with different length, flags etc). By enabling this option,
+  you will get the contents of "/etc/config#host=banana#" in preference
+  over the old "/etc/config" if there is any conflict. You can disable
+  this option if you are either sure that you have no such conflicts
+  in your system, or if you explicitly want the original "/etc/config" to take
+  precedence, or if you absolutely need some small performance gain
+  when switching this option off. In unsure, say Y.
+
+Restrict translation to gid
+CONFIG_TRANS_RESTRICT
+  When selected, default translations are carried out only if the parent
+  directory of the context-sensitive file belongs to a specific group id
+  (gid). Trying to translate names everywhere will decrease performance of
+  file openings. Normally translations are used only in system configuration
+  files but not in ordinary user filespace. So you should change the gid of
+  directories containing context-dependent files to some special group like
+  "adm" (group id 4) and enable this option. As a result, users will not
+  notice any performance degradation resulting from filename translation.
+  Note that translations resulting from the first environment variable
+  "NAMETRANS=..." are always carried out regardless of the gid of directories.
+  Beware, before turning on this option make sure all directories containing
+  context-dependent files belong to the special group, or system
+  initialization may fail. In unsure, select N.
+
+Group id (gid) for translation restriction
+CONFIG_TRANS_GID
+  Default name translations will be carried out only inside directories
+  belonging to the group id (gid) you can specify here.
+  Default is 4 (group "adm").
+
+Nodename (hostname) translation
+CONFIG_TR_NODENAME
+  Enables translation of name suffixes like in "/etc/config#host=banana#".
+  The syntax is <filename>#host=<hostname>#. The hostname can be queried
+  with the command "uname -n". Normally this option is used heavily when
+  translation is enabled. If unsure, say Y.
+
+Kerneltype translation
+CONFIG_TR_KERNTYPE
+  Enables translation of name suffixes like in "/etc/config#ktype=default#".
+  The syntax is <filename>#ktype=<string>#. The string is hard compiled
+  in the kernel by the following option. Use if you want to create
+  different kernels with different behaviour. For example, use the string
+  "default" on your server, and use "diskless" on all your diskless clients
+  (and perhaps "dataless" on dataless clients). This way you can avoid
+  dozens of "config#host=<something># with same contents and you have no
+  effort when new machines are added. If unsure, say Y.
+
+String for kerneltype translation
+CONFIG_KERNTYPE
+  Enter the string you want to compile into the kernel. The string
+  will be used as context in context-depenant file like
+  "/etc/config#ktype=default#". If your kernel is to be used on a server,
+  you probably can use "default" here. If your kernel is intended for
+  a diskless client, you probably should enter "diskless" here.
+
+Machine type translation
+CONFIG_TR_MACHINE
+  Enables translation of name suffixes like in "/etc/config#machine=i486#".
+  The syntax is <filename>#machine=<id>#. The machine types can be queried
+  with the command "uname -m". Normally used only on multi-architecture
+  installations. If unsure, say Y.
+
+System name translation
+CONFIG_TR_SYSNAME
+  Enables translation of name suffixes like in "/etc/config#system=Linux#".
+  The syntax is <filename>#system=<id>#. The system name can be queried
+  with the command "uname -s". Currently only supportet by Linux, but
+  hopefully other operating systems will pick up the idea of context-dependent
+  translations. If unsure, say Y.
+
 Minix fs support
 CONFIG_MINIX_FS
   Minix is a simple operating system used in many classes about
diff -ur linux.orig/arch/i386/defconfig linux/arch/i386/defconfig
--- linux.orig/arch/i386/defconfig	Wed Jun  5 13:51:03 1996
+++ linux/arch/i386/defconfig	Wed Jul  3 11:42:45 1996
@@ -127,6 +127,15 @@
 #
 # CONFIG_QUOTA is not set
 # CONFIG_LOCK_MANDATORY is not set
+# CONFIG_TRANS_NAMES is not set
+CONFIG_TRANS_PROOF=y
+# CONFIG_TRANS_RESTRICT is not set
+CONFIG_TRANS_GID=4
+CONFIG_TR_NODENAME=y
+CONFIG_TR_KERNTYPE=y
+CONFIG_KERNTYPE="default"
+# CONFIG_TR_MACHINE is not set
+# CONFIG_TR_SYSNAME is not set
 CONFIG_MINIX_FS=y
 # CONFIG_EXT_FS is not set
 CONFIG_EXT2_FS=y
diff -ur linux.orig/fs/Config.in linux/fs/Config.in
--- linux.orig/fs/Config.in	Wed Jun  5 12:24:45 1996
+++ linux/fs/Config.in	Wed Jul  3 11:25:06 1996
@@ -6,6 +6,22 @@
 
 bool	 'Quota support' CONFIG_QUOTA
 bool	 'Mandatory lock support' CONFIG_LOCK_MANDATORY
+bool     'Translate filename suffixes' CONFIG_TRANS_NAMES
+if [ "$CONFIG_TRANS_NAMES" = "y" ]; then
+  bool '  Failproof translation mode' CONFIG_TRANS_PROOF
+  bool '  Restrict translation to specific gid' CONFIG_TRANS_RESTRICT
+  if [ "$CONFIG_TRANS_RESTRICT" = "y" ]; then
+    int '    Enter gid to compile in' CONFIG_TRANS_GID 4
+  fi
+  bool '  Translate nodename' CONFIG_TR_NODENAME
+  bool '  Translate compiled-in kerneltype' CONFIG_TR_KERNTYPE
+  if [ "$CONFIG_TR_KERNTYPE" = "y" ]; then
+    string '    Enter kerneltype string to compile in' CONFIG_KERNTYPE 'default'
+  fi
+  bool '  Translate machine type' CONFIG_TR_MACHINE
+  bool '  Translate sysname' CONFIG_TR_SYSNAME
+fi
+
 tristate 'Minix fs support' CONFIG_MINIX_FS
 tristate 'Extended fs support' CONFIG_EXT_FS
 tristate 'Second extended fs support' CONFIG_EXT2_FS
diff -ur linux.orig/fs/namei.c linux/fs/namei.c
--- linux.orig/fs/namei.c	Tue Apr 16 09:27:10 1996
+++ linux/fs/namei.c	Wed Jul  3 12:15:54 1996
@@ -17,6 +17,8 @@
 #include <linux/fcntl.h>
 #include <linux/stat.h>
 #include <linux/mm.h>
+#include <linux/config.h>
+
 
 #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
 
@@ -145,6 +147,157 @@
 	inode->i_writecount--;
 }
 
+#ifdef CONFIG_TRANS_NAMES
+
+/* see file readdir.c */
+extern char * env_transl(void);
+extern char * transl_names[];
+
+/* If a normal filename is seen, try to determine whether a
+ * "#keyword=context#" file exists and return the new filename.
+ * If the name is to be created (create_mode), check whether a
+ * "#keyword=CREATE" name exists and optionally return the corresponding
+ * context name even if it didn't exist before.
+ */
+static char* _check_name(struct inode * dir, const char * name, int len,
+			 int create_mode, int * reslen,
+			 struct inode ** result, int * error)
+{
+	unsigned long page;
+	char * newname;
+	char * tmp;
+	char * p1;
+        char * p2;
+	char * env = env_transl();
+	char c1;
+	int newlen;
+
+	if(env && !get_user(env))
+	  return NULL;
+#ifdef CONFIG_TRANS_RESTRICT
+	if(!env && dir->i_gid != CONFIG_TRANS_GID)
+	  return NULL;
+#endif
+	if(!(page = __get_free_page(GFP_KERNEL))) {
+	  *error = -ENOMEM;
+	  return NULL;
+	}
+	newname = tmp = (char *) page;
+	memcpy(tmp, name, len);
+	tmp += len;
+	if(env) {
+	  for(p1 = env; (c1=get_user(p1)); ) {
+	    char* oldpos1 = 0;
+	    char* oldpos2 = 0;
+	    for(p2 = tmp; (c1=get_user(p1)) && c1 != ':'; p1++, p2++) {
+	      *p2 = c1;
+	      if(c1 == '=') {
+		oldpos1 = p1+1;
+		oldpos2 = p2+1;
+	      }
+	    }
+	    *p2 = 0;
+	    newlen=(long)p2 - (long)newname;
+	    dir->i_count++;
+	    *error = dir->i_op->lookup(dir,newname,newlen,result);
+	    if(!*error) {
+	      *reslen = newlen;
+	      return newname;
+	    }
+	    if(create_mode && oldpos1) {
+	      char * p1 = "CREATE#";
+	      char * p2 = oldpos2;
+	      while(*p1)
+		*p2++ = *p1++;
+	      *p2 = 0;
+	      newlen=(long)p2 - (long)newname;
+	      dir->i_count++;
+	      *error = dir->i_op->lookup(dir,newname,newlen,result);
+	      if(!*error) {
+		p1 = oldpos1;
+		p2 = oldpos2;
+		while((c1=get_user(p1++)) && c1 != ':')
+		  *p2++ = c1;
+		*p2 = 0;
+		*reslen=(long)p2 - (long)newname;
+		return newname;
+	      }
+	    }
+	    if(c1==':')
+	      p1++;
+	  }
+	} else {
+	  char ** try;
+	  *tmp++ = '#';
+	  for(try = transl_names; *try; try++) {
+	    char* oldpos;
+	    p1 = *try++;
+	    p2 = tmp;
+	    while(*p1)
+	      *p2++ = *p1++;
+	    oldpos = p2;
+	    p1 = *try;
+	    while(*p1)
+	      *p2++ = *p1++;
+	    *p2++ = '#';
+	    *p2 = 0;
+	    newlen=(long)p2 - (long)newname;
+	    dir->i_count++;
+	    *error = dir->i_op->lookup(dir,newname,newlen,result);
+	    if(!*error) {
+	      *reslen = newlen;
+	      return newname;
+	    }
+	    if(create_mode) {
+	      p1 = "CREATE#";
+	      p2 = oldpos;
+	      while(*p1)
+		*p2++ = *p1++;
+	      *p2 = 0;
+	      newlen=(long)p2 - (long)newname;
+	      dir->i_count++;
+	      *error = dir->i_op->lookup(dir,newname,newlen,result);
+	      if(!*error) {
+		p1 = *try;
+		p2 = oldpos;
+		while(*p1)
+		  *p2++ = *p1++;
+		*p2++ = '#';
+		*p2 = 0;
+		*reslen=(long)p2 - (long)newname;
+		return newname;
+	      }
+	    }
+	  }
+	}
+	free_page(page);
+	return NULL;
+}
+
+static char * check_name(struct inode * dir, const char * name, int len,
+			 int create_mode, int * reslen) {
+  struct inode * dummy = 0;
+  int error;
+  char * res = _check_name(dir,name,len,create_mode,reslen,&dummy,&error);
+  if(res)
+    iput(dummy);
+  return res;
+}
+
+/* try with the name suffixed by
+ * "#keyword=context#".
+ */
+static inline int try_transl(struct inode * dir, const char * name, int len,
+			     struct inode ** result) {
+  int error = -ENOENT;
+  int reslen;
+  char* dummy = _check_name(dir,name,len,0,&reslen,result,&error);
+  if(dummy)
+    putname(dummy);
+  return error;
+}
+#endif
+
 /*
  * lookup() looks up one part of a pathname, using the fs-dependent
  * routines (currently minix_lookup) for it. It also checks for
@@ -185,7 +338,22 @@
 		*result = dir;
 		return 0;
 	}
-	return dir->i_op->lookup(dir, name, len, result);
+#ifdef CONFIG_TRANS_NAMES     
+	dir->i_count++;
+#ifdef CONFIG_TRANS_PROOF
+	perm = try_transl(dir,name,len,result);
+	if(perm)
+#endif
+#endif
+	perm = dir->i_op->lookup(dir, name, len, result);
+#ifdef CONFIG_TRANS_NAMES
+#ifndef CONFIG_TRANS_PROOF
+	if(perm)
+	  perm = try_transl(dir,name,len,result);
+#endif
+	iput(dir);
+#endif
+	return perm;
 }
 
 int follow_link(struct inode * dir, struct inode * inode,
@@ -370,12 +538,25 @@
 		else if (IS_RDONLY(dir))
 			error = -EROFS;
 		else {
+#ifdef CONFIG_TRANS_NAMES
+		        char* newname;
+			int newlen;
+			newname = check_name(dir,basename,namelen,1,&newlen);
+			if(newname) {
+			  basename = newname;
+			  namelen = newlen;
+			}
+#endif
 			dir->i_count++;		/* create eats the dir */
 			if (dir->i_sb && dir->i_sb->dq_op)
 				dir->i_sb->dq_op->initialize(dir, -1);
 			error = dir->i_op->create(dir, basename, namelen, mode, res_inode);
 			up(&dir->i_sem);
 			iput(dir);
+#ifdef CONFIG_TRANS_NAMES
+			if(newname)
+			  putname(newname);
+#endif
 			return error;
 		}
 		up(&dir->i_sem);
@@ -460,6 +641,10 @@
 	const char * basename;
 	int namelen, error;
 	struct inode * dir;
+#ifdef CONFIG_TRANS_NAMES
+	char * newname;
+	int newlen;
+#endif
 
 	mode &= ~current->fs->umask;
 	error = dir_namei(filename, &namelen, &basename, NULL, &dir);
@@ -482,11 +667,21 @@
 		return -EPERM;
 	}
 	dir->i_count++;
+#ifdef CONFIG_TRANS_NAMES
+	if((newname=check_name(dir,basename,namelen,1,&newlen))) {
+	  basename = newname;
+	  namelen = newlen;
+	}
+#endif
 	if (dir->i_sb && dir->i_sb->dq_op)
 		dir->i_sb->dq_op->initialize(dir, -1);
 	down(&dir->i_sem);
 	error = dir->i_op->mknod(dir,basename,namelen,mode,dev);
 	up(&dir->i_sem);
+#ifdef CONFIG_TRANS_NAMES
+	if(newname)
+	  putname(newname);
+#endif
 	iput(dir);
 	return error;
 }
@@ -520,6 +715,10 @@
 	const char * basename;
 	int namelen, error;
 	struct inode * dir;
+#ifdef CONFIG_TRANS_NAMES
+	char * newname;
+	int newlen;
+#endif
 
 	error = dir_namei(pathname, &namelen, &basename, NULL, &dir);
 	if (error)
@@ -541,11 +740,21 @@
 		return -EPERM;
 	}
 	dir->i_count++;
+#ifdef CONFIG_TRANS_NAMES
+	if((newname=check_name(dir,basename,namelen,1,&newlen))) {
+	  basename = newname;
+	  namelen = newlen;
+	}
+#endif
 	if (dir->i_sb && dir->i_sb->dq_op)
 		dir->i_sb->dq_op->initialize(dir, -1);
 	down(&dir->i_sem);
 	error = dir->i_op->mkdir(dir, basename, namelen, mode & 0777 & ~current->fs->umask);
 	up(&dir->i_sem);
+#ifdef CONFIG_TRANS_NAMES
+	if(newname)
+	  putname(newname);
+#endif
 	iput(dir);
 	return error;
 }
@@ -568,6 +777,10 @@
 	const char * basename;
 	int namelen, error;
 	struct inode * dir;
+#ifdef CONFIG_TRANS_NAMES
+	char * newname;
+	int newlen;
+#endif
 
 	error = dir_namei(name, &namelen, &basename, NULL, &dir);
 	if (error)
@@ -597,7 +810,23 @@
 	}
 	if (dir->i_sb && dir->i_sb->dq_op)
 		dir->i_sb->dq_op->initialize(dir, -1);
-	return dir->i_op->rmdir(dir,basename,namelen);
+#ifdef CONFIG_TRANS_NAMES
+	dir->i_count++;
+#endif
+	error = dir->i_op->rmdir(dir,basename,namelen);
+#ifdef CONFIG_TRANS_NAMES
+	if(error == -ENOENT) {
+	  if((newname=check_name(dir,basename,namelen,0,&newlen))) {
+	    basename = newname;
+	    namelen = newlen;
+	    dir->i_count++;
+	    error = dir->i_op->rmdir(dir,basename,namelen);
+	    putname(newname);
+	  }
+	}
+	iput(dir);
+#endif
+	return error;
 }
 
 asmlinkage int sys_rmdir(const char * pathname)
@@ -618,6 +847,10 @@
 	const char * basename;
 	int namelen, error;
 	struct inode * dir;
+#ifdef CONFIG_TRANS_NAMES
+	char * newname;
+	int newlen;
+#endif
 
 	error = dir_namei(name, &namelen, &basename, NULL, &dir);
 	if (error)
@@ -647,7 +880,23 @@
 	}
 	if (dir->i_sb && dir->i_sb->dq_op)
 		dir->i_sb->dq_op->initialize(dir, -1);
-	return dir->i_op->unlink(dir,basename,namelen);
+#ifdef CONFIG_TRANS_NAMES
+	dir->i_count++;
+#endif
+	error = dir->i_op->unlink(dir,basename,namelen);
+#ifdef CONFIG_TRANS_NAMES
+	if(error == -ENOENT) {
+	  if((newname=check_name(dir,basename,namelen,0,&newlen))) {
+	    basename = newname;
+	    namelen = newlen;
+	    dir->i_count++;
+	    error = dir->i_op->unlink(dir,basename,namelen);
+	    putname(newname);
+	  }
+	}
+	iput(dir);
+#endif
+	return error;
 }
 
 asmlinkage int sys_unlink(const char * pathname)
@@ -668,6 +917,10 @@
 	struct inode * dir;
 	const char * basename;
 	int namelen, error;
+#ifdef CONFIG_TRANS_NAMES
+	char * new2name;
+	int new2len;
+#endif
 
 	error = dir_namei(newname, &namelen, &basename, NULL, &dir);
 	if (error)
@@ -689,11 +942,21 @@
 		return -EPERM;
 	}
 	dir->i_count++;
+#ifdef CONFIG_TRANS_NAMES
+	if((new2name=check_name(dir,basename,namelen,1,&new2len))) {
+	  basename = new2name;
+	  namelen = new2len;
+	}
+#endif
 	if (dir->i_sb && dir->i_sb->dq_op)
 		dir->i_sb->dq_op->initialize(dir, -1);
 	down(&dir->i_sem);
 	error = dir->i_op->symlink(dir,basename,namelen,oldname);
 	up(&dir->i_sem);
+#ifdef CONFIG_TRANS_NAMES
+	if(new2name)
+	  putname(new2name);
+#endif
 	iput(dir);
 	return error;
 }
@@ -720,6 +983,10 @@
 	struct inode * dir;
 	const char * basename;
 	int namelen, error;
+#ifdef CONFIG_TRANS_NAMES
+	char * new2name;
+	int new2len;
+#endif
 
 	error = dir_namei(newname, &namelen, &basename, NULL, &dir);
 	if (error) {
@@ -760,11 +1027,21 @@
 		return -EPERM;
 	}
 	dir->i_count++;
+#ifdef CONFIG_TRANS_NAMES
+	if((new2name=check_name(dir,basename,namelen,1,&new2len))) {
+	  basename = new2name;
+	  namelen = new2len;
+	}
+#endif
 	if (dir->i_sb && dir->i_sb->dq_op)
 		dir->i_sb->dq_op->initialize(dir, -1);
 	down(&dir->i_sem);
 	error = dir->i_op->link(oldinode, dir, basename, namelen);
 	up(&dir->i_sem);
+#ifdef CONFIG_TRANS_NAMES
+	if(new2name)
+	  putname(new2name);
+#endif
 	iput(dir);
 	return error;
 }
@@ -793,6 +1070,12 @@
 	struct inode * old_dir, * new_dir;
 	const char * old_base, * new_base;
 	int old_len, new_len, error;
+#ifdef CONFIG_TRANS_NAMES
+	char * new2name;
+	char * old2name;
+	int new2len;
+	int old2len;
+#endif
 
 	error = dir_namei(oldname, &old_len, &old_base, NULL, &old_dir);
 	if (error)
@@ -848,12 +1131,47 @@
 		return -EPERM;
 	}
 	new_dir->i_count++;
+#ifdef CONFIG_TRANS_NAMES
+	new2name = 0;
+	old2name = check_name(old_dir,old_base,old_len,0,&old2len);
+	if(old2name) {
+	  char * p1 = old2name;
+	  char * p2 = (char*) __get_free_page(GFP_KERNEL);
+	  if(p2) {
+	    const char * p3 = (const char *)new_base;
+	    old_base = old2name;
+	    old_len = old2len;
+	    new2name = p2;
+	    while(new_len--)
+	      *p2++ = *p3++;
+	    while(*p1 != '#')
+	      p1++;
+	    while(*p1)
+	      *p2++ = *p1++;
+	    *p2 = 0;
+	    new_base = new2name;
+	    new_len = (int)p2 - (int)new_base;
+	  }
+	} else {
+	  new2name = check_name(new_dir,new_base,new_len,1,&new2len);
+	  if(new2name) {
+	    new_base = new2name;
+	    new_len = new2len;
+	  }
+	}
+#endif
 	if (new_dir->i_sb && new_dir->i_sb->dq_op)
 		new_dir->i_sb->dq_op->initialize(new_dir, -1);
 	down(&new_dir->i_sem);
 	error = old_dir->i_op->rename(old_dir, old_base, old_len, 
 		new_dir, new_base, new_len);
 	up(&new_dir->i_sem);
+#ifdef CONFIG_TRANS_NAMES
+	if(old2name)
+	  putname(old2name);
+	if(new2name)
+	  putname(new2name);
+#endif	
 	iput(new_dir);
 	return error;
 }
diff -ur linux.orig/fs/proc/array.c linux/fs/proc/array.c
--- linux.orig/fs/proc/array.c	Tue Apr 30 12:09:45 1996
+++ linux/fs/proc/array.c	Wed Jul  3 10:55:10 1996
@@ -954,6 +954,30 @@
 	return destptr-buf;
 }
 
+#ifdef CONFIG_TRANS_NAMES
+extern char * transl_names[];
+
+static int get_nametrans (char * buf) {
+  char * res = buf;
+  char ** entry;
+  char * ptr;
+  for (entry = transl_names; *entry; entry++) {
+    *res++ = '#';
+    for(ptr = *entry; *ptr; ptr++)
+      *res++ = *ptr;
+    entry++;
+    for(ptr = *entry; *ptr; ptr++)
+      *res++ = *ptr;
+    *res++ = '#';
+    *res++ = ':';
+  }
+  res--;
+  *res++ = '\n';
+  *res = '\0';
+  return (int)res - (int)buf;
+}
+#endif
+
 #ifdef CONFIG_MODULES
 extern int get_module_list(char *);
 extern int get_ksyms_list(char *, char **, off_t, int);
@@ -1039,11 +1063,15 @@
                 case PROC_MTAB:
                        return get_filesystem_info( page );
 #ifdef CONFIG_RTC
-		case PROC_RTC:
+	        case PROC_RTC:
 			return get_rtc_status(page);
 #endif
-		case PROC_LOCKS:
+	        case PROC_LOCKS:
 			return get_locks_status(page);
+#ifdef CONFIG_TRANS_NAMES
+	        case PROC_NAMETRANS:
+		        return get_nametrans(page);
+#endif
 	}
 	return -EBADF;
 }
diff -ur linux.orig/fs/proc/root.c linux/fs/proc/root.c
--- linux.orig/fs/proc/root.c	Tue Apr 30 12:09:45 1996
+++ linux/fs/proc/root.c	Wed Jul  3 10:22:44 1996
@@ -372,6 +372,10 @@
 			S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0,
 		});
 	}
+#ifdef CONFIG_TRANS_NAMES
+	proc_register( &proc_root, &(struct proc_dir_entry)
+	   { PROC_NAMETRANS, 9, "nametrans", S_IFREG | S_IRUGO, 1, 0, 0, } );
+#endif
 }
 
 
diff -ur linux.orig/fs/readdir.c linux/fs/readdir.c
--- linux.orig/fs/readdir.c	Mon Sep 11 19:15:45 1995
+++ linux/fs/readdir.c	Wed Jul  3 12:17:21 1996
@@ -10,9 +10,120 @@
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
+#include <linux/config.h>
 
 #include <asm/segment.h>
 
+#ifdef CONFIG_TRANS_NAMES
+#include <linux/utsname.h>
+
+/* patch by Thomas Schoebel-Theuer:
+ * translates names of the form "filename#host=myhost#" to "filename"
+ * as if both names were hardlinked to the same file.
+ * benefit: diskless clients can mount the / filesystem of the
+ * server if /etc/fstab (and other config files) are organized using
+ * context-suffixes.
+ */
+
+char *transl_names[] = {
+#ifdef CONFIG_TR_NODENAME
+  "host=",        system_utsname.nodename,
+#endif
+#ifdef CONFIG_TR_KERNTYPE
+  "ktype=",  CONFIG_KERNTYPE,
+#endif
+#ifdef CONFIG_TR_MACHINE
+  "machine=",     system_utsname.machine,
+#endif
+#ifdef CONFIG_TR_SYSNAME
+  "system=",      system_utsname.sysname,
+#endif
+  0, 0
+};
+
+/* if the _first_ environment variable is "NAMETRANS", return
+ * a pointer to the list of appendices.
+ * You can set the first environment variable using
+ * 'env - NAMETRANS=... "`env`" command ...'
+ */
+char* env_transl(void) {
+  char* env;
+  int i;
+  if(current && current->mm && (env = (char*)current->mm->env_start)
+     && get_ds() != get_fs()
+     && current->mm->env_end>=current->mm->env_start+10
+     && !verify_area(VERIFY_READ,env,10)) {
+    for(i=0; i<10; i++)
+      if(get_user(env++) != "NAMETRANS="[i])
+	return 0;
+    return env;
+  }
+  return 0;
+}
+
+/* if name has the correct suffix "#keyword=correct_context#", cut it
+ * off and return success.
+ */
+static int testname(struct inode * parent, char* name) {
+  char* ptr = name;
+  char** try;
+  char* p1;
+  char* p2;
+  char* cut;
+  char* env;
+  char c1, c2;
+
+  while((c1=get_user(ptr++)) && c1 != '#') ;
+  if(!c1)
+    return 0;
+  cut = ptr;
+  while ((c1=get_user(ptr++)) && c1 != '#');
+  if(!c1 || get_user(ptr))
+    return 0;
+  env = env_transl();
+  if(env) {
+    for(p2 = env; (c2=get_user(p2)); p2++) {
+      p1 = cut-1;
+      c1 = ':';
+      while (c2 && c2 != ':') {
+	if((c1=get_user(p1++)) != (c2=get_user(p2++)))
+	  break;
+      }
+      if(!c1 && (!c2 || c2==':')) {
+	put_user(0, --cut);
+	return 1;
+      }
+      while ((c2=get_user(p2)) && c2 != ':') p2++;
+    }
+  } else {
+#ifdef CONFIG_TRANS_RESTRICT
+    if(parent->i_gid != CONFIG_TRANS_GID)
+      return 0;
+#endif
+    for(try = transl_names; *try; ) {
+      p1 = cut;
+      for(p2 = *try++; *p2; p2++) {
+	if(get_user(p1++) != *p2)
+	  break;
+      }
+      if(!*p2) {
+	for(p2 = *try++; *p2; p2++) {
+	  if(get_user(p1++) != *p2)
+	    break;
+	}
+	if(!*p2 && ++p1 == ptr) {
+	  put_user(0, --cut);
+	  return 1;
+	}
+	return 0;
+      }
+      try++;
+    }
+  }
+  return 0;
+}
+#endif
+
 /*
  * Traditional linux readdir() handling..
  *
@@ -68,6 +179,10 @@
 		return error;
 	buf.count = 0;
 	buf.dirent = dirent;
+#ifdef CONFIG_TRANS_NAMES
+	if(testname(file->f_inode, buf.dirent->d_name))
+	  return 1;
+#endif
 	error = file->f_op->readdir(file->f_inode, file, &buf, fillonedir);
 	if (error < 0)
 		return error;
@@ -134,6 +249,10 @@
 	buf.previous = NULL;
 	buf.count = count;
 	buf.error = 0;
+#ifdef CONFIG_TRANS_NAMES
+	if(testname(file->f_inode, buf.current_dir->d_name))
+	  return 0;
+#endif
 	error = file->f_op->readdir(file->f_inode, file, &buf, filldir);
 	if (error < 0)
 		return error;
diff -ur linux.orig/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- linux.orig/include/linux/proc_fs.h	Sun Jun  9 11:23:42 1996
+++ linux/include/linux/proc_fs.h	Wed Jul  3 12:09:15 1996
@@ -38,6 +38,7 @@
 	PROC_SMP_PROF,
 #endif
 	PROC_PROFILE, /* whether enabled or not */
+	PROC_NAMETRANS, /* whether enabled or not */
 	PROC_CMDLINE,
 	PROC_SYS,
 	PROC_MTAB,
diff -ur linux.orig/scripts/Configure linux/scripts/Configure
--- linux.orig/scripts/Configure	Fri May 31 12:46:27 1996
+++ linux/scripts/Configure	Mon Jul  1 18:47:13 1996
@@ -329,6 +329,28 @@
 }
 
 #
+# define_string sets the value of a string argument
+#
+#	define_string define value
+#
+function define_string () {
+	echo "$1="'"'$2'"' >>$CONFIG
+	echo "#define $1 "'"'$2'"' >>$CONFIG_H
+	eval "$1=$2"
+}
+
+#
+# string processes a string argument
+#
+#	string question define default
+#
+function string () {
+	old=$(eval echo "\${$2}")
+	def=${old:-$3}
+	readln "$1 ($2) [$def] " "$def" "$old"
+	define_string "$2" "$ans"
+}
+#
 # choice processes a choice list (1-out-of-n)
 #
 #	choice question choice-list default
diff -ur linux.orig/scripts/header.tk linux/scripts/header.tk
--- linux.orig/scripts/header.tk	Sat Apr 13 13:20:03 1996
+++ linux/scripts/header.tk	Mon Jul  1 18:44:27 1996
@@ -209,6 +209,10 @@
 			set cmd "global $var; set $var $value"
 			eval $cmd
 		}
+		if [regexp {([0-9A-Za-z_]+)="([0-9A-Za-z]+)"} $line foo var value] {
+			set cmd "global $var; set $var $value"
+			eval $cmd
+		}
 	}
 	close $file1
 	update_choices
@@ -260,6 +264,16 @@
 	}
 }
 
+proc write_string { file1 file2 varname variable dep } {
+	if { $dep == 0 } \
+		then { puts $file1 "# $varname is not set"; \
+		       puts $file2 "#undef $varname"} \
+	else {
+		puts $file1 "$varname=\"$variable\""; \
+		puts $file2 "#define $varname \"$variable\""; \
+	}
+}
+
 proc option_name {w mnum line text helpidx} {
 	button $w.x$line.l -text "$text" -relief groove -anchor w
 	$w.x$line.l configure -activefore [cget $w.x$line.l -fg] \
@@ -310,6 +324,15 @@
 
 proc hex { w mnum line text variable } {
 	int $w $mnum $line $text $variable
+}
+
+proc istring { w mnum line text variable } {
+	frame $w.x$line
+	entry $w.x$line.x -width 18 -relief sunken -borderwidth 2 \
+		-textvariable $variable
+	option_name $w $mnum $line $text $variable
+	pack $w.x$line.x -anchor w -side right -fill y
+	pack $w.x$line -anchor w -fill both -expand on
 }
 
 proc minimenu { w mnum line text variable helpidx } {
diff -ur linux.orig/scripts/tkcond.c linux/scripts/tkcond.c
--- linux.orig/scripts/tkcond.c	Mon May  6 11:26:18 1996
+++ linux/scripts/tkcond.c	Mon Jul  1 18:07:12 1996
@@ -195,6 +195,7 @@
 		      if( cfg->tok != tok_bool
 		         && cfg->tok != tok_int
 		         && cfg->tok != tok_hex
+		         && cfg->tok != tok_string
 			 && cfg->tok != tok_tristate 
 			 && cfg->tok != tok_choice
 			 && cfg->tok != tok_dep_tristate)
@@ -358,6 +359,7 @@
 	case tok_tristate:
 	case tok_int:
 	case tok_hex:
+	case tok_string:
 	case tok_choice:
 	case tok_make:
 	  /*
@@ -402,6 +404,7 @@
 	case tok_dep_tristate:
 	case tok_int:
 	case tok_hex:
+	case tok_string:
 	  for(cfg1=cfg;cfg1 != NULL; cfg1 = cfg1->next)
 	    {
 	      switch(cfg1->tok)
@@ -412,6 +415,7 @@
 		case tok_dep_tristate:
 		case tok_int:
 		case tok_hex:
+		case tok_string:
 		  if( strcmp(cfg->optionname, cfg1->optionname) == 0)
 		    {
 		      cfg->flags |= CFG_DUP;
diff -ur linux.orig/scripts/tkgen.c linux/scripts/tkgen.c
--- linux.orig/scripts/tkgen.c	Thu Jun  6 12:30:41 1996
+++ linux/scripts/tkgen.c	Mon Jul  1 18:28:10 1996
@@ -289,6 +289,7 @@
       break;
     case tok_int:
     case tok_hex:
+    case tok_string:
       printf("} then { ");
       printf(".menu%d.config.f.x%d.x configure -state normal -fore [ cget .ref -foreground ]; ", menu_num, line_num);
       printf(".menu%d.config.f.x%d.l configure -state normal; ", menu_num, line_num);
@@ -487,6 +488,10 @@
       printf("} then { write_hex $cfg $autocfg %s $%s $notmod }\n",
              item->optionname, item->optionname);
       break;
+    case tok_string:
+      printf("} then { write_string $cfg $autocfg %s $%s $notmod }\n",
+             item->optionname, item->optionname);
+      break;
     case tok_make:
       printf("} then { do_make {%s} }\n",item->value);
       break;
@@ -646,6 +651,7 @@
       case tok_dep_tristate:
       case tok_int:
       case tok_hex:
+      case tok_string:
       case tok_choose:
 	tot++;
 	break;
@@ -702,6 +708,7 @@
 	case tok_dep_tristate:
 	case tok_int:
 	case tok_hex:
+	case tok_string:
 	case tok_choose:
 	  /*
 	   * If we have overfilled the menu, then go to the next one.
@@ -862,6 +869,19 @@
 		 cfg->label,
 		 cfg->optionname);
 	  break;
+	case tok_string:
+	  if( cfg->menu_number != menu_num )
+	    {
+	      end_proc(menu_num);
+	      start_proc(menulabel, cfg->menu_number, FALSE);
+	      menu_num = cfg->menu_number;
+	    }
+	  printf("\tistring $w.config.f %d %d \"%s\" %s\n",
+		 cfg->menu_number,
+		 cfg->menu_line,
+		 cfg->label,
+		 cfg->optionname);
+	  break;
 	default:
 	  break;
 	}
@@ -951,6 +971,7 @@
 	  break;
 	case tok_int:
 	case tok_hex:
+	case tok_string:
 	  printf("set %s %s\n", cfg->optionname, cfg->value);
 	  break;
 	case tok_choose:
@@ -984,6 +1005,7 @@
 	{
 	case tok_int:
 	case tok_hex:
+	case tok_string:
 	case tok_bool:
 	case tok_tristate:
 	case tok_dep_tristate:
@@ -1046,6 +1068,12 @@
 	      else if (cfg->tok == tok_hex )
 	        {
 		  printf("\twrite_hex $cfg $autocfg %s $%s $notmod\n",
+			 cfg->optionname,
+			 cfg->optionname);
+	        }
+	      else if (cfg->tok == tok_string )
+	        {
+		  printf("\twrite_string $cfg $autocfg %s $%s $notmod\n",
 			 cfg->optionname,
 			 cfg->optionname);
 	        }
diff -ur linux.orig/scripts/tkparse.c linux/scripts/tkparse.c
--- linux.orig/scripts/tkparse.c	Mon May  6 11:26:18 1996
+++ linux/scripts/tkparse.c	Mon Jul  1 18:21:15 1996
@@ -370,6 +370,11 @@
       tok = tok_hex;
       pnt += 3;
     }
+  else if (strncmp(pnt, "string", 6) == 0) 
+    {
+      tok = tok_string;
+      pnt += 6;
+    }
   else if (strncmp(pnt, "if", 2) == 0) 
     {
       tok = tok_if;
@@ -457,6 +462,7 @@
       break;
     case tok_int:
     case tok_hex:
+    case tok_string:
       pnt = get_qstring(pnt, &kcfg->label);
       pnt = get_string(pnt, &kcfg->optionname);
       pnt = get_string(pnt, &kcfg->value);
@@ -702,6 +708,9 @@
 	case tok_hex:
 	  printf("hex ");
 	  break;
+	case tok_string:
+	  printf("istring ");
+	  break;
 	case tok_comment:
 	  printf("comment ");
 	  break;
@@ -732,6 +741,7 @@
 	case tok_dep_tristate:
 	case tok_int:
 	case tok_hex:
+	case tok_string:
 	  printf("%s %s\n", cfg->label, cfg->optionname);
 	  break;
 	case tok_if:
diff -ur linux.orig/scripts/tkparse.h linux/scripts/tkparse.h
--- linux.orig/scripts/tkparse.h	Sun Mar 17 08:58:21 1996
+++ linux/scripts/tkparse.h	Mon Jul  1 17:25:29 1996
@@ -12,6 +12,7 @@
   tok_fi, 
   tok_int,
   tok_hex,
+  tok_string,
   tok_make,
   tok_define,
   tok_choose,
