From: David Howells <dhowells@redhat.com>

For those who think a filesystem is the right interface for everything,
despite it being ~18KB to the ~4KB required for a syscall interface, and
_much_ less efficient generally in to the bargain.

Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/Documentation/keys.txt |  168 +++++++
 25-akpm/fs/Kconfig             |    9 
 25-akpm/fs/Makefile            |    1 
 25-akpm/fs/keyfs/Makefile      |   14 
 25-akpm/fs/keyfs/keydir.c      |  547 +++++++++++++++++++++++++
 25-akpm/fs/keyfs/keyfile.c     |  874 +++++++++++++++++++++++++++++++++++++++++
 25-akpm/fs/keyfs/keyfs.h       |   86 ++++
 25-akpm/fs/keyfs/ringdir.c     |  467 +++++++++++++++++++++
 25-akpm/fs/keyfs/root.c        |  362 ++++++++++++++++
 25-akpm/fs/keyfs/rootfile.c    |  455 +++++++++++++++++++++
 25-akpm/fs/keyfs/rootlink.c    |  180 ++++++++
 25-akpm/fs/keyfs/super.c       |  117 +++++
 12 files changed, 3280 insertions(+)

diff -puN Documentation/keys.txt~keys-keyring-management-keyfs-patch Documentation/keys.txt
--- 25/Documentation/keys.txt~keys-keyring-management-keyfs-patch	Wed Aug 18 17:27:19 2004
+++ 25-akpm/Documentation/keys.txt	Wed Aug 18 17:27:20 2004
@@ -794,3 +794,171 @@ by executing:
 
 In this case, the program isn't required to actually attach the key to a ring;
 the rings are provided for reference.
+
+
+=====================
+KEY ACCESS FILESYSTEM
+=====================
+
+The keyfs filesystem can be mounted and used to access the key database. It
+represents each key as a directory in the root of the filesystem. Only keys
+that provide at least one permission to a process attempting to read the
+root directory will appear in the list returned. Similarly, any operation (such
+as chdir) that tries to access a key directory will return error ENOENT unless
+that key supports at least one permission for the offending process.
+
+All reads and writes to control files must be achieved with a single system
+call. The files do not try to stitch together several buffers.
+
+
+Root Directory Features
+-----------------------
+
+The keyfs layout is as follows:
+
+/keyfs/
+  <keyID>/	}
+  <keyID>/	}
+  <keyID>/	} Key and keyring directories
+  <keyID>/	}
+  ...		}
+  thread		}
+  process		} Symbolic links to key directories corresponding to a
+  session		} process's subscribed keyrings and those of its owning
+  user-session		} user
+  user			}
+  join-session	- Session joining file
+  search	- Master search
+  request-key	- Master request key
+
+Writing a keyring name to /keyfs/join-session will cause a process to attach
+that keyring as its session keyring, creating the keyring if necessary. The
+keyring name can be blank, in which case an anonymous keyring is used. Any
+terminal newline character is stripped.
+
+	/bin/echo dhowells-kde-ring >/keyfs/join-session
+
+Writing a string consisting of: a type name, a newline character, a
+description, a newline character, an optional keyring ID and another newline
+character will cause a search to be made of all the process's keyrings for a
+matching key. If found, an attempt will be made to link the key to the
+keyring specified by the optional ID.
+
+	{ /bin/echo -ne user\\ncopper\\n\\n >&5; cat <&5; } 5<>/keyfs/search
+
+Similarly, writing a string consisting of: a type name, a newline character, a
+description and a newline character will cause a search to be made of the
+processes keyrings, with a fallback to calling /sbin/request-key for the key.
+
+	{ /bin/echo -ne user\\ncopper\\n >&5; cat <&5; } 5<>/keyfs/request-key
+
+
+Keyring Directory Features
+--------------------------
+
+The <keyID>/ directory for a keyring looks like:
+
+/keyfs/
+  <keyID>/
+    add		- Key adding file
+    description	- Key description file
+    expiry	- Key expiry time file
+    flags	- Key flags file
+    perm	- Key permissions file
+    revoke	- Key revoke file
+    search	- Keyring search file
+    type	- Key type name file
+    usage	- Key usage count file
+    keyring/
+      <keyID>	}
+      <keyID>	}
+      <keyID>	} Symbolic links to subscribed keys
+      <keyID>	}
+      <keyID>	}
+
+The description, expiry, flags, type and usage files are purely for reading
+information about the keyring.
+
+The keyring can be revoked by writing to the revoke file:
+
+	echo >/keyfs/1/revoke
+
+It can have its permissions altered by writing a number corresponding to the
+permissions mask to the perm file (a trailing NL will be stripped):
+
+	echo 0x1f0909 >/keyfs/1/perm
+
+And the perm file can be read back to see what the current state is. The key's
+UID[*] and GID can be changed by using chmod or chgrp on the key directory:
+
+	chmod dhowells.cambridge /keyfs/1
+	chgrp cambridge /keyfs/1
+
+[*] UID changing is not currently supported.
+
+Writing a string consisting of: a type name, a newline character, a
+description, a newline and optionally a payload blob to this file will cause a
+suitable key to be constructed and attached to the keyring if a matching key
+doesn't already exist there; or if one does exist, it is updated if possible
+instead.
+
+	{ /bin/echo -ne user\\nelement:Cu\\ncopper >&5; cat <&5; } 5<>/keyfs/1/add
+
+Writing a string consisting of: a type name, a newline character, a
+description, a newline character, an optional keyring ID and another newline
+character will cause a search to be made of this keyring and its children for a
+matching key. If found, an attempt will be made to link the key to the keyring
+specified by the optional ID.
+
+	{ /bin/echo -ne user\\ncopper\\n\\n >&5; cat <&5; } 5<>/keyfs/1/search
+
+The keyring/ subdirectory contains symbolic links to the key directories
+corresponding to those keys to which the keyring holds links. These symbolic
+links can be deleted with unlink:
+
+	rm /keyfs/1/keyring/2
+
+And made with symlink (note the path entered must contain "../../" and the
+keyID in the symlink must match that of the symlink's filename):
+
+	ln -s ../../7 /keyfs/1/keyring/7
+
+
+Key Directory Features
+----------------------
+
+The <keyID>/ directory for an ordinary key looks like:
+
+/keyfs/
+  <keyID>/
+    description	- Key description file
+    expiry	- Key expiry time file
+    flags	- Key flags file
+    perm	- Key permissions file
+    revoke	- Key revoke file
+    type	- Key type name file
+    usage	- Key usage count file
+    payload	- Key payload file
+    instantiate	- Key instantiation file (temporary)
+    negate	- Key negative instantiation file (temporary)
+
+The description, expiry, flags, perm, revoke, type and usage files all work as
+for keyring directories (described above).
+
+The payload file can be used to read out the payload of a key, as presented by
+the key type, and can be written to to update the payload if supported by the
+type.
+
+The instantiate file only exists for an uninstantiated key. Writing to it a
+string consisting of an optional keyring ID, a newline character and an
+optional payload blob will result in the key being instantiated and linked to
+the suggested keyring.
+
+	{ /bin/echo -ne 2\\ncopper >&5; cat <&5; } 5<>/keyfs/1/instantiate
+
+Similarly, the negate file only exists for an uninstantiated key.  Writing to
+it a string consisting of an optional keyring ID, a newline character, an
+optional timeout value and another newline character will result in the key
+being negatively instantiated and linked to the suggested keyring.
+
+	{ /bin/echo -ne 2\\n\\n >&5; cat <&5; } 5<>/keyfs/1/negate
diff -puN fs/Kconfig~keys-keyring-management-keyfs-patch fs/Kconfig
--- 25/fs/Kconfig~keys-keyring-management-keyfs-patch	Wed Aug 18 17:27:19 2004
+++ 25-akpm/fs/Kconfig	Wed Aug 18 17:27:20 2004
@@ -937,6 +937,15 @@ config RAMFS
 	  To compile this as a module, choose M here: the module will be called
 	  ramfs.
 
+config KEYFS
+	bool "Key managment database interface filesystem"
+	depends on KEYS
+	help
+	  Keyfs is a filesystem that allows access to the keys database that
+	  can be maintained by the kernel. It allows all the operations
+	  userspace might require to be performed using read, write, symlink
+	  and unlink.
+
 endmenu
 
 menu "Miscellaneous filesystems"
diff -puN /dev/null fs/keyfs/keydir.c
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/fs/keyfs/keydir.c	Wed Aug 18 17:27:20 2004
@@ -0,0 +1,547 @@
+/* keydir.c: key representation directory operations
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/key.h>
+#include <asm/uaccess.h>
+#include "keyfs.h"
+
+/*
+ * key directory operations
+ */
+static int keyfs_dir_readdir(struct file *, void *, filldir_t);
+
+static struct file_operations keyfs_dir_file_operations = {
+	.readdir	= keyfs_dir_readdir,
+};
+
+static struct dentry *keyfs_dir_lookup(struct inode *, struct dentry *,
+				       struct nameidata *);
+
+static int keyfs_dir_permission(struct inode *, int, struct nameidata *);
+
+static int keyfs_dir_getattr(struct vfsmount *, struct dentry *,
+			     struct kstat *);
+
+static int keyfs_dir_setattr(struct dentry *, struct iattr *);
+
+static struct inode_operations keyfs_dir_inode_operations = {
+	.lookup		= keyfs_dir_lookup,
+	.permission	= keyfs_dir_permission,
+	.getattr	= keyfs_dir_getattr,
+	.setattr	= keyfs_dir_setattr,
+};
+
+static int keyfs_dir_d_revalidate(struct dentry *, struct nameidata *);
+
+struct dentry_operations keyfs_dir_dentry_operations = {
+	.d_revalidate	= keyfs_dir_d_revalidate,
+	.d_delete	= keyfs_d_delete,
+};
+
+/*****************************************************************************/
+/*
+ * update the attributes on a key management directory
+ */
+static void keyfs_dir_update_inode(struct inode *inode)
+{
+	struct key *key = inode->u.generic_ip;
+
+	/* update the inode from the key */
+	down_read(&key->sem);
+
+	inode->i_uid	= key->uid;
+	inode->i_gid	= key->gid;
+	inode->i_mode	&= S_IFMT;
+
+	/* we have to provide access to a keys control files if any of
+	 * them can be accessed for any reason */
+	inode->i_mode |= S_IRUSR | S_IXUSR;
+
+	if (key->perm & KEY_GRP_ALL)
+		inode->i_mode |= S_IRGRP | S_IXGRP;
+
+	if (key->perm & KEY_OTH_ALL)
+		inode->i_mode |= S_IROTH | S_IXOTH;
+
+	up_read(&key->sem);
+
+} /* end keyfs_dir_update_inode() */
+
+/*****************************************************************************/
+/*
+ * get the directory for a key
+ */
+struct inode *keyfs_get_keydir(struct super_block *super, key_serial_t id)
+{
+	struct inode *inode;
+	struct key *key;
+
+	/* the key must exist, and we must have at least one permission on it
+	 * to be able to access it */
+	key = key_lookup(id);
+	if (IS_ERR(key)) {
+		inode = ERR_PTR(PTR_ERR(key));
+		goto error;
+	}
+
+	if (!key_any_permission(key, KEY_ALL)) {
+		/* we pretend a key doesn't exist if a process is not allowed
+		 * to access it */
+		key_put(key);
+		inode = ERR_PTR(-ENOENT);
+		goto error;
+	}
+
+	/* get the key directory inode */
+	inode = iget_locked(super, (id << 16) + KEYFS_INO_K_DIR);
+	if (inode && inode->i_state & I_NEW) {
+		/* initialise it */
+		inode->i_mtime	= inode->i_atime = inode->i_ctime =
+			CURRENT_TIME;
+		inode->i_blocks	= 0;
+		inode->i_blksize = 1024;
+		inode->i_uid	= key->uid;
+		inode->i_gid	= key->gid;
+		inode->i_mode	= S_IFDIR;
+		inode->i_op	= &keyfs_dir_inode_operations;
+		inode->i_fop	= &keyfs_dir_file_operations;
+		inode->i_nlink	= 2;
+		inode->u.generic_ip = key;
+
+		/* set the ownership and permissions from the key */
+		keyfs_dir_update_inode(inode);
+
+		/* success */
+		unlock_new_inode(inode);
+	}
+	else if (inode) {
+		/* exists */
+		key_put(key);
+	}
+	else {
+		/* error */
+		inode = ERR_PTR(-ENOMEM);
+		key_put(key);
+	}
+
+ error:
+	return inode;
+
+} /* end keyfs_get_keydir() */
+
+/*****************************************************************************/
+/*
+ * update the attributes on a key management directory during pathwalk
+ */
+static int keyfs_dir_d_revalidate(struct dentry *dentry, struct nameidata *nd)
+{
+	keyfs_dir_update_inode(dentry->d_inode);
+	return 1;
+
+} /* end keyfs_dir_d_revalidate() */
+
+/*****************************************************************************/
+/*
+ * read the attributes of an inode, updating from the key
+ */
+static int keyfs_dir_getattr(struct vfsmount *mnt, struct dentry *dentry,
+			      struct kstat *stat)
+{
+	/* update the inode */
+	keyfs_dir_update_inode(dentry->d_inode);
+
+	/* transfer attributes from the inode structure to the stat
+	 * structure */
+	generic_fillattr(dentry->d_inode, stat);
+
+	return 0;
+
+} /* end keyfs_dir_getattr() */
+
+/*****************************************************************************/
+/*
+ * enumerate the keys in a key directory
+ */
+static int keyfs_dir_readdir(struct file *file, void *cookie, filldir_t filldir)
+{
+	struct key *key;
+	ino_t ino;
+	int ret;
+
+	key = file->f_dentry->d_inode->u.generic_ip;
+	ino = file->f_dentry->d_inode->i_ino & ~0xffffULL;
+
+	/* read the usual "." and ".." first followed by the key control
+	 * files */
+	switch (file->f_pos) {
+	case 0:
+		ret = filldir(cookie, ".", 1, file->f_pos,
+			      file->f_dentry->d_inode->i_ino, DT_DIR);
+		if (ret < 0)
+			goto done;
+		file->f_pos++;
+
+	case 1:
+		ret = filldir(cookie, "..", 2, file->f_pos,
+			      parent_ino(file->f_dentry), DT_DIR);
+		if (ret < 0)
+			goto done;
+		file->f_pos++;
+
+	case 2:
+		ret = filldir(cookie, "type", 4, file->f_pos,
+			      ino | KEYFS_INO_K_TYPE, DT_REG);
+		if (ret < 0)
+			goto done;
+		file->f_pos++;
+
+	case 3:
+		ret = filldir(cookie, "description", 11, file->f_pos,
+			      ino | KEYFS_INO_K_DESC, DT_REG);
+		if (ret < 0)
+			goto done;
+		file->f_pos++;
+
+	case 4:
+		ret = filldir(cookie, "expiry", 6, file->f_pos,
+			      ino | KEYFS_INO_K_EXPIRY, DT_REG);
+		if (ret < 0)
+			goto done;
+		file->f_pos++;
+
+	case 5:
+		ret = filldir(cookie, "perm", 4, file->f_pos,
+			      ino | KEYFS_INO_K_PERM, DT_REG);
+		if (ret < 0)
+			goto done;
+		file->f_pos++;
+
+	case 6:
+		ret = filldir(cookie, "revoke", 6, file->f_pos,
+			      ino | KEYFS_INO_K_REVOKE, DT_REG);
+		if (ret < 0)
+			goto done;
+		file->f_pos++;
+
+	case 7:
+		if (!(key->flags & KEY_FLAG_INSTANTIATED)) {
+			ret = filldir(cookie, "instantiate", 11, file->f_pos,
+				      ino | KEYFS_INO_K_INSTANTIATE, DT_REG);
+			if (ret < 0)
+				goto done;
+		}
+		file->f_pos++;
+
+	case 8:
+		if (!(key->flags & KEY_FLAG_INSTANTIATED)) {
+			ret = filldir(cookie, "negate", 6, file->f_pos,
+				      ino | KEYFS_INO_K_NEGATE, DT_REG);
+			if (ret < 0)
+				goto done;
+		}
+		file->f_pos++;
+
+	case 9:
+		if (key->flags & KEY_FLAG_INSTANTIATED) {
+			if (key->type == &key_type_keyring) {
+				ret = filldir(cookie, "search", 6, file->f_pos,
+					      ino | KEYFS_INO_K_NEGATE, DT_REG);
+				if (ret < 0)
+					goto done;
+			}
+		}
+		file->f_pos++;
+
+	case 10:
+		ret = filldir(cookie, "usage", 5, file->f_pos,
+			      ino | KEYFS_INO_K_USAGE, DT_REG);
+		if (ret < 0)
+			goto done;
+		file->f_pos++;
+
+	case 11:
+		ret = filldir(cookie, "flags", 5, file->f_pos,
+			      ino | KEYFS_INO_K_USAGE, DT_REG);
+		if (ret < 0)
+			goto done;
+		file->f_pos++;
+
+	case 12:
+		if (key->flags & KEY_FLAG_INSTANTIATED) {
+			ret = filldir(cookie, "add", 3, file->f_pos,
+				      ino | KEYFS_INO_K_ADD, DT_REG);
+			if (ret < 0)
+				goto done;
+		}
+		file->f_pos++;
+
+	case 13:
+		if (key->flags & KEY_FLAG_INSTANTIATED) {
+			if (key->type == &key_type_keyring)
+				ret = filldir(cookie, "keyring", 7,
+					      file->f_pos,
+					      ino | KEYFS_INO_K_PAYLOAD,
+					      DT_DIR);
+			else
+				ret = filldir(cookie, "payload", 7,
+					      file->f_pos,
+					      ino | KEYFS_INO_K_PAYLOAD,
+					      DT_REG);
+			if (ret < 0)
+				goto done;
+		}
+		file->f_pos++;
+
+	default:
+		break;
+	}
+
+ done:
+	return 0;
+
+} /* end keyfs_dir_readdir() */
+
+/*****************************************************************************/
+/*
+ * look up an inode in a key directory
+ */
+static struct dentry *keyfs_dir_lookup(struct inode *dir,
+				       struct dentry *dentry,
+				       struct nameidata *nd)
+{
+	struct dentry *ret;
+	struct inode *target;
+	struct key *key;
+	const char *name;
+
+	key = dir->u.generic_ip;
+
+	/* determine which virtual file they want */
+	name = dentry->d_name.name;
+
+	switch (dentry->d_name.len) {
+	case 1:
+		if (memcmp(name, ".", 1) == 0) {
+			target = igrab(dir);
+			goto instantiate;
+		}
+		break;
+
+	case 2:
+		if (memcmp(name, "..", 2)==0) {
+			target = igrab(dir);
+			goto instantiate;
+		}
+		break;
+
+	case 3:
+		if (memcmp(name, "add", 3) == 0) {
+			target = keyfs_get_keyfile(dir, KEYFS_INO_K_ADD);
+			goto instantiate;
+		}
+		break;
+
+	case 4:
+		if (memcmp(name, "type", 4) == 0) {
+			target = keyfs_get_keyfile(dir, KEYFS_INO_K_TYPE);
+			goto instantiate;
+		}
+		if (memcmp(name, "perm", 4) == 0) {
+			target = keyfs_get_keyfile(dir, KEYFS_INO_K_PERM);
+			goto instantiate;
+		}
+		break;
+
+	case 5:
+		if (memcmp(name, "usage", 5) == 0) {
+			target = keyfs_get_keyfile(dir, KEYFS_INO_K_USAGE);
+			goto instantiate;
+		}
+		if (memcmp(name, "flags", 5) == 0) {
+			target = keyfs_get_keyfile(dir, KEYFS_INO_K_FLAGS);
+			goto instantiate;
+		}
+		break;
+
+	case 6:
+		if (memcmp(name, "expiry", 6) == 0) {
+			target = keyfs_get_keyfile(dir, KEYFS_INO_K_EXPIRY);
+			goto instantiate;
+		}
+		if (memcmp(name, "revoke", 6) == 0) {
+			target = keyfs_get_keyfile(dir, KEYFS_INO_K_REVOKE);
+			goto instantiate;
+		}
+		if (memcmp(name, "negate", 6) == 0) {
+			target = keyfs_get_keyfile(dir, KEYFS_INO_K_NEGATE);
+			goto instantiate;
+		}
+		if (key->type != &key_type_keyring)
+			break;
+		if (memcmp(name, "search", 6) == 0) {
+			target = keyfs_get_keyfile(dir, KEYFS_INO_K_SEARCH);
+			goto instantiate;
+		}
+		break;
+
+	case 7:
+		if (key->type == &key_type_keyring) {
+			if (memcmp(name, "keyring", 7) == 0) {
+				target = keyfs_get_keyfile(
+					dir,
+					KEYFS_INO_K_PAYLOAD);
+				goto instantiate;
+			}
+		}
+		else {
+			if (memcmp(name, "payload", 7) == 0) {
+				target = keyfs_get_keyfile(
+					dir,
+					KEYFS_INO_K_PAYLOAD);
+				goto instantiate;
+			}
+		}
+		break;
+
+	case 11:
+		if (memcmp(name, "description", 11) == 0) {
+			target = keyfs_get_keyfile(dir, KEYFS_INO_K_DESC);
+			goto instantiate;
+		}
+		if (memcmp(name, "instantiate", 11) == 0) {
+			target = keyfs_get_keyfile(dir,
+						   KEYFS_INO_K_INSTANTIATE);
+			goto instantiate;
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	/* make a negative dentry */
+	d_add(dentry, NULL);
+	ret = NULL;
+	goto out;
+
+	/* instantiate the dentry */
+ instantiate:
+	if (IS_ERR(target)) {
+		ret = ERR_PTR(PTR_ERR(target));
+		goto out;
+	}
+
+	dentry->d_op = &keyfs_file_dentry_operations;
+	d_add(dentry, target);
+	ret = NULL;
+
+ out:
+	return ret;
+
+} /* end keyfs_dir_lookup() */
+
+/*****************************************************************************/
+/*
+ * get permission for an operation on a directory
+ */
+static int keyfs_dir_permission(struct inode *inode, int mask,
+				struct nameidata *nd)
+{
+	struct key *key;
+	int ret;
+
+	key = inode->u.generic_ip;
+
+	/* we pretend a key doesn't exist if a process is not allowed to access
+	 * it */
+	ret = -ENOENT;
+	if (!key_any_permission(key, KEY_ALL))
+		goto error;
+
+	/* check the VFS permissions too */
+	ret = vfs_permission(inode, mask);
+ error:
+	return ret;
+
+} /* end keyfs_dir_permission() */
+
+/*****************************************************************************/
+/*
+ * permit the key's ownership to be changed
+ */
+static int keyfs_dir_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+	struct inode *inode;
+	unsigned int ia_valid = iattr->ia_valid;
+	struct key *key;
+	int ret;
+
+	inode = dentry->d_inode;
+	key = inode->u.generic_ip;
+
+	ret = -EINVAL;
+	if (ia_valid & (ATTR_SIZE | ATTR_MODE))
+		goto error;
+
+	/* make the changes with the locks held to prevent chown/chown races */
+	ret = -EACCES;
+	down_write(&key->sem);
+	write_lock(&key->lock);
+
+	if (!capable(CAP_SYS_ADMIN)) {
+		/* only the sysadmin can chown a key to some other UID */
+		if (ia_valid & ATTR_UID && key->uid != iattr->ia_uid)
+			goto no_access;
+
+		/* only the sysadmin can set the key's GID to a group other
+		 * than one of those that the current process subscribes to */
+		if (ia_valid & ATTR_GID &&
+		    iattr->ia_gid != key->gid &&
+		    !in_group_p(iattr->ia_gid))
+			goto no_access;
+	}
+
+	/* change the UID (have to update the quotas) */
+	if (ia_valid & ATTR_UID && iattr->ia_uid != key->uid) {
+		/* don't support UID changing yet */
+		ret = -EOPNOTSUPP;
+		goto no_access;
+	}
+
+	/* change the GID */
+	if (ia_valid & ATTR_GID) {
+		inode->i_gid = iattr->ia_gid;
+		key->gid = iattr->ia_gid;
+	}
+
+	ret = 0;
+
+	if (ia_valid & ATTR_ATIME)
+		inode->i_atime = iattr->ia_atime;
+	if (ia_valid & ATTR_MTIME)
+		inode->i_mtime = iattr->ia_mtime;
+	if (ia_valid & ATTR_CTIME)
+		inode->i_ctime = iattr->ia_ctime;
+
+ no_access:
+	write_unlock(&key->lock);
+	up_write(&key->sem);
+
+ error:
+	return ret;
+
+} /* end keyfs_dir_setattr() */
diff -puN /dev/null fs/keyfs/keyfile.c
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/fs/keyfs/keyfile.c	Wed Aug 18 17:27:20 2004
@@ -0,0 +1,874 @@
+/* keyfile.c: keyring management files
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/key.h>
+#include <asm/uaccess.h>
+#include "keyfs.h"
+
+/*
+ * miscellaneous key file operations
+ */
+static ssize_t keyfs_file_read(struct file *, char __user *, size_t, loff_t *);
+static ssize_t keyfs_file_write(struct file *, const char __user *, size_t,
+				loff_t *);
+
+static struct file_operations keyfs_file_file_operations = {
+	.read		= keyfs_file_read,
+	.write		= keyfs_file_write,
+	.llseek		= no_llseek,
+};
+
+static struct inode_operations keyfs_file_inode_operations = {
+	.getattr	= keyfs_file_getattr,
+};
+
+static int keyfs_file_d_revalidate(struct dentry *, struct nameidata *);
+
+struct dentry_operations keyfs_file_dentry_operations = {
+	.d_revalidate	= keyfs_file_d_revalidate,
+	.d_delete	= keyfs_d_delete,
+};
+
+/*
+ * key search file operation
+ */
+static ssize_t keyfs_search_file_write(struct file *, const char __user *,
+				       size_t, loff_t *);
+
+static struct file_operations keyfs_search_file_file_operations = {
+	.open		= keyfs_bifile_open,
+	.release	= keyfs_bifile_release,
+	.read		= keyfs_bifile_read,
+	.write		= keyfs_search_file_write,
+	.llseek		= no_llseek,
+};
+
+/*
+ * add/update key operation
+ */
+static ssize_t keyfs_add_file_write(struct file *, const char __user *, size_t,
+			       loff_t *);
+
+static struct file_operations keyfs_add_file_file_operations = {
+	.open		= keyfs_bifile_open,
+	.release	= keyfs_bifile_release,
+	.read		= keyfs_bifile_read,
+	.write		= keyfs_add_file_write,
+	.llseek		= no_llseek,
+};
+
+/*****************************************************************************/
+/*
+ * update the attributes on a key management file
+ */
+static void keyfs_file_update_inode(struct inode *inode)
+{
+	struct key *key = inode->u.generic_ip;
+
+	/* update the inode from the key */
+	down_read(&key->sem);
+
+	inode->i_uid	= key->uid;
+	inode->i_gid	= key->gid;
+	inode->i_mode	&= S_IFMT;
+
+	/* we have to provide access to key management files in various ways
+	 * depending on various factors */
+	switch (inode->i_ino & 0xffff) {
+	case KEYFS_INO_K_PAYLOAD:
+		/* the payload is a file if it's an ordinary key and a
+		 * directory if it's a keyring, so we need to adjust
+		 * appropriately */
+		if (key->perm & KEY_USR_READ)
+			inode->i_mode |= S_IRUSR;
+
+		if (key->perm & KEY_USR_WRITE)
+			inode->i_mode |= S_IWUSR;
+
+		if (key->perm & KEY_GRP_READ)
+			inode->i_mode |= S_IRGRP;
+
+		if (key->perm & KEY_GRP_WRITE)
+			inode->i_mode |= S_IWGRP;
+
+		if (key->perm & KEY_OTH_READ)
+			inode->i_mode |= S_IROTH;
+
+		if (key->perm & KEY_OTH_WRITE)
+			inode->i_mode |= S_IWOTH;
+
+		if (key->type == &key_type_keyring) {
+			/* copy R->X bits on a keyring */
+			inode->i_mode |= (inode->i_mode & S_IRUGO) >> 2;
+		}
+		else {
+			/* turn off write on the payload if they can't
+			 * write to it */
+			if (key->flags & KEY_FLAG_INSTANTIATED &&
+			    !key->type->update)
+				inode->i_mode &= ~S_IWUGO;
+
+			/* turn off read if they can't read it */
+			if (!key->type->read)
+				inode->i_mode &= ~S_IRUGO;
+		}
+		break;
+
+	case KEYFS_INO_K_SEARCH:
+		if (key->perm & KEY_USR_SEARCH)
+			inode->i_mode |= S_IRUSR | S_IWUSR;
+		if (key->perm & KEY_GRP_SEARCH)
+			inode->i_mode |= S_IRGRP | S_IWGRP;
+		if (key->perm & KEY_OTH_SEARCH)
+			inode->i_mode |= S_IROTH | S_IWOTH;
+		break;
+
+	case KEYFS_INO_K_ADD:
+		if (key->perm & KEY_USR_WRITE)
+			inode->i_mode |= S_IRUSR | S_IWUSR;
+		if (key->perm & KEY_GRP_WRITE)
+			inode->i_mode |= S_IRGRP | S_IWGRP;
+		if (key->perm & KEY_OTH_WRITE)
+			inode->i_mode |= S_IROTH | S_IWOTH;
+		break;
+
+	case KEYFS_INO_K_REVOKE:
+	case KEYFS_INO_K_INSTANTIATE:
+	case KEYFS_INO_K_NEGATE:
+		/* only the owner can revoke or instantiate the key */
+		inode->i_mode |= S_IWUSR;
+		break;
+
+	case KEYFS_INO_K_PERM:
+		/* only the owner can change the mode */
+		inode->i_mode |= S_IWUSR | S_IRUGO;
+		break;
+
+	default:
+		if (key->perm & KEY_USR_VIEW)
+			inode->i_mode |= S_IRUSR;
+		if (key->perm & KEY_GRP_VIEW)
+			inode->i_mode |= S_IRGRP;
+		if (key->perm & KEY_OTH_VIEW)
+			inode->i_mode |= S_IROTH;
+		break;
+	}
+
+	up_read(&key->sem);
+
+} /* end keyfs_file_update_inode() */
+
+/*****************************************************************************/
+/*
+ * get a key control/access file
+ */
+struct inode *keyfs_get_keyfile(struct inode *dir, ino_t ino)
+{
+	struct inode *inode;
+	struct key *key;
+
+	key = dir->u.generic_ip;
+
+	/* get the key file inode */
+	inode = iget_locked(dir->i_sb, (dir->i_ino & ~(ino_t)0xffffU) | ino);
+	if (inode && inode->i_state & I_NEW) {
+		atomic_inc(&key->usage);
+		inode->u.generic_ip = key;
+
+		/* initialise it */
+		inode->i_mtime	= inode->i_atime = inode->i_ctime =
+			CURRENT_TIME;
+		inode->i_blocks	= 0;
+		inode->i_blksize = 1024;
+		inode->i_mode	= S_IFREG;
+		inode->i_nlink	= 1;
+		inode->i_op	= &keyfs_file_inode_operations;
+		inode->i_fop	= &keyfs_file_file_operations;
+
+		if (key->type == &key_type_keyring) {
+			switch (inode->i_ino & 0xffff) {
+			case KEYFS_INO_K_PAYLOAD:
+				inode->i_mode	= S_IFDIR;
+				inode->i_nlink	= 2;
+				inode->i_op	= &keyfs_ring_inode_operations;
+				inode->i_fop	= &keyfs_ring_file_operations;
+				break;
+
+			case KEYFS_INO_K_SEARCH:
+				inode->i_fop =
+					&keyfs_search_file_file_operations;
+				break;
+
+			case KEYFS_INO_K_ADD:
+				inode->i_fop = &keyfs_add_file_file_operations;
+				break;
+			}
+		}
+
+		/* transfer the ownership and permissions */
+		keyfs_file_update_inode(inode);
+
+		/* success */
+		unlock_new_inode(inode);
+	}
+
+	return inode;
+
+} /* end keyfs_get_keyfile() */
+
+/*****************************************************************************/
+/*
+ * update the attributes on a key management file during pathwalk
+ */
+static int keyfs_file_d_revalidate(struct dentry *dentry, struct nameidata *nd)
+{
+	keyfs_file_update_inode(dentry->d_inode);
+	return 1;
+
+} /* end keyfs_file_d_revalidate() */
+
+/*****************************************************************************/
+/*
+ * read the attributes of an inode, updating from the key
+ */
+int keyfs_file_getattr(struct vfsmount *mnt, struct dentry *dentry,
+		       struct kstat *stat)
+{
+	keyfs_file_update_inode(dentry->d_inode);
+
+	/* transfer attributes from the inode structure to the stat
+	 * structure */
+	generic_fillattr(dentry->d_inode, stat);
+
+	return 0;
+
+} /* end keyfs_file_getattr() */
+
+/*****************************************************************************/
+/*
+ * read the data from a key file
+ */
+static ssize_t keyfs_file_read(struct file *file, char __user *_buffer,
+			       size_t buflen, loff_t *fpos)
+{
+	struct key *key;
+	const char *data;
+	char buffer[30];
+	ssize_t ret;
+	size_t len;
+
+	ret = 0;
+	if (*fpos > 0)
+		goto error;
+
+	key = file->f_dentry->d_inode->u.generic_ip;
+
+	switch (file->f_dentry->d_inode->i_ino & 0xffff) {
+	case KEYFS_INO_K_TYPE:
+		data = key->type->name;
+		goto copyout;
+
+	case KEYFS_INO_K_DESC:
+		data = key->description;
+		goto copyout;
+
+	case KEYFS_INO_K_EXPIRY:
+		sprintf(buffer, "%lu", key->expiry);
+		data = buffer;
+		goto copyout;
+
+	case KEYFS_INO_K_PERM:
+		sprintf(buffer, "0x%06x", key->perm);
+		data = buffer;
+		goto copyout;
+
+	case KEYFS_INO_K_REVOKE:
+	case KEYFS_INO_K_NEGATE:
+		ret = -EOPNOTSUPP;
+		goto error;
+
+	case KEYFS_INO_K_USAGE:
+		sprintf(buffer, "%d", atomic_read(&key->usage));
+		data = buffer;
+		goto copyout;
+
+	case KEYFS_INO_K_FLAGS:
+		sprintf(buffer, "%08x", key->flags);
+		data = buffer;
+		goto copyout;
+
+	case KEYFS_INO_K_PAYLOAD:
+		ret = -EOPNOTSUPP;
+		if (!key->type->read)
+			goto error;
+
+		/* read the data with the semaphore held (since we
+		 * might sleep) */
+		down_read(&key->sem);
+		ret = key->type->read(key, _buffer, buflen);
+		up_read(&key->sem);
+
+		if (ret > 0) {
+			if (ret > buflen)
+				ret = buflen;
+			*fpos += ret;
+		}
+		goto error;
+
+	default:
+		BUG();
+	}
+
+ error:
+	return ret;
+
+	/* if the return is a simple string constructed on the spot, copy that
+	 * to userspace */
+ copyout:
+	len = strlen(data);
+	if (len > buflen)
+		len = buflen;
+
+	ret = -EFAULT;
+	if (copy_to_user(_buffer, data, len) == 0) {
+		if (len < buflen && put_user('\n', _buffer + len) == 0)
+			ret = len + 1;
+		else
+			ret = len;
+	}
+
+	*fpos += ret;
+	goto error;
+
+} /* end keyfs_file_read() */
+
+/*****************************************************************************/
+/*
+ * write command to change a key's permissions
+ * - format: <perm>[NL]
+ */
+static ssize_t keyfs_perm_write(struct key *key,
+				const char __user *_buffer,
+				size_t buflen)
+{
+	key_perm_t perm;
+	ssize_t ret;
+	char buffer[16], *p;
+
+	ret = -EINVAL;
+	if (buflen > sizeof(buffer) - 1)
+		goto error;
+
+	ret = -EFAULT;
+	if (copy_from_user(buffer, _buffer, buflen) != 0)
+		goto error;
+	buffer[buflen] = 0;
+
+	ret = -EINVAL;
+	perm = simple_strtoul(buffer, &p, 0);
+	if (*p == '\n')
+		p++;
+	if (*p)
+		goto error;
+
+	/* check the process's owner owns it */
+	ret = -EPERM;
+	if (current->fsuid != key->uid)
+		goto error;
+
+	/* make the changes with the locks held to prevent chown/chmod races */
+	down_write(&key->sem);
+	write_lock(&key->lock);
+	key->perm = perm;
+	write_unlock(&key->lock);
+	up_write(&key->sem);
+
+	ret = buflen;
+
+ error:
+	return ret;
+
+} /* end keyfs_perm_write() */
+
+/*****************************************************************************/
+/*
+ * instantiate a key
+ * - format: [<ringid>]NL[<payload>]
+ */
+static ssize_t keyfs_instantiate_write(struct key *key,
+				       const char __user *_buffer,
+				       size_t buflen)
+{
+	key_serial_t ringid;
+	struct key *keyring;
+	ssize_t ret;
+	size_t plen;
+	char *buffer, *bend, *p, *ring, *payload;
+
+	ret = -EINVAL;
+	if (buflen > 40000)
+		goto error;
+
+	/* fetch the data into kernel memory */
+	ret = -ENOMEM;
+	buffer = kmalloc(buflen + 1, GFP_KERNEL);
+	if (!buffer)
+		goto error;
+
+	ret = -EFAULT;
+	if (copy_from_user(buffer, _buffer, buflen) != 0)
+		goto error2;
+	bend = buffer + buflen;
+	*bend = 0;
+
+	/* parse the string */
+	ret = -EINVAL;
+
+	ring = buffer;
+
+	p = memchr(buffer, '\n', bend - buffer);
+	if (!p)
+		goto error2;
+	*p++ = 0;
+	payload = p;
+	plen = bend - payload;
+	if (plen == 0)
+		payload = NULL;
+
+	/* if supplied, turn the ring ID into a keyring */
+	keyring = NULL;
+
+	if (*ring) {
+		ringid = simple_strtoul(ring, &p, 0);
+		if (*p)
+			goto error2;
+
+		/* find the target keyring (which must be writable) */
+		keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE);
+		if (IS_ERR(keyring)) {
+			ret = PTR_ERR(keyring);
+			goto error2;
+		}
+	}
+
+	/* instantiate the key, potentially attaching it to the target
+	 * keyring */
+	ret = key_instantiate_and_link(key, payload, plen, keyring);
+	if (ret < 0) {
+		ret = PTR_ERR(key);
+		goto error3;
+	}
+
+	ret = buflen;
+
+ error3:
+	key_put(keyring);
+ error2:
+	kfree(buffer);
+ error:
+	return ret;
+
+} /* end keyfs_instantiate_write() */
+
+/*****************************************************************************/
+/*
+ * negatively instantiate a key
+ * - format: [<ringid>]NL[<timeout>]NL
+ */
+static ssize_t keyfs_negate_write(struct key *key,
+				  const char __user *_buffer,
+				  size_t buflen)
+{
+	key_serial_t ringid;
+	struct key *keyring;
+	unsigned timo;
+	ssize_t ret;
+	char *buffer, *bend, *p, *ring, *timeout;
+
+	ret = -EINVAL;
+	if (buflen > 100)
+		goto error;
+
+	/* fetch the data into kernel memory */
+	ret = -ENOMEM;
+	buffer = kmalloc(buflen + 1, GFP_KERNEL);
+	if (!buffer)
+		goto error;
+
+	ret = -EFAULT;
+	if (copy_from_user(buffer, _buffer, buflen) != 0)
+		goto error2;
+	bend = buffer + buflen;
+	*bend = 0;
+
+	/* parse the string */
+	ret = -EINVAL;
+
+	ring = buffer;
+
+	p = memchr(buffer, '\n', bend - buffer);
+	if (!p)
+		goto error2;
+	*p++ = 0;
+	timeout = p;
+
+	p = memchr(p, '\n', bend - buffer);
+	if (!p)
+		goto error2;
+	*p++ = 0;
+
+	if (p != bend)
+		goto error2;
+
+	/* if supplied, turn the ring ID into a keyring */
+	keyring = NULL;
+
+	if (*ring) {
+		ringid = simple_strtoul(ring, &p, 0);
+		if (*p)
+			goto error2;
+
+		/* find the target keyring (which must be writable) */
+		keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE);
+		if (IS_ERR(keyring)) {
+			ret = PTR_ERR(keyring);
+			goto error2;
+		}
+	}
+
+	/* turn the timeout into number */
+	timo = key_negative_timeout;
+
+	if (*timeout) {
+		timo = simple_strtoul(ring, &p, 0);
+		if (*p)
+			goto error3;
+	}
+
+	/* negatively instantiate the key, potentially attaching to
+	 * the target keyring */
+	ret = key_negate_and_link(key, timo, keyring);
+	if (ret < 0) {
+		ret = PTR_ERR(key);
+		goto error3;
+	}
+
+	ret = buflen;
+
+ error3:
+	key_put(keyring);
+ error2:
+	kfree(buffer);
+ error:
+	return ret;
+
+} /* end keyfs_negate_write() */
+
+/*****************************************************************************/
+/*
+ * update the payload
+ */
+static ssize_t keyfs_payload_write(struct key *key,
+				   const char __user *_buffer,
+				   size_t buflen)
+{
+	char *buffer;
+	ssize_t ret;
+
+	ret = -EINVAL;
+	if (buflen == 0 || buflen > 32767)
+		goto error;
+
+	/* must have write permission on the key */
+	ret = -EACCES;
+	if (!key_permission(key, KEY_WRITE))
+		goto error;
+
+	/* the key must be instantiated and updatable */
+	ret = -EPERM;
+	if (!(key->flags & KEY_FLAG_INSTANTIATED) ||
+	    !key->type->update)
+		goto error;
+
+	/* fetch the data into kernel space */
+	ret = -ENOMEM;
+	buffer = kmalloc(buflen, GFP_KERNEL);
+	if (!buffer)
+		goto error;
+
+	ret = -EFAULT;
+	if (copy_from_user(buffer, _buffer, buflen) == 0) {
+		/* perform the update */
+		ret = key_update(key, buffer, buflen);
+		if (ret == 0)
+			ret = buflen;
+	}
+
+	kfree(buffer);
+
+ error:
+	return ret;
+
+} /* end keyfs_payload_write() */
+
+/*****************************************************************************/
+/*
+ * allow userspace to control keys by writing to certain files
+ */
+static ssize_t keyfs_file_write(struct file *file, const char __user *_buffer,
+				size_t buflen, loff_t *fpos)
+{
+	struct key *key;
+	ssize_t ret;
+
+	key = file->f_dentry->d_inode->u.generic_ip;
+
+	switch (file->f_dentry->d_inode->i_ino & 0xffff) {
+	case KEYFS_INO_K_PERM:
+		ret = keyfs_perm_write(key, _buffer, buflen);
+		break;
+
+	case KEYFS_INO_K_REVOKE:
+		ret = -EACCES;
+		if (!key_permission(key, KEY_WRITE))
+			goto error;
+
+		key_revoke(key);
+		ret = 0;
+		goto error;
+
+	case KEYFS_INO_K_INSTANTIATE:
+		ret = keyfs_instantiate_write(key, _buffer, buflen);
+		goto error;
+
+	case KEYFS_INO_K_NEGATE:
+		ret = keyfs_negate_write(key, _buffer, buflen);
+		goto error;
+
+	case KEYFS_INO_K_PAYLOAD:
+		ret = keyfs_payload_write(key, _buffer, buflen);
+		break;
+
+	default:
+		ret = -EPERM;
+		break;
+	}
+
+ error:
+	return ret;
+
+} /* end keyfs_file_write() */
+
+/*****************************************************************************/
+/*
+ * search for a key in a keyring
+ * - format: <type>NL<desc>NL[<destkeyring>]NL
+ */
+static ssize_t keyfs_search_file_write(struct file *file,
+				       const char __user *_buffer,
+				       size_t buflen, loff_t *fpos)
+{
+	struct keyfs_bifile_record *rec = file->private_data;
+	struct key_type *ktype;
+	key_serial_t ringid;
+	struct key *key, *keyring, *dest;
+	ssize_t ret;
+	char *buffer, *bend, *p, *type, *desc, *ring;
+
+	keyring = file->f_dentry->d_inode->u.generic_ip;
+
+	ret = -EINVAL;
+	if (rec->state != KEYFS_BIFILE_WANT_PARAMS || buflen > 40000)
+		goto error;
+
+	rec->state = KEYFS_BIFILE_GOT_RESULT;
+
+	/* fetch the data into kernel memory */
+	ret = -ENOMEM;
+	buffer = kmalloc(buflen + 1, GFP_KERNEL);
+	if (!buffer)
+		goto error;
+
+	ret = -EFAULT;
+	if (copy_from_user(buffer, _buffer, buflen) != 0)
+		goto error2;
+	bend = buffer + buflen;
+	*bend = 0;
+
+	/* parse the parameter string */
+	ret = -EINVAL;
+
+	type = buffer;
+
+	p = memchr(buffer, '\n', bend - buffer);
+	if (!p)
+		goto error2;
+	*p++ = 0;
+	desc = p;
+
+	p = memchr(p, '\n', bend - p);
+	if (!p)
+		goto error2;
+	*p++ = 0;
+	ring = p;
+
+	p = memchr(p, '\n', bend - p);
+	if (!p)
+		goto error2;
+	*p++ = 0;
+
+	if (p != bend)
+		goto error2;
+
+	/* if supplied, turn the destination ring ID into a keyring */
+	dest = NULL;
+
+	if (*ring) {
+		ringid = simple_strtoul(ring, &p, 0);
+		if (*p)
+			goto error2;
+
+		/* find the target keyring (which must be writable) */
+		dest = lookup_user_key(ringid, 1, 0, KEY_WRITE);
+		if (IS_ERR(dest)) {
+			ret = PTR_ERR(dest);
+			goto error2;
+		}
+	}
+
+	/* find the key type */
+	ktype = key_type_lookup(type);
+	if (IS_ERR(ktype)) {
+		ret = PTR_ERR(ktype);
+		goto error3;
+	}
+
+	/* search for a key */
+	key = keyring_search(keyring, ktype, desc);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		goto error4;
+	}
+
+	/* link the resulting key to the destination keyring if we can */
+	if (dest) {
+		ret = -EACCES;
+		if (!key_permission(key, KEY_LINK))
+			goto error5;
+
+		ret = key_link(dest, key);
+		if (ret < 0)
+			goto error5;
+	}
+
+	/* we'll be returning the key ID on the next read */
+	rec->id = key->serial;
+
+	ret = buflen;
+
+ error5:
+	key_put(key);
+ error4:
+	key_type_put(ktype);
+ error3:
+	key_put(dest);
+ error2:
+	kfree(buffer);
+ error:
+	if (ret < 0)
+		rec->error = ret;
+	return ret;
+
+} /* end keyfs_search_file_write() */
+
+/*****************************************************************************/
+/*
+ * create a key
+ * - format: <type>NL<desc>NL[<payload>]
+ */
+static ssize_t keyfs_add_file_write(struct file *file,
+				    const char __user *_buffer,
+				    size_t buflen, loff_t *fpos)
+{
+	struct keyfs_bifile_record *rec = file->private_data;
+	struct key *keyring, *key;
+	ssize_t ret;
+	size_t plen;
+	char *buffer, *bend, *p, *type, *desc, *payload;
+
+	keyring = file->f_dentry->d_inode->u.generic_ip;
+
+	ret = -EINVAL;
+	if (rec->state != KEYFS_BIFILE_WANT_PARAMS || buflen > 40000)
+		goto error;
+
+	rec->state = KEYFS_BIFILE_GOT_RESULT;
+
+	/* fetch the data into kernel memory */
+	ret = -ENOMEM;
+	buffer = kmalloc(buflen + 1, GFP_KERNEL);
+	if (!buffer)
+		goto error;
+
+	ret = -EFAULT;
+	if (copy_from_user(buffer, _buffer, buflen) != 0)
+		goto error2;
+	bend = buffer + buflen;
+	*bend = 0;
+
+	/* parse the parameter string */
+	ret = -EINVAL;
+
+	type = buffer;
+
+	p = memchr(buffer, '\n', bend - buffer);
+	if (!p)
+		goto error2;
+	*p++ = 0;
+	desc = p;
+
+	p = memchr(p, '\n', bend - p);
+	if (!p)
+		goto error2;
+	*p++ = 0;
+	payload = p;
+	plen = bend - payload;
+	if (plen == 0)
+		payload = NULL;
+
+	/* create or update the requested key and add it to the target
+	 * keyring */
+	key = key_create_or_update(keyring, type, desc, payload, plen, 0);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		goto error2;
+	}
+
+	/* we'll be returning the key ID on the next read */
+	rec->id = key->serial;
+	key_put(key);
+
+	ret = buflen;
+
+ error2:
+	kfree(buffer);
+ error:
+	if (ret < 0)
+		rec->error = ret;
+	return ret;
+
+} /* end keyfs_add_file_write() */
diff -puN /dev/null fs/keyfs/keyfs.h
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/fs/keyfs/keyfs.h	Wed Aug 18 17:27:20 2004
@@ -0,0 +1,86 @@
+/* keyfs.h: keyfs defines
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * 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.
+ */
+
+#include <linux/key.h>
+#include <linux/key-ui.h>
+
+/* special inodes */
+#define KEYFS_INO_ROOT		1
+#define KEYFS_INO_THREAD	2
+#define KEYFS_INO_PROCESS	3
+#define KEYFS_INO_SESSION	4
+#define KEYFS_INO_USER_SESSION	5
+#define KEYFS_INO_USER		6
+#define KEYFS_INO_REQUEST_KEY	7
+#define KEYFS_INO_SEARCH	8
+#define KEYFS_INO_JOIN_SESSION	9
+#define KEYFS_INO__LAST		9
+
+/* key dir inodes added to keyid << 16 */
+#define KEYFS_INO_K_SYMLINK	0	/* symlink to key's directory */
+#define KEYFS_INO_K_DIR		1	/* key's directory */
+#define KEYFS_INO_K_TYPE	2
+#define KEYFS_INO_K_DESC	3
+#define KEYFS_INO_K_EXPIRY	4
+#define KEYFS_INO_K_PERM	5
+#define KEYFS_INO_K_REVOKE	6
+#define KEYFS_INO_K_INSTANTIATE	7
+#define KEYFS_INO_K_NEGATE	8
+#define KEYFS_INO_K_SEARCH	9
+#define KEYFS_INO_K_USAGE	10
+#define KEYFS_INO_K_FLAGS	11
+#define KEYFS_INO_K_ADD		12
+#define KEYFS_INO_K_PAYLOAD	13
+#define KEYFS_INO_K__LAST	13
+
+/* root.c */
+extern struct inode *keyfs_get_rootdir(struct super_block *super);
+extern int keyfs_d_delete(struct dentry *dentry);
+
+/* rootfile.c */
+enum keyfs_bifile_state {
+	KEYFS_BIFILE_WANT_PARAMS,
+	KEYFS_BIFILE_GOT_RESULT,
+	KEYFS_BIFILE_DONE
+};
+
+struct keyfs_bifile_record {
+	enum keyfs_bifile_state	state;
+	key_serial_t		id;
+	ssize_t			error;
+};
+
+extern ssize_t keyfs_bifile_read(struct file *, char __user *, size_t, loff_t *);
+extern int keyfs_bifile_open(struct inode *, struct file *);
+extern int keyfs_bifile_release(struct inode *, struct file *);
+
+extern struct inode *keyfs_get_rootfile(struct super_block *super, ino_t ino);
+
+/* rootlink.c */
+extern struct inode *keyfs_get_rootlink(struct super_block *super, ino_t ino);
+
+/* keydir.c */
+extern struct dentry_operations keyfs_dir_dentry_operations;
+
+extern struct inode *keyfs_get_keydir(struct super_block *super,
+				      key_serial_t id);
+
+/* keyfile.c */
+extern struct dentry_operations keyfs_file_dentry_operations;
+
+extern struct inode *keyfs_get_keyfile(struct inode *dir, ino_t ino);
+
+extern int keyfs_file_getattr(struct vfsmount *mnt, struct dentry *dentry,
+			      struct kstat *stat);
+
+/* ringdir.c */
+extern struct file_operations keyfs_ring_file_operations;
+extern struct inode_operations keyfs_ring_inode_operations;
diff -puN /dev/null fs/keyfs/Makefile
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/fs/keyfs/Makefile	Wed Aug 18 17:27:20 2004
@@ -0,0 +1,14 @@
+#
+# Makefile for Key view filesystem
+#
+
+keyfs-objs := \
+	super.o \
+	root.o \
+	rootfile.o \
+	rootlink.o \
+	keydir.o \
+	keyfile.o \
+	ringdir.o
+
+obj-$(CONFIG_KEYFS)  := keyfs.o
diff -puN /dev/null fs/keyfs/ringdir.c
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/fs/keyfs/ringdir.c	Wed Aug 18 17:27:20 2004
@@ -0,0 +1,467 @@
+/* ringdir.c: keyring directory
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/err.h>
+#include <linux/key.h>
+#include <asm/uaccess.h>
+#include "keyfs.h"
+
+/*
+ * keyring dir operations
+ */
+static int keyfs_ring_readdir(struct file *, void *, filldir_t);
+static struct dentry *keyfs_ring_lookup(struct inode *, struct dentry *,
+					struct nameidata *);
+
+struct file_operations keyfs_ring_file_operations = {
+	.readdir	= keyfs_ring_readdir,
+};
+
+static int keyfs_ring_unlink(struct inode *, struct dentry *);
+static int keyfs_ring_symlink(struct inode *, struct dentry *, const char *);
+
+struct inode_operations keyfs_ring_inode_operations = {
+	.getattr	= keyfs_file_getattr,
+	.lookup		= keyfs_ring_lookup,
+	.unlink		= keyfs_ring_unlink,
+	.symlink	= keyfs_ring_symlink,
+};
+
+/*
+ * key link operations
+ */
+static struct file_operations keyfs_keylink_file_operations = {
+};
+
+static int keyfs_keylink_getattr(struct vfsmount *, struct dentry *,
+				 struct kstat *);
+
+static int keyfs_keylink_readlink(struct dentry *, char __user *, int);
+static int keyfs_keylink_follow_link(struct dentry *, struct nameidata *);
+
+static struct inode_operations keyfs_keylink_inode_operations = {
+	.getattr	= keyfs_keylink_getattr,
+	.follow_link	= keyfs_keylink_follow_link,
+	.readlink	= keyfs_keylink_readlink,
+};
+
+static int keyfs_keylink_d_revalidate(struct dentry *, struct nameidata *);
+
+static struct dentry_operations keyfs_keylink_dentry_operations = {
+	.d_revalidate	= keyfs_keylink_d_revalidate,
+	.d_delete	= keyfs_d_delete,
+};
+
+/*****************************************************************************/
+/*
+ * get a key symlink
+ */
+static struct inode *keyfs_get_keylink(struct super_block *super,
+				       key_serial_t id)
+{
+	struct inode *inode;
+	struct key *key;
+
+	key = key_lookup(id);
+	if (IS_ERR(key)) {
+		inode = ERR_PTR(PTR_ERR(key));
+		goto error;
+	}
+
+	/* get the appropriate key symlink inode */
+	inode = iget_locked(super, (id << 16) + KEYFS_INO_K_SYMLINK);
+	if (inode && inode->i_state & I_NEW) {
+		/* initialise it */
+		down_read(&key->sem);
+
+		inode->i_mtime	= inode->i_atime = inode->i_ctime =
+			CURRENT_TIME;
+		inode->i_blocks	= 0;
+		inode->i_blksize = 1024;
+		inode->i_uid	= key->uid;
+		inode->i_gid	= key->gid;
+		inode->i_mode	= S_IFLNK | S_IRWXUGO;
+		inode->i_op	= &keyfs_keylink_inode_operations;
+		inode->i_fop	= &keyfs_keylink_file_operations;
+		inode->i_nlink	= 2;
+		inode->u.generic_ip = key;
+
+		up_read(&key->sem);
+
+		/* success */
+		unlock_new_inode(inode);
+	}
+	else if (inode) {
+		/* reuse one that already exists */
+		key_put(key);
+	}
+	else {
+		/* error */
+		inode = ERR_PTR(-ENOMEM);
+		key_put(key);
+	}
+
+ error:
+	return inode;
+
+} /* end keyfs_get_keylink() */
+
+/*****************************************************************************/
+/*
+ * update the attributes on a keyring symlink
+ */
+static int keyfs_keylink_d_revalidate(struct dentry *dentry, struct nameidata *nd)
+{
+	struct inode *inode;
+	struct key *key;
+
+	inode = dentry->d_inode;
+	key = inode->u.generic_ip;
+
+	/* update the inode from the key */
+	down_read(&key->sem);
+
+	inode->i_uid = key->uid;
+	inode->i_gid = key->gid;
+
+	up_read(&key->sem);
+
+	return 1;
+
+} /* end keyfs_keylink_d_revalidate() */
+
+/*****************************************************************************/
+/*
+ * read the attributes of an inode, updating from the key
+ */
+static int keyfs_keylink_getattr(struct vfsmount *mnt, struct dentry *dentry,
+				 struct kstat *stat)
+{
+	/* update the inode */
+	keyfs_keylink_d_revalidate(dentry, NULL);
+
+	/* transfer attributes from the inode structure to the stat
+	 * structure */
+	generic_fillattr(dentry->d_inode, stat);
+
+	return 0;
+
+} /* end keyfs_keylink_getattr() */
+
+/*****************************************************************************/
+/*
+ * enumerate the keys in the ring directory
+ */
+static int keyfs_ring_readdir(struct file *file, void *cookie, filldir_t filldir)
+{
+	struct keyring_list *klist;
+	struct key *key;
+	loff_t pos;
+	char id[16];
+	int ret, loop, n;
+
+	key = file->f_dentry->d_inode->u.generic_ip;
+	pos = file->f_pos;
+
+	/* read the usual "." and ".." first followed by the key symlinks */
+	switch (file->f_pos) {
+	case 0:
+		ret = filldir(cookie, ".", 1, file->f_pos,
+			      file->f_dentry->d_inode->i_ino, DT_DIR);
+		if (ret < 0)
+			goto done;
+		file->f_pos++;
+
+	case 1:
+		ret = filldir(cookie, "..", 2, file->f_pos,
+			      parent_ino(file->f_dentry), DT_DIR);
+		if (ret < 0)
+			goto done;
+		file->f_pos++;
+		pos--;
+
+	default:
+		break;
+	}
+
+	pos -= 2;
+
+	/* then come the key links, a symlink for each */
+	down_read(&key->sem);
+
+	klist = key->payload.subscriptions;
+	if (!klist || pos >= klist->nkeys)
+		goto done_unlock;
+
+	/* enumerate the keys */
+	for (loop = pos; loop < klist->nkeys; loop++) {
+		/* each symlink's name is the corresponding key serial
+		 * number, and the inode number is based on that too */
+		n = sprintf(id, "%d", klist->keys[loop]->serial);
+
+		ret = filldir(cookie, id, n, file->f_pos,
+			      (ino_t) klist->keys[loop] << 16 |
+			      KEYFS_INO_K_SYMLINK,
+			      DT_LNK);
+		if (ret < 0)
+			goto done_unlock;
+
+		file->f_pos++;
+	}
+
+ done_unlock:
+	up_read(&key->sem);
+
+ done:
+	return 0;
+
+} /* end keyfs_ring_readdir() */
+
+/*****************************************************************************/
+/*
+ * look up an inode in the ring directory
+ */
+static struct dentry *keyfs_ring_lookup(struct inode *dir,
+					struct dentry *dentry,
+					struct nameidata *nd)
+{
+	struct keyring_list *klist;
+	struct dentry *ret;
+	struct inode *target;
+	struct key *key;
+	key_serial_t id;
+	const char *name;
+	char *p;
+	int loop;
+
+	key = dir->u.generic_ip;
+
+	/* determine which virtual file they want */
+	name = dentry->d_name.name;
+
+	switch (dentry->d_name.len) {
+	case 1:
+		if (memcmp(name, ".", 1) == 0) {
+			target = igrab(dir);
+			goto instantiate;
+		}
+		break;
+
+	case 2:
+		if (memcmp(name, "..", 2)==0) {
+			target = igrab(dir);
+			goto instantiate;
+		}
+		break;
+
+
+	default:
+		break;
+	}
+
+	/* it's going to be a keyring symlink then */
+	id = simple_strtoul(name, &p, 10);
+	if (*p)
+		goto noent; /* not a decimal number */
+
+	/* check the keyring has a link to the specified key */
+	ret = NULL;
+
+	read_lock(&key->lock);
+
+	klist = key->payload.subscriptions;
+	if (!klist)
+		goto noent_unlock;
+
+	for (loop = 0; loop < klist->nkeys; loop++)
+		if (klist->keys[loop]->serial == id)
+			goto found;
+
+	goto noent_unlock;
+ found:
+
+	read_unlock(&key->lock);
+
+	/* get the symlink inode */
+	target = keyfs_get_keylink(dir->i_sb, id);
+	if (!IS_ERR(target))
+		goto instantiate;
+
+	if (ret == ERR_PTR(-ENOENT))
+		goto noent;
+
+	ret = ERR_PTR(PTR_ERR(target));
+	goto error;
+
+	/* instantiate the dentry */
+ instantiate:
+	dentry->d_op = &keyfs_keylink_dentry_operations;
+	d_add(dentry, target);
+	ret = NULL;
+
+ error:
+	return ret;
+
+ noent_unlock:
+	read_unlock(&key->lock);
+ noent:
+	/* make a negative dentry */
+	d_add(dentry, NULL);
+	ret = NULL;
+	goto error;
+
+} /* end keyfs_ring_lookup() */
+
+/*****************************************************************************/
+/*
+ * unlink a key from a keyring
+ */
+static int keyfs_ring_unlink(struct inode *dir, struct dentry *dentry)
+{
+	struct key *keyring, *key;
+	int ret;
+
+	keyring = dir->u.generic_ip;
+	key = dentry->d_inode->u.generic_ip;
+
+	/* we must have write permission on the keyring */
+	ret = -EPERM;
+	if (key_permission(keyring, KEY_WRITE))
+		/* attempt the unlink */
+		ret = key_unlink(keyring, key);
+
+	return ret;
+
+} /* end keyfs_ring_unlink() */
+
+/*****************************************************************************/
+/*
+ * link a key into a keyring
+ */
+static int keyfs_ring_symlink(struct inode *dir, struct dentry *dentry,
+			      const char *to)
+{
+	struct inode *inode;
+	key_serial_t id;
+	struct key *keyring, *key;
+	char *p;
+	int ret;
+
+	keyring = dir->u.generic_ip;
+
+	/* must have write permission on the keyring */
+	ret = -EPERM;
+	if (!key_permission(keyring, KEY_WRITE))
+		goto error;
+
+	/* extract the ID from the symlink target */
+	ret = -EINVAL;
+	if (strncmp(to, "../../", 6) != 0)
+		goto error;
+	id = simple_strtoul(to + 6, &p, 10);
+	if (*p)
+		goto error;
+
+	/* the new filename must specify the same ID */
+	if (strlen(to + 6) != dentry->d_name.len)
+		goto error;
+
+	if (memcmp(dentry->d_name.name, to + 6, dentry->d_name.len) != 0)
+		goto error;
+
+	/* create an inode for the symlink */
+	inode = keyfs_get_keylink(dir->i_sb, id);
+	if (IS_ERR(inode)) {
+		ret = PTR_ERR(inode);
+		goto error;
+	}
+
+	key = inode->u.generic_ip;
+
+	/* we don't admit a key exists if we have no permissions on it at
+	 * all */
+	ret = -ENOENT;
+	if (!key_any_permission(key, KEY_ALL))
+		goto error2;
+
+	/* check the key has link access */
+	ret = -EPERM;
+	if (!key_permission(key, KEY_LINK))
+		goto error2;
+
+	/* attempt to forge a link */
+	ret = key_link(keyring, key);
+	if (ret < 0)
+		goto error2;
+
+	/* point the dentry at the inode */
+	d_instantiate(dentry, inode);
+	inode = NULL;
+	ret = 0;
+
+ error2:
+	iput(inode);
+ error:
+	return ret;
+
+} /* end keyfs_ring_symlink() */
+
+/*****************************************************************************/
+/*
+ * read a symlink that goes from a keyring to a key
+ */
+static int keyfs_keylink_readlink(struct dentry *dentry, char __user *_buffer,
+				  int buflen)
+{
+	key_serial_t id;
+	char buffer[30];
+	int n;
+
+	id = dentry->d_inode->i_ino >> 16;
+
+	/* we render the symlink in the form required for pathwalk */
+	n = sprintf(buffer, "../../%d", id);
+	if (buflen > n)
+		buflen = n;
+
+	/* and pass back to userspace */
+	if (copy_to_user(_buffer, buffer, buflen) != 0)
+		buflen = -EFAULT;
+
+	return buflen;
+
+} /* end keyfs_keylink_readlink() */
+
+/*****************************************************************************/
+/*
+ * follow a symlink from a keyring to a key
+ */
+static int keyfs_keylink_follow_link(struct dentry *dentry,
+				     struct nameidata *nd)
+{
+	key_serial_t id;
+	char buffer[30];
+
+	id = dentry->d_inode->i_ino >> 16;
+
+	/* we render the symlink in the form required for pathwalk */
+	sprintf(buffer, "../../%d", id);
+	nd_set_link(nd, buffer);
+
+	return 0;
+
+} /* end keyfs_keylink_follow_link() */
diff -puN /dev/null fs/keyfs/root.c
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/fs/keyfs/root.c	Wed Aug 18 17:27:20 2004
@@ -0,0 +1,362 @@
+/* root.c: keyfs root inode operations
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/key.h>
+#include <asm/uaccess.h>
+#include "keyfs.h"
+
+/*
+ * root directory operations
+ */
+static int keyfs_root_readdir(struct file *, void *, filldir_t);
+
+static struct file_operations keyfs_root_file_operations = {
+	.readdir	= keyfs_root_readdir,
+};
+
+static struct dentry *keyfs_root_lookup(struct inode *, struct dentry *,
+					struct nameidata *);
+
+static struct inode_operations keyfs_root_inode_operations = {
+	.lookup		= keyfs_root_lookup,
+};
+
+/*****************************************************************************/
+/*
+ * get the root directory
+ */
+struct inode *keyfs_get_rootdir(struct super_block *super)
+{
+	struct inode *inode;
+
+	/* get the root inode */
+	inode = iget_locked(super, KEYFS_INO_ROOT);
+	if (inode && inode->i_state & I_NEW) {
+		/* initialise it */
+		inode->i_mtime	= inode->i_atime = inode->i_ctime =
+			CURRENT_TIME;
+		inode->i_blocks	= 0;
+		inode->i_blksize = 1024;
+		inode->i_uid	= 0;
+		inode->i_gid	= 0;
+		inode->i_mode	= S_IFDIR | S_IRUGO | S_IXUGO;
+		inode->i_op	= &keyfs_root_inode_operations;
+		inode->i_fop	= &keyfs_root_file_operations;
+		inode->i_nlink	= 2;
+
+		/* success */
+		unlock_new_inode(inode);
+	}
+
+	return inode;
+
+} /* end keyfs_get_rootdir() */
+
+/*****************************************************************************/
+/*
+ * enumerate the keys in the root directory
+ */
+static int keyfs_root_readdir(struct file *file, void *cookie, filldir_t filldir)
+{
+	struct rb_node *_p;
+	struct key *next, *cursor;
+	loff_t pos;
+	ino_t ino;
+	char buf[16];
+	int ret, n;
+
+	pos = file->f_pos;
+
+	/* read the usual "." and ".." first and then the master control files
+	 * and special symlinks to process/user specific keyrings
+	 */
+	switch (file->f_pos) {
+	case 0:
+		ret = filldir(cookie, ".", 1, file->f_pos,
+			      file->f_dentry->d_inode->i_ino, DT_DIR);
+		if (ret < 0)
+			goto out;
+		file->f_pos++;
+
+	case 1:
+		ret = filldir(cookie, "..", 2, file->f_pos,
+			      parent_ino(file->f_dentry), DT_DIR);
+		if (ret < 0)
+			goto out;
+		file->f_pos++;
+		pos--;
+
+	case 2:
+		ret = filldir(cookie, "thread", 6, file->f_pos,
+			      KEYFS_INO_THREAD, DT_LNK);
+		if (ret < 0)
+			goto out;
+		file->f_pos++;
+
+	case 3:
+		ret = filldir(cookie, "process", 7, file->f_pos,
+			      KEYFS_INO_PROCESS, DT_LNK);
+		if (ret < 0)
+			goto out;
+		file->f_pos++;
+
+	case 4:
+		ret = filldir(cookie, "session", 7, file->f_pos,
+			      KEYFS_INO_SESSION, DT_LNK);
+		if (ret < 0)
+			goto out;
+		file->f_pos++;
+
+	case 5:
+		ret = filldir(cookie, "user-session", 12, file->f_pos,
+			      KEYFS_INO_USER_SESSION, DT_LNK);
+		if (ret < 0)
+			goto out;
+		file->f_pos++;
+
+	case 6:
+		ret = filldir(cookie, "user", 4, file->f_pos,
+			      KEYFS_INO_USER, DT_LNK);
+		if (ret < 0)
+			goto out;
+		file->f_pos++;
+
+	case 7:
+		ret = filldir(cookie, "request-key", 11, file->f_pos,
+			      KEYFS_INO_REQUEST_KEY, DT_REG);
+		if (ret < 0)
+			goto out;
+		file->f_pos++;
+
+	case 8:
+		ret = filldir(cookie, "search", 6, file->f_pos,
+			      KEYFS_INO_SEARCH, DT_REG);
+		if (ret < 0)
+			goto out;
+		file->f_pos++;
+
+	case 9:
+		ret = filldir(cookie, "join-session", 12, file->f_pos,
+			      KEYFS_INO_REQUEST_KEY, DT_REG);
+		if (ret < 0)
+			goto out;
+		file->f_pos++;
+
+	default:
+		break;
+	}
+
+	/* then come the keys, a directory for each for each one we can see */
+	pos = file->f_pos - 10;
+
+	cursor = NULL;
+	ret = 0;
+
+	spin_lock(&key_serial_lock);
+
+	/* start with the Nth key */
+	_p = rb_first(&key_serial_tree);
+	if (!_p)
+		goto out_unlock;
+
+	while (pos > 0) {
+		pos--;
+		_p = rb_next(_p);
+		if (!_p)
+			goto out_unlock;
+	}
+
+	/* loop around using the key currently being examined as a cursor (we
+	 * keep it pinned whilst we're outside of the lock so that it doesn't
+	 * get removed from the tree) */
+	for (;;) {
+		next = rb_entry(_p, struct key, serial_node);
+		atomic_inc(&next->usage);
+
+		spin_unlock(&key_serial_lock);
+
+		key_put(cursor);
+		cursor = next;
+
+		/* a process is only allowed to see a key if it has at least
+		 * one applicable permission */
+		if (key_any_permission(cursor, KEY_ALL)) {
+			/* each directory's name is the corresponding key
+			 * serial number, and the inode number is based on that
+			 * too */
+			n = sprintf(buf, "%d", cursor->serial);
+
+			ino = (ino_t) cursor->serial << 16 | KEYFS_INO_K_DIR;
+
+			ret = filldir(cookie, buf, n, file->f_pos,
+				      ino, DT_DIR);
+			if (ret < 0)
+				goto out_put;
+		}
+
+		file->f_pos++;
+
+		/* advance the cursor under lock */
+		spin_lock(&key_serial_lock);
+		_p = rb_next(_p);
+		if (!_p)
+			goto out_unlock;
+	}
+
+ out_unlock:
+	spin_unlock(&key_serial_lock);
+ out_put:
+	key_put(cursor);
+ out:
+	return 0;
+
+} /* end keyfs_root_readdir() */
+
+/*****************************************************************************/
+/*
+ * look up an inode in the root directory
+ */
+static struct dentry *keyfs_root_lookup(struct inode *dir,
+					struct dentry *dentry,
+					struct nameidata *nd)
+{
+	struct dentry_operations *dops = NULL;
+	struct dentry *ret;
+	struct inode *target;
+	key_serial_t id;
+	const char *name;
+	char *p;
+
+	/* determine which virtual file they're asking for */
+	name = dentry->d_name.name;
+
+	switch (dentry->d_name.len) {
+	case 1:
+		if (memcmp(name, ".", 1) == 0) {
+			target = igrab(dir);
+			goto instantiate;
+		}
+		break;
+
+	case 2:
+		if (memcmp(name, "..", 2)==0) {
+			target = igrab(dir);
+			goto instantiate;
+		}
+		break;
+
+	case 4:
+		if (memcmp(name, "user", 4) == 0) {
+			target = keyfs_get_rootlink(dir->i_sb,
+						    KEYFS_INO_USER);
+			goto instantiate;
+		}
+		break;
+
+	case 6:
+		if (memcmp(name, "thread", 6) == 0) {
+			target = keyfs_get_rootlink(dir->i_sb,
+						    KEYFS_INO_THREAD);
+			goto instantiate;
+		}
+		if (memcmp(name, "search", 6) == 0) {
+			target = keyfs_get_rootfile(dir->i_sb,
+						    KEYFS_INO_SEARCH);
+			goto instantiate;
+		}
+		break;
+
+	case 7:
+		if (memcmp(name, "process", 7) == 0) {
+			target = keyfs_get_rootlink(dir->i_sb,
+						    KEYFS_INO_PROCESS);
+			goto instantiate;
+		}
+		if (memcmp(name, "session", 7) == 0) {
+			target = keyfs_get_rootlink(dir->i_sb,
+						    KEYFS_INO_SESSION);
+			goto instantiate;
+		}
+		break;
+
+	case 11:
+		if (memcmp(name, "request-key", 11) == 0) {
+			target = keyfs_get_rootfile(dir->i_sb,
+						    KEYFS_INO_REQUEST_KEY);
+			goto instantiate;
+		}
+		break;
+
+	case 12:
+		if (memcmp(name, "user-session", 12) == 0) {
+			target = keyfs_get_rootlink(dir->i_sb,
+						    KEYFS_INO_USER_SESSION);
+			goto instantiate;
+		}
+		if (memcmp(name, "join-session", 12) == 0) {
+			target = keyfs_get_rootfile(dir->i_sb,
+						    KEYFS_INO_JOIN_SESSION);
+			goto instantiate;
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	/* see if they asked for a virtual keyring directory */
+	id = simple_strtoul(name, &p, 10);
+	if (*p)
+		goto noent; /* not a decimal number, so no */
+
+	target = keyfs_get_keydir(dir->i_sb, id);
+	if (!IS_ERR(target))
+		goto instantiate_keydir;
+	if (target == ERR_PTR(-ENOENT))
+		goto noent;
+
+	ret = ERR_PTR(PTR_ERR(target));
+	goto out;
+
+	/* instantiate the dentry */
+ instantiate_keydir:
+	dops = &keyfs_dir_dentry_operations;
+ instantiate:
+	dentry->d_op = dops;
+	d_add(dentry, target);
+	ret = NULL;
+
+ out:
+	return ret;
+
+ noent:
+	/* make a negative dentry */
+	d_add(dentry, NULL);
+	ret = NULL;
+	goto out;
+
+} /* end keyfs_root_lookup() */
+
+/*****************************************************************************/
+/*
+ * we don't want key-specifc files cached, lest the dentry-cache pins keys
+ */
+int keyfs_d_delete(struct dentry *dentry)
+{
+	return 1;
+
+} /* end keyfs_d_delete() */
diff -puN /dev/null fs/keyfs/rootfile.c
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/fs/keyfs/rootfile.c	Wed Aug 18 17:27:20 2004
@@ -0,0 +1,455 @@
+/* rootfile.c: keyfs root file operations
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/key.h>
+#include <asm/uaccess.h>
+#include "keyfs.h"
+
+static struct inode_operations keyfs_rootfile_inode_operations = {
+};
+
+/*
+ * request-key operation
+ */
+static ssize_t keyfs_request_key_write(struct file *, const char __user *,
+				       size_t, loff_t *);
+
+static struct file_operations keyfs_request_key_file_operations = {
+	.open		= keyfs_bifile_open,
+	.release	= keyfs_bifile_release,
+	.read		= keyfs_bifile_read,
+	.write		= keyfs_request_key_write,
+	.llseek		= no_llseek,
+};
+
+/*
+ * search operation
+ */
+static ssize_t keyfs_search_write(struct file *, const char __user *,
+				  size_t, loff_t *);
+
+static struct file_operations keyfs_search_file_operations = {
+	.open		= keyfs_bifile_open,
+	.release	= keyfs_bifile_release,
+	.read		= keyfs_bifile_read,
+	.write		= keyfs_search_write,
+	.llseek		= no_llseek,
+};
+
+/*
+ * join session operation
+ */
+static ssize_t keyfs_join_session_write(struct file *, const char __user *,
+					size_t, loff_t *);
+
+static struct file_operations keyfs_join_session_file_operations = {
+	.open		= keyfs_bifile_open,
+	.release	= keyfs_bifile_release,
+	.read		= keyfs_bifile_read,
+	.write		= keyfs_join_session_write,
+	.llseek		= no_llseek,
+};
+
+/*****************************************************************************/
+/*
+ * get a root file inode
+ */
+struct inode *keyfs_get_rootfile(struct super_block *super, ino_t ino)
+{
+	struct inode *inode;
+
+	/* get the inode */
+	inode = iget_locked(super, ino);
+	if (inode && inode->i_state & I_NEW) {
+		/* initialise it */
+		inode->i_mtime	= inode->i_atime = inode->i_ctime =
+			CURRENT_TIME;
+		inode->i_blocks	= 0;
+		inode->i_blksize = 1024;
+		inode->i_uid	= 0;
+		inode->i_gid	= 0;
+		inode->i_op	= &keyfs_rootfile_inode_operations;
+		inode->i_nlink	= 1;
+
+		switch (ino) {
+		case KEYFS_INO_REQUEST_KEY:
+			inode->i_fop	= &keyfs_request_key_file_operations;
+			inode->i_mode	= S_IFREG | S_IRUGO | S_IWUGO;
+			break;
+
+		case KEYFS_INO_SEARCH:
+			inode->i_fop	= &keyfs_search_file_operations;
+			inode->i_mode	= S_IFREG | S_IRUGO | S_IWUGO;
+			break;
+
+		case KEYFS_INO_JOIN_SESSION:
+			inode->i_fop	= &keyfs_join_session_file_operations;
+			inode->i_mode	= S_IFREG | S_IRUGO | S_IWUGO;
+			break;
+		}
+
+		/* success */
+		unlock_new_inode(inode);
+	}
+
+	return inode;
+
+} /* end keyfs_get_rootfile() */
+
+/*****************************************************************************/
+/*
+ * open a two-phase command file
+ */
+int keyfs_bifile_open(struct inode *inode, struct file *file)
+{
+	int ret;
+
+	/* allocate and initialise a state record */
+	ret = -ENOMEM;
+	file->private_data =
+		kmalloc(sizeof(struct keyfs_bifile_record), GFP_KERNEL);
+	if (!file->private_data)
+		goto error;
+
+	memset(file->private_data, 0, sizeof(struct keyfs_bifile_record));
+	ret = 0;
+
+ error:
+	return ret;
+
+} /* end keyfs_bifile_open() */
+
+/*****************************************************************************/
+/*
+ * release a two-phase command file
+ */
+int keyfs_bifile_release(struct inode *inode, struct file *file)
+{
+	kfree(file->private_data);
+	return 0;
+
+} /* end keyfs_bifile_release() */
+
+/*****************************************************************************/
+/*
+ * read the operation result from a two-phase command file (key ID)
+ */
+ssize_t keyfs_bifile_read(struct file *file, char __user *_buffer,
+			  size_t buflen, loff_t *fpos)
+{
+	struct keyfs_bifile_record *rec = file->private_data;
+	char buffer[16];
+	ssize_t ret, n;
+
+	ret = rec->error;
+	if (rec->error)
+		goto error;
+
+	ret = -EINVAL;
+	if (rec->state == KEYFS_BIFILE_WANT_PARAMS)
+		goto error;
+
+	ret = 0;
+	if (rec->state != KEYFS_BIFILE_GOT_RESULT)
+		goto error;
+
+	rec->state = KEYFS_BIFILE_DONE;
+
+	/* allow userspace to read back the new keyID */
+	n = sprintf(buffer, "%d\n", rec->id);
+	if (n > buflen)
+		n = buflen;
+
+	ret = -EAGAIN;
+	if (copy_to_user(_buffer, buffer, n) != 0)
+		goto error;
+
+	ret = n;
+
+ error:
+	return ret;
+
+} /* end keyfs_bifile_read() */
+
+/*****************************************************************************/
+/*
+ * request a key
+ * - format: <type>NL<desc>NL
+ */
+static ssize_t keyfs_request_key_write(struct file *file,
+				       const char __user *_buffer,
+				       size_t buflen, loff_t *fpos)
+{
+	struct keyfs_bifile_record *rec = file->private_data;
+	struct key_type *ktype;
+	struct key *key;
+	ssize_t ret;
+	char *buffer, *bend, *p, *type, *desc;
+
+	ret = -EINVAL;
+	if (rec->state != KEYFS_BIFILE_WANT_PARAMS || buflen > 40000)
+		goto error;
+
+	rec->state = KEYFS_BIFILE_GOT_RESULT;
+
+	/* fetch the data into kernel memory */
+	ret = -ENOMEM;
+	buffer = kmalloc(buflen + 1, GFP_KERNEL);
+	if (!buffer)
+		goto error;
+
+	ret = -EFAULT;
+	if (copy_from_user(buffer, _buffer, buflen) != 0)
+		goto error2;
+	bend = buffer + buflen;
+	*bend = 0;
+
+	/* parse the parameter string */
+	ret = -EINVAL;
+
+	type = buffer;
+
+	p = memchr(buffer, '\n', bend - buffer);
+	if (!p)
+		goto error2;
+	*p++ = 0;
+	desc = p;
+
+	p = memchr(p, '\n', bend - p);
+	if (!p)
+		goto error2;
+	*p++ = 0;
+
+	if (p != bend)
+		goto error2;
+
+	/* find the key type */
+	ktype = key_type_lookup(type);
+	if (IS_ERR(ktype)) {
+		ret = PTR_ERR(ktype);
+		goto error2;
+	}
+
+	/* request the key */
+	key = request_key(ktype, desc, 1);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		goto error3;
+	}
+
+	/* we'll be returning the key ID on the next read */
+	rec->id = key->serial;
+	key_put(key);
+
+	ret = buflen;
+
+ error3:
+	key_type_put(ktype);
+ error2:
+	kfree(buffer);
+ error:
+	if (ret < 0)
+		rec->error = ret;
+	return ret;
+
+} /* end keyfs_request_key_write() */
+
+/*****************************************************************************/
+/*
+ * search for a key
+ * - format: <type>NL<desc>NL[<keyring>]NL
+ */
+static ssize_t keyfs_search_write(struct file *file,
+				  const char __user *_buffer,
+				  size_t buflen, loff_t *fpos)
+{
+	struct keyfs_bifile_record *rec = file->private_data;
+	struct key_type *ktype;
+	key_serial_t ringid;
+	struct key *key, *keyring;
+	ssize_t ret;
+	char *buffer, *bend, *p, *type, *desc, *ring;
+
+	ret = -EINVAL;
+	if (rec->state != KEYFS_BIFILE_WANT_PARAMS || buflen > 40000)
+		goto error;
+
+	rec->state = KEYFS_BIFILE_GOT_RESULT;
+
+	/* fetch the data into kernel memory */
+	ret = -ENOMEM;
+	buffer = kmalloc(buflen + 1, GFP_KERNEL);
+	if (!buffer)
+		goto error;
+
+	ret = -EFAULT;
+	if (copy_from_user(buffer, _buffer, buflen) != 0)
+		goto error2;
+	bend = buffer + buflen;
+	*bend = 0;
+
+	/* parse the parameter string */
+	ret = -EINVAL;
+
+	type = buffer;
+
+	p = memchr(buffer, '\n', bend - buffer);
+	if (!p)
+		goto error2;
+	*p++ = 0;
+	desc = p;
+
+	p = memchr(p, '\n', bend - p);
+	if (!p)
+		goto error2;
+	*p++ = 0;
+	ring = p;
+
+	p = memchr(p, '\n', bend - p);
+	if (!p)
+		goto error2;
+	*p++ = 0;
+
+	if (p != bend)
+		goto error2;
+
+	/* if supplied, turn the ring ID into a keyring */
+	keyring = NULL;
+
+	if (*ring) {
+		ringid = simple_strtoul(ring, &p, 0);
+		if (*p)
+			goto error2;
+
+		/* find the target keyring (which must be writable) */
+		keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE);
+		if (IS_ERR(keyring)) {
+			ret = PTR_ERR(keyring);
+			goto error2;
+		}
+	}
+
+	/* find the key type */
+	ktype = key_type_lookup(type);
+	if (IS_ERR(ktype)) {
+		ret = PTR_ERR(ktype);
+		goto error3;
+	}
+
+	/* search for the key */
+	key = search_process_keyrings(ktype, desc);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		goto error4;
+	}
+
+	/* link the resulting key to the destination keyring if we can */
+	if (keyring) {
+		ret = -EACCES;
+		if (!key_permission(key, KEY_LINK))
+			goto error5;
+
+		ret = key_link(keyring, key);
+		if (ret < 0)
+			goto error5;
+	}
+
+	/* we'll be returning the key ID on the next read */
+	rec->id = key->serial;
+
+	ret = buflen;
+
+ error5:
+	key_put(key);
+ error4:
+	key_type_put(ktype);
+ error3:
+	key_put(keyring);
+ error2:
+	kfree(buffer);
+ error:
+	if (ret < 0)
+		rec->error = ret;
+	return ret;
+
+} /* end keyfs_search_write() */
+
+/*****************************************************************************/
+/*
+ * join a session keyring, creating it if it doesn't exist
+ * - format: [<name>][NL]
+ */
+static ssize_t keyfs_join_session_write(struct file *file,
+					const char __user *_buffer,
+					size_t buflen, loff_t *fpos)
+{
+	struct keyfs_bifile_record *rec = file->private_data;
+	ssize_t ret;
+	long jret;
+	char *buffer, *bend, *p, *name;
+
+	ret = -EINVAL;
+	if (rec->state != KEYFS_BIFILE_WANT_PARAMS || buflen > PAGE_SIZE)
+		goto error;
+
+	rec->state = KEYFS_BIFILE_GOT_RESULT;
+
+	/* fetch the data into kernel memory */
+	ret = -ENOMEM;
+	buffer = kmalloc(buflen + 1, GFP_KERNEL);
+	if (!buffer)
+		goto error;
+
+	ret = -EFAULT;
+	if (copy_from_user(buffer, _buffer, buflen) != 0)
+		goto error2;
+	bend = buffer + buflen;
+	*bend = 0;
+
+	/* parse the parameter string */
+	ret = -EINVAL;
+
+	p = memchr(buffer, '\n', bend - buffer);
+	if (p) {
+		*p++ = 0;
+		if (p != bend)
+			goto error2;
+	}
+
+	name = buffer;
+	if (!*buffer)
+		name = NULL; /* anonymous session */
+
+	/* attempt to join the named session */
+	jret = join_session_keyring(name);
+	if (jret < 0) {
+		ret = jret;
+		goto error2;
+	}
+
+	/* we'll be returning the key ID on the next read */
+	rec->id = jret;
+	ret = buflen;
+
+ error2:
+	kfree(buffer);
+ error:
+	if (ret < 0)
+		rec->error = ret;
+	return ret;
+
+} /* end keyfs_join_session_write() */
diff -puN /dev/null fs/keyfs/rootlink.c
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/fs/keyfs/rootlink.c	Wed Aug 18 17:27:20 2004
@@ -0,0 +1,180 @@
+/* rootlink.c: special process/user keyring symlink operations
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/err.h>
+#include <linux/key.h>
+#include <asm/uaccess.h>
+#include "keyfs.h"
+
+static struct file_operations keyfs_rootlink_file_operations = {
+};
+
+static int keyfs_rootlink_readlink(struct dentry *, char __user *, int);
+static int keyfs_rootlink_follow_link(struct dentry *, struct nameidata *);
+
+static struct inode_operations keyfs_rootlink_inode_operations = {
+	.follow_link	= keyfs_rootlink_follow_link,
+	.readlink	= keyfs_rootlink_readlink,
+};
+
+/*****************************************************************************/
+/*
+ * get a special process keyring symlink inode
+ */
+struct inode *keyfs_get_rootlink(struct super_block *super, ino_t ino)
+{
+	struct inode *inode;
+
+	/* get the inode */
+	inode = iget_locked(super, ino);
+	if (inode && inode->i_state & I_NEW) {
+		/* initialise it */
+		inode->i_mtime	= inode->i_atime = inode->i_ctime =
+			CURRENT_TIME;
+		inode->i_blocks	= 0;
+		inode->i_blksize = 1024;
+		inode->i_uid	= 0;
+		inode->i_gid	= 0;
+		inode->i_mode	= S_IFLNK | S_IRUGO | S_IXUGO;
+		inode->i_op	= &keyfs_rootlink_inode_operations;
+		inode->i_fop	= &keyfs_rootlink_file_operations;
+		inode->i_nlink	= 2;
+
+		/* success */
+		unlock_new_inode(inode);
+	}
+
+	return inode;
+
+} /* end keyfs_get_rootlink() */
+
+/*****************************************************************************/
+/*
+ * read the symlink
+ */
+static int keyfs_rootlink_readlink(struct dentry *dentry,
+				   char __user *_buffer,
+				   int buflen)
+{
+	key_serial_t id;
+	char buffer[12];
+	int n;
+
+	id = 0;
+
+	/* the symlink targets the appropriate keyring directory */
+	switch (dentry->d_inode->i_ino) {
+	case KEYFS_INO_THREAD:
+		if (current->thread_keyring)
+			id = current->thread_keyring->serial;
+		break;
+
+	case KEYFS_INO_PROCESS:
+		if (current->process_keyring)
+			id = current->process_keyring->serial;
+		break;
+
+	case KEYFS_INO_SESSION:
+		if (current->session_keyring)
+			id = current->session_keyring->serial;
+		break;
+
+	case KEYFS_INO_USER_SESSION:
+		if (current->user->uid_keyring)
+			id = current->user->uid_keyring->serial;
+		break;
+
+	case KEYFS_INO_USER:
+		if (current->user->session_keyring)
+			id = current->user->session_keyring->serial;
+		break;
+
+	default:
+		BUG();
+	}
+
+	/* render the keyring ID as a string and write to userspace */
+	n = sprintf(buffer, "%d", id);
+	if (buflen > n)
+		buflen = n;
+
+	if (copy_to_user(_buffer, buffer, buflen) != 0)
+		buflen = -EFAULT;
+
+	return buflen;
+
+} /* end keyfs_rootlink_readlink() */
+
+/*****************************************************************************/
+/*
+ * follow the symlink to the appropriate directory
+ */
+static int keyfs_rootlink_follow_link(struct dentry *dentry,
+				      struct nameidata *nd)
+{
+	key_serial_t id;
+	char buffer[13];
+	int ret;
+
+	id = 0;
+	ret = -ENOENT;
+
+	/* target the symlink to the appropriate keyring directory */
+	switch (dentry->d_inode->i_ino) {
+	case KEYFS_INO_THREAD:
+		if (!current->thread_keyring)
+			goto no_keyring;
+		id = current->thread_keyring->serial;
+		break;
+
+	case KEYFS_INO_PROCESS:
+		if (!current->process_keyring)
+			goto no_keyring;
+		id = current->process_keyring->serial;
+		break;
+
+	case KEYFS_INO_SESSION:
+		if (!current->session_keyring)
+			goto no_keyring;
+		id = current->session_keyring->serial;
+		break;
+
+	case KEYFS_INO_USER_SESSION:
+		if (!current->user->uid_keyring)
+			goto no_keyring;
+		id = current->user->uid_keyring->serial;
+		break;
+
+	case KEYFS_INO_USER:
+		if (!current->user->session_keyring)
+			goto no_keyring;
+		id = current->user->session_keyring->serial;
+		break;
+
+	default:
+		BUG();
+	}
+
+	/* render the keyring ID as a string and pass back to pathwalk */
+	sprintf(buffer, "%d", id);
+	nd_set_link(nd, buffer);
+	ret = 0;
+
+ no_keyring:
+	return ret;
+
+} /* end keyfs_rootlink_follow_link() */
diff -puN /dev/null fs/keyfs/super.c
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/fs/keyfs/super.c	Wed Aug 18 17:27:20 2004
@@ -0,0 +1,117 @@
+/* super.c: key management filesystme
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/key.h>
+#include <asm/uaccess.h>
+#include "keyfs.h"
+
+#define KEYFS_FS_MAGIC	0x4B455953 /* 'KEYS' */
+
+static struct super_block *keyfs_get_sb(struct file_system_type *fs_type,
+					int flags, const char *dev_name,
+					void *data);
+
+static int keyfs_fill_super(struct super_block *s, void *data, int silent);
+
+static struct file_system_type keyfs_fs_type = {
+	.owner		= THIS_MODULE,
+	.name		= "keyfs",
+	.get_sb		= keyfs_get_sb,
+	.kill_sb	= kill_anon_super,
+};
+
+static void keyfs_clear_inode(struct inode *vfs_inode);
+
+static struct super_operations keyfs_super_ops = {
+	.statfs		= simple_statfs,
+	.clear_inode	= keyfs_clear_inode,
+};
+
+
+/*****************************************************************************/
+/*
+ * initialise the filesystem
+ */
+static int __init keyfs_fs_init(void)
+{
+	/* export our filesystem to lesser mortals */
+	return register_filesystem(&keyfs_fs_type);
+
+} /* end keyfs_fs_init() */
+
+fs_initcall(keyfs_fs_init);
+
+/*****************************************************************************/
+/*
+ * create the keyfs superblock (one time only)
+ */
+static struct super_block *keyfs_get_sb(struct file_system_type *fs_type,
+					int flags, const char *dev_name,
+					void *data)
+{
+	return get_sb_single(&keyfs_fs_type, flags, data, keyfs_fill_super);
+
+} /* end keyfs_get_sb() */
+
+/*****************************************************************************/
+/*
+ * fill out the superblock
+ */
+static int keyfs_fill_super(struct super_block *s, void *data, int silent)
+{
+	struct inode *inode;
+	int ret;
+
+	s->s_blocksize = 1024;
+	s->s_blocksize_bits = 10;
+	s->s_magic = KEYFS_FS_MAGIC;
+	s->s_op = &keyfs_super_ops;
+
+	/* allocate the root inode */
+	ret = -ENOMEM;
+	inode = keyfs_get_rootdir(s);
+
+	if (!IS_ERR(inode)) {
+		/* allocate a root dentry */
+		s->s_root = d_alloc_root(inode);
+		if (s->s_root) {
+			ret = 0;
+		}
+		else {
+			printk("keyfs: get root dentry failed\n");
+			iput(inode);
+		}
+	}
+
+
+	return ret;
+
+} /* end keyfs_fill_super() */
+
+/*****************************************************************************/
+/*
+ * clear an inode
+ */
+static void keyfs_clear_inode(struct inode *inode)
+{
+	struct key *key = inode->u.generic_ip;
+
+	inode->u.generic_ip = NULL;
+
+	key_put(key);
+
+} /* end keyfs_clear_inode() */
diff -puN fs/Makefile~keys-keyring-management-keyfs-patch fs/Makefile
--- 25/fs/Makefile~keys-keyring-management-keyfs-patch	Wed Aug 18 17:27:19 2004
+++ 25-akpm/fs/Makefile	Wed Aug 18 17:27:20 2004
@@ -93,3 +93,4 @@ obj-$(CONFIG_AFS_FS)		+= afs/
 obj-$(CONFIG_BEFS_FS)		+= befs/
 obj-$(CONFIG_HOSTFS)		+= hostfs/
 obj-$(CONFIG_HPPFS)		+= hppfs/
+obj-$(CONFIG_KEYFS)		+= keyfs/
_