All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCHv2 0/5] VFS: DazukoFS, stackable-fs, file access control
@ 2009-02-03 19:14 John Ogness
  2009-02-03 19:15 ` [PATCHv2 1/5] " John Ogness
  0 siblings, 1 reply; 24+ messages in thread
From: John Ogness @ 2009-02-03 19:14 UTC (permalink / raw)
  To: linux-kernel; +Cc: viro, malware-list, eparis, hch, alan

Hello,

This is the second submission of DazukoFS. As suggested by Pavel
Machek, I have gone through and cleaned up the coding style, added
missing includes, and worked to improve readability. This patchset is
against Linux 2.6.29-rc3. Following is the introduction text from the
original submission:


This patchset introduces a new stackable filesystem: DazukoFS.


SUMMARY

DazukoFS is a stackable filesystem that provides functionality
allowing userspace applications to perform online file access
control. When a file is accessed on DazukoFS, registered userspace
processes will be notified and given the opportunity to allow or deny
the access. To help a registered process make its decision, it
receives a read-only open file descriptor to the file being
accessed. DazukoFS uses named grouping to determine if multiple
registered processes are working together.

The features of DazukoFS can be briefly summarized as follows:

1. Parallel Event Handling
   Multiple processes (or threads) may register using the same group
   name to signify that they are working together. When a file on
   DazukoFS is accessed, a file access event will be assigned to the
   first available process in the group. If another file access event
   occurs before the first has been handled, the next available
   process in the group will be assigned the event. If no processes in
   the group are currently available, that particular file access will
   block. This allows multiple files to be processed simultaneously.

2. Multiple Groups
   Different applications may register, each using their own group
   name. Each group will be given the opportunity to determine if
   access may be allowed. Access is denied if any group decides to
   deny access. This allows different access control applications to
   run simultaneously and independent from one another.

3. Device-Determined Privileges
   Each group uses its own dedicated device. When performing file
   access control, an application only needs read/write access to its
   group device. The application does not require any other
   privileges. Since DazukoFS already opens the file being accessed
   (read-only), a non-privileged registered application is able to
   perform online file access control for all files (including those
   that it would normally not have access to).


MOTIVATION

Since 2001 various anti-virus vendors have been providing out-of-tree
solutions for online virus scanning. Although GNU/Linux systems
currently are not targets of virus authors, many organizations are
interested in online virus scanning on Linux-based servers in order to
help protect Microsoft Windows clients. It is often argued that file
scanning should be implemented in the various services (such as Samba,
Apache, vsftpd, etc.), and indeed many such solutions have been
implemented. However, there is a continued demand for a kernel-based
solution because it can guard the entire filesystem independent from
the types and numbers of services running on a system.

In 2002, the Dazuko project was started with the goal of providing a
userspace interface on top of a kernel-based file access control
mechanism. From early on, the project has aimed to support multiple
anti-virus applications simultaneously without any bias towards a
particular application. Today several anti-virus vendors choose Dazuko
as a basis for their online file scanning solution.

Dazuko originally used syscall-table hooking for its access control
mechanism. With the arrival of Linux 2.6, Dazuko switched to using
LSM. However, in February 2004, Christoph Hellwig suggested that file
access events should be intercepted using a stackable
filesystem. After many discussions and several prototypes, it was
decided that the Dazuko project would move to become a stackable
filesystem: DazukoFS.

Implementing online file access control using a stackable filesystem
has several benefits:

1. No core Linux code must be modified. As a stackable filesystem,
   DazukoFS is a separate component of the kernel. This could prove
   very useful if/when anti-virus vendors want to develop new DazukoFS
   features. With DazukoFS as a separate component, new features could
   be added without requiring core Linux code changes.

2. On LKML, an interest in a stackable filesystem framework has been
   expressed. With DazukoFS being another stackable filesystem, we can
   begin looking at common code between the stackable filesystems and
   possibly begin identifying a stackable filesystem framework. Using
   real stackable filesystems as a model is much easier than guessing
   what should go into a stackable filesystem framework.

The Dazuko project is interested in going mainline with
DazukoFS. Nearly seven years of out-of-tree development were more than
enough to prove that out-of-tree kernel drivers have an unnecessarily
large maintenance cost (which increases with each new kernel
release). With DazukoFS mainline, anti-virus vendors would finally
have an official interface and implementation on which to base their
online scanning applications.


CURRENT STATE

DazukoFS currently includes a minimal set of features. This was done
on purpose to try to minimize the amount of code for the initial
presentation for mainline acceptance. Here is a brief listing of the
current state of the project:

- Only file access events for "open" file operations are intercepted.

- Writing to memory mapped files is not supported.

- Many exotic VFS calls are not stacked upon.

- A bug was recently reported at Red Hat regarding problems using
  SElinux and stackable filesystems. This affects DazukoFS as well as
  other stackable filesystems.
  https://bugzilla.redhat.com/show_bug.cgi?id=475985

- DazukoFS has been tested primarily on top of ext3 (read/write) for
  the powerpc architecture.


A WORD ON FANOTIFY

I am aware of the current work of Eric Paris to implement a file
access control mechanism within a unified inotify/dnotify
framework. Although I welcome any official interface to provide a file
access control mechanism for userspace applications in Linux, I feel
that DazukoFS provides a more elegant solution. (Note that the two
projects do not conflict with each other.) I am open to discuss this
point further if anyone is interested.


SUBMISSION PATCHSET

A series of 5 patches will be posted. Each patch provides new features
and working code so that DazukoFS can be evaluated with various
feature (patch) subsets. The patches can be briefly summarized as
follows:

Patch 1: Introduces DazukoFS as a nullfs. This is the raw stackable
         filesystem part of DazukoFS and does nothing except stack and
         pass filesystem calls to the lower filesystem.

Patch 2: Creates /dev/dazukofs.0 for userspace applications to perform
         file access control. At this point, all applications are
         considered to be working together (in the same group).

Patch 3: Creates /dev/dazukofs.ctrl to allow for groups to be added,
         listed, and deleted. Also, /dev/dazukofs.[0-9] devices are
         created to support multiple groups.

Patch 4: Adds a new (optional) command to allow registered processes
         to be tracked. The tracking allows processes to be
         automatically unregistered if they crash and also allows
         groups to be automatically deleted if no more processes are
         registered.

Patch 5: Creates /dev/dazukofs.ign as an (optional) mechanism for any
         processes to hide themselves from DazukoFS file access
         control.

Device permissions can be best set using udev rules. The file
"Documentation/filesystems/dazukofs.txt" (also updated with each
patch) describes how to use and write applications for DazukoFS.

John Ogness

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCHv2 1/5] VFS: DazukoFS, stackable-fs, file access control
  2009-02-03 19:14 [PATCHv2 0/5] VFS: DazukoFS, stackable-fs, file access control John Ogness
@ 2009-02-03 19:15 ` John Ogness
  2009-02-03 19:17   ` [PATCHv2 2/5] " John Ogness
  2009-02-12 15:27   ` [PATCHv2 1/5] " Jan Engelhardt
  0 siblings, 2 replies; 24+ messages in thread
From: John Ogness @ 2009-02-03 19:15 UTC (permalink / raw)
  To: linux-kernel; +Cc: viro, malware-list, eparis, hch, alan

Patch 1: Introduces DazukoFS as a nullfs. This is the raw stackable
         filesystem part of DazukoFS and does nothing except stack and
         pass filesystem calls to the lower filesystem.

Patched against 2.6.29-rc3.

Signed-off-by: John Ogness <dazukocode@ogness.net>
---
 Documentation/filesystems/dazukofs.txt |   81 ++
 fs/Kconfig                             |    1 
 fs/Makefile                            |    1 
 fs/dazukofs/Kconfig                    |   10 
 fs/dazukofs/Makefile                   |    7 
 fs/dazukofs/dazukofs_fs.h              |  163 ++++
 fs/dazukofs/dentry.c                   |  163 ++++
 fs/dazukofs/file.c                     |  324 +++++++++
 fs/dazukofs/inode.c                    |  813 +++++++++++++++++++++++
 fs/dazukofs/mmap.c                     |  116 +++
 fs/dazukofs/super.c                    |  335 +++++++++
 11 files changed, 2014 insertions(+)
Index: linux-2.6.28/fs/dazukofs/dazukofs_fs.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.28/fs/dazukofs/dazukofs_fs.h	2009-02-03 07:41:04.000000000 +0100
@@ -0,0 +1,163 @@
+/* dazukofs: access control stackable filesystem
+
+   Copyright (C) 1997-2004 Erez Zadok
+   Copyright (C) 2001-2004 Stony Brook University
+   Copyright (C) 2004-2007 International Business Machines Corp.
+   Copyright (C) 2008-2009 John Ogness
+     Author: John Ogness <dazukocode@ogness.net>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*/
+
+#ifndef __DAZUKOFS_FS_H
+#define __DAZUKOFS_FS_H
+
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/dcache.h>
+#include <linux/mount.h>
+
+extern struct kmem_cache *dazukofs_dentry_info_cachep;
+extern struct kmem_cache *dazukofs_file_info_cachep;
+extern const struct file_operations dazukofs_main_fops;
+extern const struct file_operations dazukofs_dir_fops;
+extern struct dentry_operations dazukofs_dops;
+extern const struct address_space_operations dazukofs_aops;
+
+extern int dazukofs_interpose(struct dentry *lower_dentry,
+			      struct dentry *dentry, struct super_block *sb,
+			      int already_hashed);
+
+struct dazukofs_sb_info {
+	struct super_block *lower_sb;
+};
+
+struct dazukofs_inode_info {
+	struct inode *lower_inode;
+
+	/*
+	 * the inode (embedded)
+	 */
+	struct inode vfs_inode;
+};
+
+struct dazukofs_dentry_info {
+	struct dentry *lower_dentry;
+	struct vfsmount *lower_mnt;
+};
+
+struct dazukofs_file_info {
+	struct file *lower_file;
+};
+
+static inline struct dazukofs_sb_info *get_sb_private(
+						struct super_block *upper_sb)
+{
+	return upper_sb->s_fs_info;
+}
+
+static inline void set_sb_private(struct super_block *upper_sb,
+				  struct dazukofs_sb_info *sbi)
+{
+	upper_sb->s_fs_info = sbi;
+}
+
+static inline struct super_block *get_lower_sb(struct super_block *upper_sb)
+{
+	return get_sb_private(upper_sb)->lower_sb;
+}
+
+static inline void set_lower_sb(struct super_block *upper_sb,
+				struct super_block *lower_sb)
+{
+	struct dazukofs_sb_info *sbi = get_sb_private(upper_sb);
+	sbi->lower_sb = lower_sb;
+}
+
+static inline struct dazukofs_inode_info *get_inode_private(
+						struct inode *upper_inode)
+{
+	return container_of(upper_inode, struct dazukofs_inode_info,
+			    vfs_inode);
+}
+
+static inline struct inode *get_lower_inode(struct inode *upper_inode)
+{
+	return get_inode_private(upper_inode)->lower_inode;
+}
+
+static inline void set_lower_inode(struct inode *upper_inode,
+				   struct inode *lower_inode)
+{
+	struct dazukofs_inode_info *dii = get_inode_private(upper_inode);
+	dii->lower_inode = lower_inode;
+}
+
+static inline struct dazukofs_dentry_info *get_dentry_private(
+						struct dentry *upper_dentry)
+{
+	return upper_dentry->d_fsdata;
+}
+
+static inline void set_dentry_private(struct dentry *upper_dentry,
+				      struct dazukofs_dentry_info *dentryi)
+{
+	upper_dentry->d_fsdata = dentryi;
+}
+
+static inline struct dentry *get_lower_dentry(struct dentry *upper_dentry)
+{
+	return get_dentry_private(upper_dentry)->lower_dentry;
+}
+
+static inline struct vfsmount *get_lower_mnt(struct dentry *upper_dentry)
+{
+	return get_dentry_private(upper_dentry)->lower_mnt;
+}
+
+static inline void set_lower_dentry(struct dentry *upper_dentry,
+				    struct dentry *lower_dentry,
+				    struct vfsmount *lower_mnt)
+{
+	struct dazukofs_dentry_info *dii = get_dentry_private(upper_dentry);
+	dii->lower_dentry = lower_dentry;
+	dii->lower_mnt = lower_mnt;
+}
+
+static inline struct dazukofs_file_info *get_file_private(
+						struct file *upper_file)
+{
+	return upper_file->private_data;
+}
+
+static inline void set_file_private(struct file *upper_file,
+				    struct dazukofs_file_info *filei)
+{
+	upper_file->private_data = filei;
+}
+
+static inline struct file *get_lower_file(struct file *upper_file)
+{
+	return get_file_private(upper_file)->lower_file;
+}
+
+static inline void set_lower_file(struct file *upper_file,
+				  struct file *lower_file)
+{
+	struct dazukofs_file_info *dfi = get_file_private(upper_file);
+	dfi->lower_file = lower_file;
+}
+
+#endif  /* __DAZUKOFS_FS_H */
Index: linux-2.6.28/fs/dazukofs/dentry.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.28/fs/dazukofs/dentry.c	2009-02-02 22:47:28.000000000 +0100
@@ -0,0 +1,163 @@
+/* dazukofs: access control stackable filesystem
+
+   Copyright (C) 1997-2003 Erez Zadok
+   Copyright (C) 2001-2003 Stony Brook University
+   Copyright (C) 2004-2006 International Business Machines Corp.
+   Copyright (C) 2008-2009 John Ogness
+     Author: John Ogness <dazukocode@ogness.net>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+#include <linux/fs_stack.h>
+
+#include "dazukofs_fs.h"
+
+/**
+ * dazukofs_d_revalidate - revalidate a dentry found in the dcache
+ * @dentry: dentry to revalidate
+ * @nd: nameidata associated with dentry
+ *
+ * Description: Called when the VFS needs to revalidate a dentry. This is
+ * called whenever a name look-up finds a dentry in the dcache. Most
+ * filesystems leave this as NULL, because all their dentries in the dcache
+ * are valid.
+ *
+ * Call d_revalidate() on the lower dentry if available. The mnt/dentry
+ * (path) data in the nameidata needs to be temporarily swapped out for the
+ * lower call.
+ *
+ * After the call, the original path data is restored and the dentry's inode
+ * attributes are updated to match the lower inode.
+ *
+ * Returns 1 if dentry is valid, otherwise 0.
+ */
+static int dazukofs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
+{
+	struct vfsmount *lower_mnt = get_lower_mnt(dentry);
+	struct dentry *lower_dentry = get_lower_dentry(dentry);
+	struct vfsmount *vfsmount_save = nd->path.mnt;
+	struct dentry *dentry_save = nd->path.dentry;
+	int valid;
+
+	if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate)
+		return 1;
+
+	nd->path.mnt = mntget(lower_mnt);
+	nd->path.dentry = dget(lower_dentry);
+
+	valid = lower_dentry->d_op->d_revalidate(lower_dentry, nd);
+
+	mntput(lower_mnt);
+	dput(lower_dentry);
+
+	nd->path.mnt = vfsmount_save;
+	nd->path.dentry = dentry_save;
+
+	/* update the inode, even if d_revalidate() != 1 */
+	if (dentry->d_inode) {
+		struct inode *lower_inode = get_lower_inode(dentry->d_inode);
+		fsstack_copy_attr_all(dentry->d_inode, lower_inode, NULL);
+	}
+	return valid;
+}
+
+/**
+ * dazukofs_d_hash - hash the given name
+ * @dentry: the parent dentry
+ * @name: the name to hash
+ *
+ * Description: Called when the VFS adds a dentry to the hash table.
+ *
+ * Call d_hash() on the lower dentry if available. Otherwise dazukofs
+ * does nothing. This is ok because the VFS will compute a default
+ * hash.
+ *
+ * Returns 0 on success.
+ */
+static int dazukofs_d_hash(struct dentry *dentry, struct qstr *name)
+{
+	struct dentry *lower_dentry = get_lower_dentry(dentry);
+
+	if (!lower_dentry->d_op || !lower_dentry->d_op->d_hash)
+		return 0;
+
+	return lower_dentry->d_op->d_hash(lower_dentry, name);
+}
+
+/**
+ * dazukofs_d_release - clean up dentry
+ * @dentry: the dentry that will be released
+ *
+ * Description: Called when a dentry is really deallocated.
+ *
+ * Release our hold on the lower dentry and mnt. Then free the structure
+ * (from the cache) containing the lower data for this dentry.
+ */
+static void dazukofs_d_release(struct dentry *dentry)
+{
+	if (get_dentry_private(dentry)) {
+		dput(get_lower_dentry(dentry));
+		mntput(get_lower_mnt(dentry));
+
+		kmem_cache_free(dazukofs_dentry_info_cachep,
+				get_dentry_private(dentry));
+	}
+}
+
+/**
+ * dazukofs_d_compare - used to compare dentry's
+ * @dentry: the parent dentry
+ * @a: qstr of an existing dentry
+ * @b: qstr of a second dentry (dentry may not be valid)
+ *
+ * Description: Called when a dentry should be compared with another.
+ *
+ * Call d_compare() on the lower dentry if available. Otherwise, perform
+ * some basic comparisons between the two qstr's.
+ *
+ * Returns 0 if they are the same, otherwise 1.
+ */
+static int dazukofs_d_compare(struct dentry *dentry, struct qstr *a,
+			    struct qstr *b)
+{
+	struct dentry *lower_dentry = get_lower_dentry(dentry);
+
+	if (lower_dentry->d_op && lower_dentry->d_op->d_compare)
+		return lower_dentry->d_op->d_compare(lower_dentry, a, b);
+
+	if (a->len != b->len)
+		return 1;
+	if (memcmp(a->name, b->name, a->len))
+		return 1;
+	return 0;
+}
+
+/**
+ * Unused operations:
+ *   - d_delete
+ *   - d_iput
+ *   - d_dname
+ */
+struct dentry_operations dazukofs_dops = {
+	.d_revalidate	= dazukofs_d_revalidate,
+	.d_hash		= dazukofs_d_hash,
+	.d_release	= dazukofs_d_release,
+	.d_compare	= dazukofs_d_compare,
+};
Index: linux-2.6.28/fs/dazukofs/super.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.28/fs/dazukofs/super.c	2009-02-03 18:07:52.000000000 +0100
@@ -0,0 +1,335 @@
+/* dazukofs: access control stackable filesystem
+
+   Copyright (C) 1997-2003 Erez Zadok
+   Copyright (C) 2001-2003 Stony Brook University
+   Copyright (C) 2004-2006 International Business Machines Corp.
+   Copyright (C) 2008-2009 John Ogness
+     Author: John Ogness <dazukocode@ogness.net>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+
+#include "dazukofs_fs.h"
+
+static struct kmem_cache *dazukofs_inode_info_cachep;
+static struct kmem_cache *dazukofs_sb_info_cachep;
+struct kmem_cache *dazukofs_dentry_info_cachep;
+struct kmem_cache *dazukofs_file_info_cachep;
+
+static struct inode *dazukofs_alloc_inode(struct super_block *sb)
+{
+	struct dazukofs_inode_info *inodei =
+		kmem_cache_alloc(dazukofs_inode_info_cachep, GFP_KERNEL);
+	if (!inodei)
+		return NULL;
+
+	/*
+	 * The inode is embedded within the dazukofs_inode_info struct.
+	 */
+	return &(inodei->vfs_inode);
+}
+
+static void dazukofs_destroy_inode(struct inode *inode)
+{
+	/*
+	 * The inode is embedded within the dazukofs_inode_info struct.
+	 */
+	kmem_cache_free(dazukofs_inode_info_cachep,
+			get_inode_private(inode));
+}
+
+static int dazukofs_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+	return vfs_statfs(get_lower_dentry(dentry), buf);
+}
+
+static void dazukofs_clear_inode(struct inode *inode)
+{
+	iput(get_lower_inode(inode));
+}
+
+static void dazukofs_put_super(struct super_block *sb)
+{
+	struct dazukofs_sb_info *sbi = get_sb_private(sb);
+	if (sbi)
+		kmem_cache_free(dazukofs_sb_info_cachep, sbi);
+}
+
+/**
+ * Unused operations:
+ *   - dirty_inode
+ *   - write_inode
+ *   - put_inode
+ *   - drop_inode
+ *   - delete_inode
+ *   - write_super
+ *   - sync_fs
+ *   - write_super_lockfs
+ *   - unlockfs
+ *   - remount_fs
+ *   - umount_begin
+ *   - show_options
+ *   - show_stats
+ *   - quota_read
+ *   - quota_write
+ */
+static struct super_operations dazukofs_sops = {
+	.alloc_inode	= dazukofs_alloc_inode,
+	.destroy_inode	= dazukofs_destroy_inode,
+	.put_super	= dazukofs_put_super,
+	.statfs		= dazukofs_statfs,
+	.clear_inode	= dazukofs_clear_inode,
+};
+
+static int dazukofs_parse_mount_options(char *options, struct super_block *sb)
+{
+	return 0;
+}
+
+static int dazukofs_fill_super(struct super_block *sb, void *data, int silent)
+{
+	struct dazukofs_sb_info *sbi;
+	struct dentry *root;
+	static const struct qstr name = { .name = "/", .len = 1 };
+	struct dazukofs_dentry_info *di;
+
+	sbi =  kmem_cache_zalloc(dazukofs_sb_info_cachep, GFP_KERNEL);
+	if (!sbi)
+		return -ENOMEM;
+
+	sb->s_op = &dazukofs_sops;
+
+	root = d_alloc(NULL, &name);
+	if (!root) {
+		kmem_cache_free(dazukofs_sb_info_cachep, sbi);
+		return -ENOMEM;
+	}
+
+	sb->s_root = root;
+
+	sb->s_root->d_op = &dazukofs_dops;
+	sb->s_root->d_sb = sb;
+	sb->s_root->d_parent = sb->s_root;
+
+	di = kmem_cache_zalloc(dazukofs_dentry_info_cachep, GFP_KERNEL);
+	if (!di) {
+		kmem_cache_free(dazukofs_sb_info_cachep, sbi);
+		dput(sb->s_root);
+		return -ENOMEM;
+	}
+
+	set_dentry_private(sb->s_root, di);
+
+	set_sb_private(sb, sbi);
+
+	return 0;
+}
+
+static int dazukofs_read_super(struct super_block *sb, const char *dev_name)
+{
+	struct nameidata nd;
+	struct dentry *lower_root;
+	struct vfsmount *lower_mnt;
+	int err;
+
+	memset(&nd, 0, sizeof(struct nameidata));
+	err = path_lookup(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &nd);
+	if (err)
+		return err;
+
+	lower_root = dget(nd.path.dentry);
+	lower_mnt = mntget(nd.path.mnt);
+
+	if (IS_ERR(lower_root)) {
+		err = PTR_ERR(lower_root);
+		goto out_put;
+	}
+
+	if (!lower_root->d_inode) {
+		err = -ENOENT;
+		goto out_put;
+	}
+
+	set_lower_sb(sb, lower_root->d_sb);
+	sb->s_maxbytes = lower_root->d_sb->s_maxbytes;
+	set_lower_dentry(sb->s_root, lower_root, lower_mnt);
+
+	err = dazukofs_interpose(lower_root, sb->s_root, sb, 0);
+	if (err)
+		goto out_put;
+	goto out;
+
+out_put:
+	dput(lower_root);
+	mntput(lower_mnt);
+out:
+	path_put(&nd.path);
+	return err;
+}
+
+static int dazukofs_get_sb(struct file_system_type *fs_type, int flags,
+			   const char *dev_name, void *data,
+			   struct vfsmount *mnt)
+{
+	struct super_block *sb;
+	int err;
+
+	err = get_sb_nodev(fs_type, flags, data, dazukofs_fill_super, mnt);
+	if (err)
+		goto out;
+
+	sb = mnt->mnt_sb;
+
+	err = dazukofs_parse_mount_options(data, sb);
+	if (err)
+		goto out_abort;
+
+	err = dazukofs_read_super(sb, dev_name);
+	if (err)
+		goto out_abort;
+
+	goto out;
+
+out_abort:
+	up_write(&sb->s_umount);
+	deactivate_super(sb);
+out:
+	return err;
+}
+
+static void init_once(void *data)
+{
+	struct dazukofs_inode_info *inode_info =
+		(struct dazukofs_inode_info *)data;
+
+	memset(inode_info, 0, sizeof(struct dazukofs_inode_info));
+	inode_init_once(&(inode_info->vfs_inode));
+}
+
+static void destroy_caches(void)
+{
+	if (dazukofs_inode_info_cachep) {
+		kmem_cache_destroy(dazukofs_inode_info_cachep);
+		dazukofs_inode_info_cachep = NULL;
+	}
+
+	if (dazukofs_sb_info_cachep) {
+		kmem_cache_destroy(dazukofs_sb_info_cachep);
+		dazukofs_sb_info_cachep = NULL;
+	}
+
+	if (dazukofs_dentry_info_cachep) {
+		kmem_cache_destroy(dazukofs_dentry_info_cachep);
+		dazukofs_dentry_info_cachep = NULL;
+	}
+
+	if (dazukofs_file_info_cachep) {
+		kmem_cache_destroy(dazukofs_file_info_cachep);
+		dazukofs_file_info_cachep = NULL;
+	}
+}
+
+static int init_caches(void)
+{
+	dazukofs_inode_info_cachep =
+		kmem_cache_create("dazukofs_inode_info_cache",
+				  sizeof(struct dazukofs_inode_info), 0,
+				  SLAB_HWCACHE_ALIGN,
+				  init_once);
+	if (!dazukofs_inode_info_cachep)
+		goto out_nomem;
+
+	dazukofs_sb_info_cachep =
+		kmem_cache_create("dazukofs_sb_info_cache",
+				  sizeof(struct dazukofs_sb_info), 0,
+				  SLAB_HWCACHE_ALIGN,
+				  NULL);
+	if (!dazukofs_sb_info_cachep)
+		goto out_nomem;
+
+	dazukofs_dentry_info_cachep =
+		kmem_cache_create("dazukofs_dentry_info_cache",
+				  sizeof(struct dazukofs_dentry_info), 0,
+				  SLAB_HWCACHE_ALIGN,
+				  NULL);
+	if (!dazukofs_dentry_info_cachep)
+		goto out_nomem;
+
+	dazukofs_file_info_cachep =
+		kmem_cache_create("dazukofs_file_info_cache",
+				  sizeof(struct dazukofs_file_info), 0,
+				  SLAB_HWCACHE_ALIGN,
+				  NULL);
+	if (!dazukofs_file_info_cachep)
+		goto out_nomem;
+
+	return 0;
+
+out_nomem:
+	destroy_caches();
+	return -ENOMEM;
+}
+
+static struct file_system_type dazukofs_fs_type = {
+	.owner		= THIS_MODULE,
+	.name		= "dazukofs",
+	.get_sb		= dazukofs_get_sb,
+	/*
+	 * XXX: We are using kill_anon_super() instead of my own function.
+	 *      Is this OK?
+	 */
+	.kill_sb	= kill_anon_super,
+	.fs_flags	= 0,
+};
+
+static int __init init_dazukofs_fs(void)
+{
+	int err;
+
+	err = init_caches();
+	if (err)
+		goto error_out1;
+
+	err = register_filesystem(&dazukofs_fs_type);
+	if (err)
+		goto error_out2;
+
+	printk(KERN_INFO "dazukofs: loaded\n");
+	return 0;
+
+error_out2:
+	destroy_caches();
+error_out1:
+	return err;
+}
+
+static void __exit exit_dazukofs_fs(void)
+{
+	unregister_filesystem(&dazukofs_fs_type);
+	destroy_caches();
+	printk(KERN_INFO "dazukofs: unloaded\n");
+}
+
+MODULE_AUTHOR("John Ogness");
+MODULE_DESCRIPTION("pass-through stackable filesystem");
+MODULE_LICENSE("GPL");
+module_init(init_dazukofs_fs)
+module_exit(exit_dazukofs_fs)
Index: linux-2.6.28/fs/dazukofs/file.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.28/fs/dazukofs/file.c	2009-02-03 18:08:42.000000000 +0100
@@ -0,0 +1,324 @@
+/* dazukofs: access control stackable filesystem
+
+   Copyright (C) 1997-2004 Erez Zadok
+   Copyright (C) 2001-2004 Stony Brook University
+   Copyright (C) 2004-2007 International Business Machines Corp.
+   Copyright (C) 2008-2009 John Ogness
+     Author: John Ogness <dazukocode@ogness.net>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/file.h>
+#include <linux/fs_stack.h>
+#include <linux/cred.h>
+
+#include "dazukofs_fs.h"
+
+/**
+ * Description: Called when the VFS needs to move the file position index.
+ */
+static loff_t dazukofs_llseek(struct file *file, loff_t offset, int origin)
+{
+	loff_t retval;
+	struct file *lower_file = get_lower_file(file);
+
+	lower_file->f_pos = file->f_pos;
+
+	memcpy(&(lower_file->f_ra), &(file->f_ra),
+	       sizeof(struct file_ra_state));
+
+	if (lower_file->f_op && lower_file->f_op->llseek)
+		retval = lower_file->f_op->llseek(lower_file, offset, origin);
+	else
+		retval = generic_file_llseek(lower_file, offset, origin);
+
+	if (retval >= 0) {
+		file->f_pos = lower_file->f_pos;
+		file->f_version = lower_file->f_version;
+	}
+	return retval;
+}
+
+/**
+ * Description: Called by read(2) and related system calls.
+ */
+static ssize_t dazukofs_read(struct file *file, char *buf, size_t count,
+			     loff_t *ppos)
+{
+	int err;
+	struct file *lower_file = get_lower_file(file);
+	loff_t pos_copy = *ppos;
+
+	if (!lower_file->f_op || !lower_file->f_op->read)
+		return -EINVAL;
+
+	err = lower_file->f_op->read(lower_file, buf, count, &pos_copy);
+
+	lower_file->f_pos = pos_copy;
+	*ppos = pos_copy;
+
+	if (err >= 0) {
+		fsstack_copy_attr_atime(file->f_dentry->d_inode,
+					lower_file->f_dentry->d_inode);
+	}
+
+	memcpy(&(file->f_ra), &(lower_file->f_ra),
+	       sizeof(struct file_ra_state));
+	return err;
+}
+
+/**
+ * Description: Called by write(2) and related system calls.
+ */
+static ssize_t dazukofs_write(struct file *file, const char *buf,
+			      size_t count, loff_t *ppos)
+{
+	int err;
+	struct file *lower_file = get_lower_file(file);
+	struct inode *inode = file->f_dentry->d_inode;
+	struct inode *lower_inode = get_lower_inode(inode);
+	loff_t pos_copy = *ppos;
+
+	if (!lower_file->f_op || !lower_file->f_op->write)
+		return -EINVAL;
+
+	err = lower_file->f_op->write(lower_file, buf, count, &pos_copy);
+
+	lower_file->f_pos = pos_copy;
+	*ppos = pos_copy;
+
+	if (err >= 0)
+		fsstack_copy_attr_atime(inode, lower_inode);
+
+	memcpy(&(file->f_ra), &(lower_file->f_ra),
+	       sizeof(struct file_ra_state));
+
+	mutex_lock(&inode->i_mutex);
+	i_size_write(inode, i_size_read(lower_inode));
+	mutex_unlock(&inode->i_mutex);
+	return err;
+}
+
+/**
+ * Description: Called when the VFS needs to read the directory contents.
+ */
+static int dazukofs_readdir(struct file *file, void *dirent, filldir_t filldir)
+{
+	int err;
+	struct file *lower_file = get_lower_file(file);
+	struct inode *inode = file->f_dentry->d_inode;
+
+	lower_file->f_pos = file->f_pos;
+
+	err = vfs_readdir(lower_file, filldir, dirent);
+
+	file->f_pos = lower_file->f_pos;
+
+	if (err >= 0)
+		fsstack_copy_attr_atime(inode, lower_file->f_dentry->d_inode);
+
+	return err;
+}
+
+/**
+ * Description: Called by the ioctl(2) system call.
+ */
+static int dazukofs_ioctl(struct inode *inode, struct file *file,
+			  unsigned int cmd, unsigned long arg)
+{
+	struct file *lower_file = get_lower_file(file);
+	struct inode *lower_inode = get_lower_inode(inode);
+
+	if (!lower_file->f_op || !lower_file->f_op->ioctl || !lower_inode)
+		return -ENOTTY;
+
+	return lower_file->f_op->ioctl(lower_inode, lower_file, cmd, arg);
+}
+
+/**
+ * Description: Called by the VFS when an inode should be opened. When the
+ * VFS opens a file, it creates a new "struct file". It then calls the open
+ * method for the newly allocated file structure. You might think that the
+ * open method really belongs in "struct inode_operations", and you may be
+ * right. I think it's done the way it is because it makes filesystems
+ * simpler to implement. The open() method is a good place to initialize
+ * the "private_data" member in the file structure if you want to point to
+ * a device structure.
+ */
+static int dazukofs_open(struct inode *inode, struct file *file)
+{
+	struct dentry *dentry = file->f_dentry;
+	struct dentry *lower_dentry = dget(get_lower_dentry(dentry));
+	struct vfsmount *lower_mnt = mntget(get_lower_mnt(dentry));
+	struct file *lower_file;
+	int err = 0;
+
+	set_file_private(file, kmem_cache_zalloc(dazukofs_file_info_cachep,
+						 GFP_KERNEL));
+	if (!get_file_private(file)) {
+		err = -ENOMEM;
+		goto error_out1;
+	}
+
+	lower_file = dentry_open(lower_dentry, lower_mnt, file->f_flags,
+				 current_cred());
+	if (IS_ERR(lower_file)) {
+		err = PTR_ERR(lower_file);
+		/* dentry_open() already did dput() and mntput() */
+		goto error_out2;
+	}
+
+	set_lower_file(file, lower_file);
+
+	return err;
+
+error_out1:
+	dput(lower_dentry);
+	mntput(lower_mnt);
+error_out2:
+	return err;
+}
+
+/**
+ * Description: Called by the close(2) system call to flush a file.
+ */
+static int dazukofs_flush(struct file *file, fl_owner_t td)
+{
+	struct file *lower_file = get_lower_file(file);
+
+	if (!lower_file->f_op || !lower_file->f_op->flush)
+		return 0;
+
+	return lower_file->f_op->flush(lower_file, td);
+}
+
+/**
+ * Description: Called when the last reference to an open file is closed.
+ */
+static int dazukofs_release(struct inode *inode, struct file *file)
+{
+	struct inode *lower_inode = get_lower_inode(inode);
+
+	fput(get_lower_file(file));
+	inode->i_blocks = lower_inode->i_blocks;
+
+	kmem_cache_free(dazukofs_file_info_cachep, get_file_private(file));
+	return 0;
+}
+
+/**
+ * Description: Called by the fsync(2) system call.
+ */
+static int dazukofs_fsync(struct file *file, struct dentry *dentry,
+			  int datasync)
+{
+	struct file *lower_file = get_lower_file(file);
+	struct dentry *lower_dentry = get_lower_dentry(dentry);
+
+	if (!lower_file->f_op || !lower_file->f_op->fsync)
+		return -EINVAL;
+
+	return lower_file->f_op->fsync(lower_file, lower_dentry, datasync);
+}
+
+/**
+ * Description: .called by the fcntl(2) system call when asynchronous
+ * (non-blocking) mode is enabled for a file.
+ */
+static int dazukofs_fasync(int fd, struct file *file, int flag)
+{
+	struct file *lower_file = get_lower_file(file);
+
+	if (!lower_file->f_op || !lower_file->f_op->fasync)
+		return 0;
+
+	return lower_file->f_op->fasync(fd, lower_file, flag);
+}
+
+/**
+ * Unused operations:
+ *   - owner
+ *   - aio_read (generic)
+ *   - aio_write (generic)
+ *   - poll
+ *   - unlocked_ioctl
+ *   - compat_ioctl
+ *   - mmap (generic)
+ *   - aio_fsync
+ *   - lock
+ *   - sendpage
+ *   - get_unmapped_area
+ *   - check_flags
+ *   - dir_notify
+ *   - flock
+ *   - splice_write
+ *   - splice_read (generic)
+ *   - setlease
+ */
+const struct file_operations dazukofs_main_fops = {
+	.llseek		= dazukofs_llseek,
+	.read		= dazukofs_read,
+	.aio_read	= generic_file_aio_read,
+	.write		= dazukofs_write,
+	.aio_write	= generic_file_aio_write,
+	.readdir	= dazukofs_readdir,
+	.ioctl		= dazukofs_ioctl,
+	.mmap		= generic_file_mmap,
+	.open		= dazukofs_open,
+	.flush		= dazukofs_flush,
+	.release	= dazukofs_release,
+	.fsync		= dazukofs_fsync,
+	.fasync		= dazukofs_fasync,
+	.splice_read	= generic_file_splice_read,
+};
+
+/**
+ * Unused operations:
+ *   - owner
+ *   - llseek
+ *   - read
+ *   - write
+ *   - aio_read
+ *   - aio_write
+ *   - poll
+ *   - unlocked_ioctl
+ *   - compat_ioctl
+ *   - mmap (generic)
+ *   - aio_fsync
+ *   - lock
+ *   - sendpage
+ *   - get_unmapped_area
+ *   - check_flags
+ *   - dir_notify
+ *   - flock
+ *   - splice_write
+ *   - splice_read (generic)
+ *   - setlease
+ */
+const struct file_operations dazukofs_dir_fops = {
+	.readdir	= dazukofs_readdir,
+	.ioctl		= dazukofs_ioctl,
+	.mmap		= generic_file_mmap,
+	.open		= dazukofs_open,
+	.flush		= dazukofs_flush,
+	.release	= dazukofs_release,
+	.fsync		= dazukofs_fsync,
+	.fasync		= dazukofs_fasync,
+	.splice_read	= generic_file_splice_read,
+};
Index: linux-2.6.28/fs/dazukofs/inode.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.28/fs/dazukofs/inode.c	2009-02-03 18:10:38.000000000 +0100
@@ -0,0 +1,813 @@
+/* dazukofs: access control stackable filesystem
+
+   Copyright (C) 1997-2004 Erez Zadok
+   Copyright (C) 2001-2004 Stony Brook University
+   Copyright (C) 2004-2007 International Business Machines Corp.
+   Copyright (C) 2008-2009 John Ogness
+     Author: John Ogness <dazukocode@ogness.net>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+#include <linux/uaccess.h>
+#include <linux/fs_stack.h>
+
+#include "dazukofs_fs.h"
+
+static struct inode_operations dazukofs_symlink_iops;
+static struct inode_operations dazukofs_dir_iops;
+static struct inode_operations dazukofs_main_iops;
+
+static int dazukofs_inode_test(struct inode *inode,
+			       void *candidate_lower_inode)
+{
+	if (get_lower_inode(inode) ==
+	    (struct inode *)candidate_lower_inode) {
+		return 1;
+	}
+
+	return 0;
+}
+
+static void dazukofs_init_inode(struct inode *inode, struct inode *lower_inode)
+{
+	set_lower_inode(inode, lower_inode);
+	inode->i_ino = lower_inode->i_ino;
+	inode->i_version++;
+	inode->i_op = &dazukofs_main_iops;
+	inode->i_fop = &dazukofs_main_fops;
+	inode->i_mapping->a_ops = &dazukofs_aops;
+}
+
+static int dazukofs_inode_set(struct inode *inode, void *lower_inode)
+{
+	dazukofs_init_inode(inode, (struct inode *)lower_inode);
+	return 0;
+}
+
+/**
+ * dazukofs_interpose - fill in new dentry, linking it to the lower dentry
+ * @lower_dentry: the corresponding lower dentry
+ * @denty: the new DazukoFS dentry
+ * @sb: super block of DazukoFS
+ * @already_hashed: flag to signify if "dentry" is already hashed
+ *
+ * Description: This is the key function which sets up all the hooks to
+ *              give DazukoFS control.
+ *
+ * Returns 0 on success.
+ */
+int dazukofs_interpose(struct dentry *lower_dentry, struct dentry *dentry,
+		       struct super_block *sb, int already_hashed)
+{
+	struct inode *inode;
+	struct inode *lower_inode = igrab(lower_dentry->d_inode);
+
+	if (!lower_inode)
+		return -ESTALE;
+
+	if (lower_inode->i_sb != get_lower_sb(sb)) {
+		iput(lower_inode);
+		return -EXDEV;
+	}
+
+	inode = iget5_locked(sb, (unsigned long)lower_inode,
+			     dazukofs_inode_test, dazukofs_inode_set,
+			     lower_inode);
+
+	if (!inode) {
+		iput(lower_inode);
+		return -EACCES;
+	}
+
+	if (inode->i_state & I_NEW) {
+		unlock_new_inode(inode);
+		/*
+		 * This is a new node so we leave the lower_node "in use"
+		 * and do not call iput().
+		 */
+	} else {
+		/*
+		 * This is not a new node so we decrement the usage count.
+		 */
+		iput(lower_inode);
+	}
+
+	if (S_ISLNK(lower_inode->i_mode))
+		inode->i_op = &dazukofs_symlink_iops;
+	else if (S_ISDIR(lower_inode->i_mode))
+		inode->i_op = &dazukofs_dir_iops;
+
+	if (S_ISDIR(lower_inode->i_mode))
+		inode->i_fop = &dazukofs_dir_fops;
+
+	if (special_file(lower_inode->i_mode)) {
+		init_special_inode(inode, lower_inode->i_mode,
+				   lower_inode->i_rdev);
+	}
+
+	dentry->d_op = &dazukofs_dops;
+
+	if (already_hashed)
+		d_add(dentry, inode);
+	else
+		d_instantiate(dentry, inode);
+
+	fsstack_copy_attr_all(inode, lower_inode, NULL);
+	fsstack_copy_inode_size(inode, lower_inode);
+	return 0;
+}
+
+/**
+ * Description: Called when the VFS needs to look up an inode in a parent
+ * directory. The name to look for is found in the dentry. This method
+ * must call d_add() to insert the found inode into the dentry. The
+ * "i_count" field in the inode structure should be incremented. If the
+ * named inode does not exist a NULL inode should be inserted into the
+ * dentry (this is called a negative dentry). Returning an error code
+ * from this routine must only be done on a real error, otherwise
+ * creating inodes with system calls like create(2), mknod(2), mkdir(2)
+ * and so on will fail. If you wish to overload the dentry methods then
+ * you should initialise the "d_dop" field in the dentry; this is a
+ * pointer to a struct "dentry_operations". This method is called with
+ * the directory inode semaphore held.
+ */
+static struct dentry *dazukofs_lookup(struct inode *dir, struct dentry *dentry,
+				      struct nameidata *nd)
+{
+	struct dentry *lower_dentry;
+	struct dentry *lower_dentry_parent;
+	struct vfsmount *lower_mnt;
+	int err = 0;
+
+	if ((dentry->d_name.len == 1 && !strcmp(dentry->d_name.name, "."))
+	    || (dentry->d_name.len == 2 &&
+		!strcmp(dentry->d_name.name, ".."))) {
+		d_drop(dentry);
+		goto out;
+	}
+
+	dentry->d_op = &dazukofs_dops;
+
+	lower_dentry_parent = get_lower_dentry(dentry->d_parent);
+	lower_dentry = lookup_one_len(dentry->d_name.name,
+				      lower_dentry_parent,
+				      dentry->d_name.len);
+
+	if (IS_ERR(lower_dentry)) {
+		err = PTR_ERR(lower_dentry);
+		d_drop(dentry);
+		goto out;
+	}
+
+	BUG_ON(!atomic_read(&lower_dentry->d_count));
+
+	set_dentry_private(dentry,
+			   kmem_cache_zalloc(dazukofs_dentry_info_cachep,
+					     GFP_KERNEL));
+
+	if (!get_dentry_private(dentry)) {
+		err = -ENOMEM;
+		goto out_dput;
+	}
+
+	lower_mnt = mntget(get_lower_mnt(dentry->d_parent));
+
+	fsstack_copy_attr_atime(dir, lower_dentry_parent->d_inode);
+
+	set_lower_dentry(dentry, lower_dentry, lower_mnt);
+
+	if (!lower_dentry->d_inode) {
+		/*
+		 * We want to add because we could not find in lower.
+		 */
+		d_add(dentry, NULL);
+		goto out;
+	}
+
+	err = dazukofs_interpose(lower_dentry, dentry, dir->i_sb, 1);
+	if (err)
+		goto out_dput;
+	goto out;
+
+out_dput:
+	dput(lower_dentry);
+	d_drop(dentry);
+out:
+	return ERR_PTR(err);
+}
+
+/**
+ * Description: Called by the mknod(2) system call to create a device
+ * (char, block) inode or a named pipe (FIFO) or socket. Only required if
+ * you want to support creating these types of inodes. You will probably
+ * need to call d_instantiate() just as you would in the create() method.
+ */
+static int dazukofs_mknod(struct inode *dir, struct dentry *dentry, int mode,
+			  dev_t dev)
+{
+	struct dentry *lower_dentry = get_lower_dentry(dentry);
+	struct dentry *lower_dentry_parent = dget(lower_dentry->d_parent);
+	struct inode *lower_dentry_parent_inode = lower_dentry_parent->d_inode;
+	int err;
+
+	mutex_lock_nested(&(lower_dentry_parent_inode->i_mutex),
+			  I_MUTEX_PARENT);
+
+	err = vfs_mknod(lower_dentry_parent_inode, lower_dentry, mode, dev);
+	if (err)
+		goto out;
+
+	err = dazukofs_interpose(lower_dentry, dentry, dir->i_sb, 0);
+	if (err)
+		goto out;
+
+	fsstack_copy_attr_times(dir, lower_dentry_parent_inode);
+	fsstack_copy_inode_size(dir, lower_dentry_parent_inode);
+out:
+	mutex_unlock(&(lower_dentry_parent_inode->i_mutex));
+	dput(lower_dentry_parent);
+	return err;
+}
+
+/**
+ * Description: Called by the mkdir(2) system call. Only required if you
+ * want to support creating subdirectories. You will probably need to call
+ * d_instantiate() just as you would in the create() method.
+ */
+static int dazukofs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+	struct dentry *lower_dentry = get_lower_dentry(dentry);
+	struct dentry *lower_dentry_parent = dget(lower_dentry->d_parent);
+	struct inode *lower_dentry_parent_inode = lower_dentry_parent->d_inode;
+	int err;
+
+	mutex_lock_nested(&(lower_dentry_parent_inode->i_mutex),
+			  I_MUTEX_PARENT);
+
+	err = vfs_mkdir(lower_dentry_parent_inode, lower_dentry, mode);
+	if (err)
+		goto out;
+
+	err = dazukofs_interpose(lower_dentry, dentry, dir->i_sb, 0);
+	if (err)
+		goto out;
+
+	fsstack_copy_attr_times(dir, lower_dentry_parent_inode);
+	fsstack_copy_inode_size(dir, lower_dentry_parent_inode);
+	dir->i_nlink = lower_dentry_parent_inode->i_nlink;
+out:
+	mutex_unlock(&(lower_dentry_parent_inode->i_mutex));
+	dput(lower_dentry_parent);
+	return err;
+}
+
+/**
+ * Description: Called by the open(2) and creat(2) system calls. Only
+ * required if you want to support regular files. The dentry you get
+ * should not have an inode (i.e. it should be a negative dentry). Here
+ * you will probably call d_instantiate() with the dentry and the newly
+ * created inode.
+ */
+static int dazukofs_create(struct inode *dir, struct dentry *dentry, int mode,
+			   struct nameidata *nd)
+{
+	struct vfsmount *lower_mnt = get_lower_mnt(dentry);
+	struct dentry *lower_dentry = get_lower_dentry(dentry);
+	struct dentry *lower_dentry_parent = dget(lower_dentry->d_parent);
+	struct inode *lower_dentry_parent_inode = lower_dentry_parent->d_inode;
+	struct vfsmount *vfsmount_save;
+	struct dentry *dentry_save;
+	int err;
+
+	mutex_lock_nested(&(lower_dentry_parent_inode->i_mutex),
+			  I_MUTEX_PARENT);
+
+	vfsmount_save = nd->path.mnt;
+	dentry_save = nd->path.dentry;
+
+	nd->path.mnt = mntget(lower_mnt);
+	nd->path.dentry = dget(lower_dentry);
+
+	err = vfs_create(lower_dentry_parent_inode, lower_dentry, mode, nd);
+
+	mntput(lower_mnt);
+	dput(lower_dentry);
+
+	nd->path.mnt = vfsmount_save;
+	nd->path.dentry = dentry_save;
+
+	if (err)
+		goto out;
+
+	err = dazukofs_interpose(lower_dentry, dentry, dir->i_sb, 0);
+	if (err)
+		goto out;
+
+	fsstack_copy_attr_times(dir, lower_dentry_parent_inode);
+	fsstack_copy_inode_size(dir, lower_dentry_parent_inode);
+out:
+	mutex_unlock(&(lower_dentry_parent_inode->i_mutex));
+	dput(lower_dentry_parent);
+	return err;
+}
+
+/**
+ * Description: Called by the symlink(2) system call. Only required if you
+ * want to support symlinks. You will probably need to call d_instantiate()
+ * just as you would in the create() method.
+ */
+static int dazukofs_symlink(struct inode *dir, struct dentry *dentry,
+			    const char *symname)
+{
+	struct dentry *lower_dentry = get_lower_dentry(dentry);
+	struct dentry *lower_dentry_parent = dget(lower_dentry->d_parent);
+	struct inode *lower_dentry_parent_inode = lower_dentry_parent->d_inode;
+	int err;
+
+	mutex_lock_nested(&(lower_dentry_parent_inode->i_mutex),
+			  I_MUTEX_PARENT);
+
+	err = vfs_symlink(lower_dentry_parent_inode, lower_dentry, symname);
+	if (err)
+		goto out;
+
+	err = dazukofs_interpose(lower_dentry, dentry, dir->i_sb, 0);
+	if (err)
+		goto out;
+
+	fsstack_copy_attr_times(dir, lower_dentry_parent_inode);
+	fsstack_copy_inode_size(dir, lower_dentry_parent_inode);
+out:
+	mutex_unlock(&(lower_dentry_parent_inode->i_mutex));
+	dput(lower_dentry_parent);
+	return err;
+}
+
+/**
+ * Description: Called by the readlink(2) system call. Only required if
+ * you want to support reading symbolic links.
+ */
+static int dazukofs_readlink(struct dentry *dentry, char __user *buf,
+			     int bufsiz)
+{
+	struct dentry *lower_dentry = get_lower_dentry(dentry);
+	struct inode *lower_dentry_inode = lower_dentry->d_inode;
+	int err;
+
+	if (!lower_dentry_inode) {
+		err = -ENOENT;
+		d_drop(dentry);
+		goto out;
+	}
+
+	if (!lower_dentry_inode->i_op->readlink) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	err = lower_dentry_inode->i_op->readlink(lower_dentry, buf, bufsiz);
+	if (err)
+		goto out;
+
+	fsstack_copy_attr_times(dentry->d_inode, lower_dentry_inode);
+out:
+	return err;
+}
+
+/**
+ * Description: Called by the VFS to follow a symbolic link to the inode
+ * it points to. Only required if you want to support symbolic links. This
+ * method returns a void pointer cookie that is passed to put_link().
+ */
+static void *dazukofs_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+	mm_segment_t fs_save;
+	int rc;
+	char *buf;
+	int len = PAGE_SIZE;
+	int err = 0;
+
+	/*
+	 * Released in dazukofs_put_link(). Only release here on error.
+	 */
+	buf = kmalloc(len, GFP_KERNEL);
+	if (!buf) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	fs_save = get_fs();
+	set_fs(get_ds());
+	rc = dazukofs_readlink(dentry, (char __user *)buf, len);
+	set_fs(fs_save);
+
+	if (rc < 0) {
+		err = rc;
+		goto out_free;
+	}
+	buf[rc] = 0;
+
+	nd_set_link(nd, buf);
+	goto out;
+
+out_free:
+	kfree(buf);
+out:
+	return ERR_PTR(err);
+}
+
+/**
+ * Description: Called by the VFS to release resources allocated by
+ * follow_link(). The cookie returned by follow_link() is passed to this
+ * method as the last parameter. It is used by filesystems such as NFS
+ * where page cache is not stable (i.e. page that was installed when the
+ * symbolic link walk started might not be in the page cache at the end
+ * of the walk).
+ */
+static void dazukofs_put_link(struct dentry *dentry, struct nameidata *nd,
+			      void *ptr)
+{
+	/*
+	 * Release the char* from dazukofs_follow_link().
+	 */
+	kfree(nd_get_link(nd));
+}
+
+/**
+ * Description: Called by the VFS to check for access rights on a
+ * POSIX-like filesystem.
+ */
+static int dazukofs_permission(struct inode *inode, int mask)
+{
+	return inode_permission(get_lower_inode(inode), mask);
+}
+
+/**
+ * Description: Called by the VFS to set attributes for a file. This method
+ * is called by chmod(2) and related system calls.
+ */
+static int dazukofs_setattr(struct dentry *dentry, struct iattr *ia)
+{
+	struct dentry *lower_dentry = get_lower_dentry(dentry);
+	struct inode *inode = dentry->d_inode;
+	struct inode *lower_inode = get_lower_inode(inode);
+	int err;
+
+	err = notify_change(lower_dentry, ia);
+
+	fsstack_copy_attr_all(inode, lower_inode, NULL);
+	fsstack_copy_inode_size(inode, lower_inode);
+	return err;
+}
+
+/**
+ * Description: Called by the VFS to set an extended attribute for a file.
+ * Extended attribute is a name:value pair associated with an inode. This
+ * method is called by setxattr(2) system call.
+ */
+static int dazukofs_setxattr(struct dentry *dentry, const char *name,
+			     const void *value, size_t size, int flags)
+{
+	struct dentry *lower_dentry = get_lower_dentry(dentry);
+	struct inode *lower_dentry_inode = lower_dentry->d_inode;
+	int err;
+
+	if (!lower_dentry_inode) {
+		err = -ENOENT;
+		d_drop(dentry);
+		goto out;
+	}
+
+	if (!lower_dentry_inode->i_op->setxattr) {
+		err = -ENOSYS;
+		goto out;
+	}
+
+	err = lower_dentry_inode->i_op->setxattr(lower_dentry, name, value,
+						 size, flags);
+
+	fsstack_copy_attr_all(dentry->d_inode, lower_dentry_inode, NULL);
+	fsstack_copy_inode_size(dentry->d_inode, lower_dentry_inode);
+out:
+	return err;
+}
+
+/**
+ * Description: Called by the VFS to retrieve the value of an extended
+ * attribute name. This method is called by getxattr(2) function call.
+ */
+static ssize_t dazukofs_getxattr(struct dentry *dentry, const char *name,
+				 void *value, size_t size)
+{
+	struct dentry *lower_dentry = get_lower_dentry(dentry);
+	struct inode *lower_dentry_inode = lower_dentry->d_inode;
+	ssize_t err;
+
+	if (!lower_dentry_inode) {
+		err = -ENOENT;
+		d_drop(dentry);
+		goto out;
+	}
+
+	if (!lower_dentry_inode->i_op->getxattr) {
+		err = -ENOSYS;
+		goto out;
+	}
+
+	err = lower_dentry_inode->i_op->getxattr(lower_dentry, name,
+						 value, size);
+out:
+	return err;
+}
+
+/**
+ * Description: Called by the VFS to list all extended attributes for a
+ * given file. This method is called by listxattr(2) system call.
+ */
+static ssize_t dazukofs_listxattr(struct dentry *dentry, char *list,
+				  size_t size)
+{
+	struct dentry *lower_dentry = get_lower_dentry(dentry);
+	struct inode *lower_dentry_inode = lower_dentry->d_inode;
+	int err;
+
+	if (!lower_dentry_inode) {
+		err = -ENOENT;
+		d_drop(dentry);
+		goto out;
+	}
+
+	if (!lower_dentry_inode->i_op->listxattr) {
+		err = -ENOSYS;
+		goto out;
+	}
+
+	err = lower_dentry_inode->i_op->listxattr(lower_dentry, list, size);
+out:
+	return err;
+}
+
+/**
+ * Description: Called by the VFS to remove an extended attribute from a
+ * file. This method is called by removexattr(2) system call.
+ */
+static int dazukofs_removexattr(struct dentry *dentry, const char *name)
+{
+	struct dentry *lower_dentry = get_lower_dentry(dentry);
+	struct inode *lower_dentry_inode = lower_dentry->d_inode;
+	int err;
+
+	if (!lower_dentry_inode) {
+		err = -ENOENT;
+		d_drop(dentry);
+		goto out;
+	}
+
+	if (!lower_dentry_inode->i_op->removexattr) {
+		err = -ENOSYS;
+		goto out;
+	}
+
+	err = lower_dentry_inode->i_op->removexattr(lower_dentry, name);
+out:
+	return err;
+}
+
+/**
+ * Description: Called by the link(2) system call. Only required if you want
+ * to support hard links. You will probably need to call d_instantiate()
+ * just as you would in the create() method.
+ */
+static int dazukofs_link(struct dentry *old_dentry, struct inode *dir,
+			 struct dentry *new_dentry)
+{
+	struct dentry *lower_old_dentry = get_lower_dentry(old_dentry);
+	struct dentry *lower_new_dentry = get_lower_dentry(new_dentry);
+	struct dentry *lower_dentry_parent = dget(lower_new_dentry->d_parent);
+	struct inode *lower_dentry_parent_inode = lower_dentry_parent->d_inode;
+	int err;
+
+	mutex_lock_nested(&(lower_dentry_parent_inode->i_mutex),
+			  I_MUTEX_PARENT);
+
+	err = vfs_link(lower_old_dentry, lower_dentry_parent_inode,
+		       lower_new_dentry);
+	if (err)
+		goto out;
+
+	err = dazukofs_interpose(lower_new_dentry, new_dentry, dir->i_sb, 0);
+	if (err)
+		goto out;
+
+	fsstack_copy_attr_times(dir, lower_dentry_parent_inode);
+	fsstack_copy_inode_size(dir, lower_dentry_parent_inode);
+out:
+	mutex_unlock(&(lower_dentry_parent_inode->i_mutex));
+	dput(lower_dentry_parent);
+	return err;
+}
+
+/**
+ * Description: Called by the unlink(2) system call. Only required if you
+ * want to support deleting inodes.
+ */
+static int dazukofs_unlink(struct inode *dir, struct dentry *dentry)
+{
+	struct dentry *lower_dentry = get_lower_dentry(dentry);
+	struct dentry *lower_dentry_parent = dget(lower_dentry->d_parent);
+	struct inode *lower_dentry_parent_inode = lower_dentry_parent->d_inode;
+	int err;
+
+	mutex_lock_nested(&(lower_dentry_parent_inode->i_mutex),
+			  I_MUTEX_PARENT);
+
+	err = vfs_unlink(lower_dentry_parent_inode, lower_dentry);
+	if (err)
+		goto out;
+
+	fsstack_copy_attr_times(dir, lower_dentry_parent_inode);
+	dentry->d_inode->i_nlink =
+		get_lower_inode(dentry->d_inode)->i_nlink;
+	fsstack_copy_attr_times(dentry->d_inode, dir);
+out:
+	mutex_unlock(&(lower_dentry_parent_inode->i_mutex));
+	dput(lower_dentry_parent);
+	return err;
+}
+
+/**
+ * Description: Called by the rmdir(2) system call. Only required if you
+ * want to support deleting subdirectories.
+ */
+static int dazukofs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	struct dentry *lower_dentry = get_lower_dentry(dentry);
+	struct dentry *lower_dentry_parent = dget(lower_dentry->d_parent);
+	struct inode *lower_dentry_parent_inode = lower_dentry_parent->d_inode;
+	int err;
+
+	mutex_lock_nested(&(lower_dentry_parent_inode->i_mutex),
+			  I_MUTEX_PARENT);
+
+	err = vfs_rmdir(lower_dentry_parent_inode, lower_dentry);
+	if (err)
+		goto out;
+
+	fsstack_copy_attr_times(dir, lower_dentry_parent_inode);
+	dir->i_nlink = lower_dentry_parent_inode->i_nlink;
+out:
+	mutex_unlock(&(lower_dentry_parent_inode->i_mutex));
+	dput(lower_dentry_parent);
+
+	if (!err)
+		d_drop(dentry);
+	return err;
+}
+
+/**
+ * Description: Called by the rename(2) system call to rename the object to
+ * have the parent and name given by the second inode and dentry.
+ */
+static int dazukofs_rename(struct inode *old_dir, struct dentry *old_dentry,
+			   struct inode *new_dir, struct dentry *new_dentry)
+{
+	struct dentry *lower_old_dentry = get_lower_dentry(old_dentry);
+	struct dentry *lower_new_dentry = get_lower_dentry(new_dentry);
+	struct dentry *lower_old_dentry_parent =
+		dget(lower_old_dentry->d_parent);
+	struct dentry *lower_new_dentry_parent =
+		dget(lower_new_dentry->d_parent);
+	struct inode *lower_old_dentry_parent_inode =
+		lower_old_dentry_parent->d_inode;
+	struct inode *lower_new_dentry_parent_inode =
+		lower_new_dentry_parent->d_inode;
+	int err = -ENOENT;
+
+	if (!lower_old_dentry_parent_inode) {
+		d_drop(old_dentry);
+		goto out;
+	}
+
+	if (!lower_new_dentry_parent_inode) {
+		d_drop(new_dentry);
+		goto out;
+	}
+
+	lock_rename(lower_old_dentry_parent, lower_new_dentry_parent);
+	err = vfs_rename(lower_old_dentry_parent_inode, lower_old_dentry,
+			 lower_new_dentry_parent_inode, lower_new_dentry);
+	unlock_rename(lower_old_dentry_parent, lower_new_dentry_parent);
+
+	if (err)
+		goto out;
+
+	fsstack_copy_attr_all(new_dir, lower_new_dentry_parent_inode, NULL);
+	if (new_dir != old_dir)
+		fsstack_copy_attr_all(old_dir, lower_old_dentry_parent_inode,
+				      NULL);
+out:
+	dput(lower_old_dentry_parent);
+	dput(lower_new_dentry_parent);
+	return err;
+}
+
+/**
+ * Unused operations:
+ *   - create
+ *   - lookup
+ *   - link
+ *   - unlink
+ *   - symlink
+ *   - mkdir
+ *   - rmdir
+ *   - mknod
+ *   - rename
+ *   - truncate
+ *   - getattr
+ *   - truncate_range
+ *   - fallocate
+ */
+static struct inode_operations dazukofs_symlink_iops = {
+	.readlink	= dazukofs_readlink,
+	.follow_link	= dazukofs_follow_link,
+	.put_link	= dazukofs_put_link,
+	.permission	= dazukofs_permission,
+	.setattr	= dazukofs_setattr,
+	.setxattr	= dazukofs_setxattr,
+	.getxattr	= dazukofs_getxattr,
+	.listxattr	= dazukofs_listxattr,
+	.removexattr	= dazukofs_removexattr,
+};
+
+/**
+ * Unused operations:
+ *   - readlink
+ *   - follow_link
+ *   - put_link
+ *   - truncate
+ *   - getattr
+ *   - truncate_range
+ *   - fallocate
+ */
+static struct inode_operations dazukofs_dir_iops = {
+	.create		= dazukofs_create,
+	.lookup		= dazukofs_lookup,
+	.link		= dazukofs_link,
+	.unlink		= dazukofs_unlink,
+	.symlink	= dazukofs_symlink,
+	.mkdir		= dazukofs_mkdir,
+	.rmdir		= dazukofs_rmdir,
+	.mknod		= dazukofs_mknod,
+	.rename		= dazukofs_rename,
+	.permission	= dazukofs_permission,
+	.setattr	= dazukofs_setattr,
+	.setxattr	= dazukofs_setxattr,
+	.getxattr	= dazukofs_getxattr,
+	.listxattr	= dazukofs_listxattr,
+	.removexattr	= dazukofs_removexattr,
+};
+
+/**
+ * Unused operations:
+ *   - create
+ *   - lookup
+ *   - link
+ *   - unlink
+ *   - symlink
+ *   - mkdir
+ *   - rmdir
+ *   - mknod
+ *   - rename
+ *   - readlink
+ *   - follow_link
+ *   - put_link
+ *   - truncate
+ *   - getattr
+ *   - truncate_range
+ *   - fallocate
+ */
+static struct inode_operations dazukofs_main_iops = {
+	.permission	= dazukofs_permission,
+	.setattr	= dazukofs_setattr,
+	.setxattr	= dazukofs_setxattr,
+	.getxattr	= dazukofs_getxattr,
+	.listxattr	= dazukofs_listxattr,
+	.removexattr	= dazukofs_removexattr,
+};
Index: linux-2.6.28/fs/dazukofs/mmap.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.28/fs/dazukofs/mmap.c	2009-02-03 18:07:44.000000000 +0100
@@ -0,0 +1,116 @@
+/* dazukofs: access control stackable filesystem
+
+   Copyright (C) 1997-2003 Erez Zadok
+   Copyright (C) 2001-2003 Stony Brook University
+   Copyright (C) 2004-2007 International Business Machines Corp.
+   Copyright (C) 2008-2009 John Ogness
+     Author: John Ogness <dazukocode@ogness.net>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+
+#include "dazukofs_fs.h"
+
+/**
+ * Description: Called by the VM to read a page from backing store. The page
+ * will be Locked when readpage is called, and should be unlocked and marked
+ * uptodate once the read completes. If ->readpage discovers that it needs
+ * to unlock the page for some reason, it can do so, and then return
+ * AOP_TRUNCATED_PAGE. In this case, the page will be relocated, relocked
+ * and if that all succeeds, ->readpage will be called again.
+ */
+static int dazukofs_readpage(struct file *file, struct page *page)
+{
+	struct dentry *dentry = file->f_dentry;
+	struct file *lower_file = get_lower_file(file);
+	struct inode *inode = dentry->d_inode;
+	struct inode *lower_inode = get_lower_inode(inode);
+	const struct address_space_operations *lower_a_ops =
+		lower_inode->i_mapping->a_ops;
+	char *page_data;
+	struct page *lower_page;
+	char *lower_page_data;
+	int err = 0;
+
+	lower_page = read_cache_page(lower_inode->i_mapping, page->index,
+				     (filler_t *)lower_a_ops->readpage,
+				     (void *)lower_file);
+
+	if (IS_ERR(lower_page)) {
+		err = PTR_ERR(lower_page);
+		lower_page = NULL;
+		printk(KERN_ERR "dazukofs: Error reading from page cache.\n");
+		goto out;
+	}
+
+	wait_on_page_locked(lower_page);
+
+	page_data = (char *)kmap(page);
+	if (!page_data) {
+		err = -ENOMEM;
+		printk(KERN_ERR "dazukofs: Error mapping page.\n");
+		goto out;
+	}
+
+	lower_page_data = (char *)kmap(lower_page);
+	if (!lower_page_data) {
+		err = -ENOMEM;
+		printk(KERN_ERR "dazukofs: Error mapping lower page.\n");
+		goto out;
+	}
+
+	memcpy(page_data, lower_page_data, PAGE_CACHE_SIZE);
+
+	kunmap(lower_page);
+	kunmap(page);
+out:
+	if (lower_page)
+		page_cache_release(lower_page);
+
+	if (err)
+		ClearPageUptodate(page);
+	else
+		SetPageUptodate(page);
+
+	unlock_page(page);
+	return err;
+}
+
+/**
+ * Unused operations:
+ *   - writepage
+ *   - sync_page
+ *   - writepages
+ *   - set_page_dirty
+ *   - readpages
+ *   - prepare_write
+ *   - commit_write
+ *   - write_begin
+ *   - write_end
+ *   - bmap
+ *   - invalidatepage
+ *   - releasepage
+ *   - direct_IO
+ *   - get_xip_page
+ *   - migratepage
+ *   - launder_page
+ */
+const struct address_space_operations dazukofs_aops = {
+	.readpage	= dazukofs_readpage,
+};
Index: linux-2.6.28/fs/dazukofs/Makefile
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.28/fs/dazukofs/Makefile	2009-02-03 18:07:44.000000000 +0100
@@ -0,0 +1,7 @@
+#
+# Makefile for the Linux dazukofs-filesystem routines.
+#
+
+obj-$(CONFIG_DAZUKOFS_FS) += dazukofs.o
+
+dazukofs-objs := super.o inode.o file.o dentry.o mmap.o
Index: linux-2.6.28/fs/Makefile
===================================================================
--- linux-2.6.28.orig/fs/Makefile	2009-02-02 22:05:54.000000000 +0100
+++ linux-2.6.28/fs/Makefile	2009-02-02 22:05:56.000000000 +0100
@@ -85,6 +85,7 @@
 obj-$(CONFIG_HFSPLUS_FS)	+= hfsplus/ # Before hfs to find wrapped HFS+
 obj-$(CONFIG_HFS_FS)		+= hfs/
 obj-$(CONFIG_ECRYPT_FS)		+= ecryptfs/
+obj-$(CONFIG_DAZUKOFS_FS)	+= dazukofs/
 obj-$(CONFIG_VXFS_FS)		+= freevxfs/
 obj-$(CONFIG_NFS_FS)		+= nfs/
 obj-$(CONFIG_EXPORTFS)		+= exportfs/
Index: linux-2.6.28/Documentation/filesystems/dazukofs.txt
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.28/Documentation/filesystems/dazukofs.txt	2009-02-03 18:07:44.000000000 +0100
@@ -0,0 +1,81 @@
+================
+ ABOUT DAZUKOFS
+================
+
+DazukoFS is a pass-through stackable filesystem. A filesystem that does
+not perform any special modification but simply passes VFS calls to and
+from the lower filesystem is typically known as nullfs.
+
+
+
+=====================
+ MOUNTING/UNMOUNTING
+=====================
+
+DazukoFS is typically mounted on top of an existing directory. For example,
+to stack DazukoFS on top of the /opt directory, the following mount(8)
+command can be given:
+
+# mount -t dazukofs /opt /opt
+
+A process that accesses files in /opt will now be accessing them through
+DazukoFS. The stackable filesystem can then be unmounted with:
+
+# umount /opt
+
+
+
+===============
+ MOUNT ON BOOT
+===============
+
+You may want DazukoFS to be mounted over certain directories when the
+machine boots. The easiest way to do this is to add the mounts to
+the end of /etc/fstab. They would look something like this:
+
+/usr   /usr   dazukofs   defaults   0   0
+/opt   /opt   dazukofs   defaults   0   0
+
+
+
+=========
+ WARNING
+=========
+
+It is possible to mount DazukoFS to a directory other than the directory
+that is being stacked upon. For example:
+
+# mount -t dazukofs /opt /mnt
+
+When accessing files within /mnt, you will be accessing files in /opt
+(through DazukoFS). When accessing files directly in /opt, DazukoFS will not
+be involved.
+
+THIS HAS POTENTIAL PROBLEMS!
+
+If files are modified directly in /opt, the DazukoFS layer will not know
+about it. When DazukoFS later tries to access those files, it may result
+in corrupt data or kernel crashes. As long as /opt is modified ONLY through
+DazukoFS, there should not be any problems.
+
+This method of mounting DazukoFS may be interesting for servers that export
+a part of the filesystem and the service is in a chroot environment.
+
+
+
+==============
+ KNOWN ISSUES
+==============
+
+- DazukoFS does not support writing to memory mapped files. This should not
+  cause the kernel to crash, but will instead result in the application
+  failing to perform the writes (although mmap() will appear to be
+  successful from the application's viewpoint!).
+
+- It is not possible to stack DazukoFS over the root filesystem (/).
+  Stacking over pseudo filesystems (/proc, /dev, /sys) has not been
+  tested and should be avoided.
+
+Please report problems to the dazuko-devel mailing list
+(subscription required):
+     http://lists.nongnu.org/mailman/listinfo/dazuko-devel
Index: linux-2.6.28/fs/dazukofs/Kconfig
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.28/fs/dazukofs/Kconfig	2009-02-03 18:07:44.000000000 +0100
@@ -0,0 +1,10 @@
+config DAZUKOFS_FS
+	tristate "DazukoFS filesystem layer support (EXPERIMENTAL)"
+	depends on EXPERIMENTAL
+	help
+	  A pass-through stackable filesystem (also referred to as nullfs).
+	  See <file:Documentation/filesystems/dazukofs.txt> to learn more
+	  about DazukoFS.
+
+	  To compile this file system support as a module, choose M here: the
+	  module will be called dazukofs.
Index: linux-2.6.28/fs/Kconfig
===================================================================
--- linux-2.6.28.orig/fs/Kconfig	2009-02-02 22:05:54.000000000 +0100
+++ linux-2.6.28/fs/Kconfig	2009-02-02 22:05:56.000000000 +0100
@@ -204,6 +204,7 @@
 source "fs/adfs/Kconfig"
 source "fs/affs/Kconfig"
 source "fs/ecryptfs/Kconfig"
+source "fs/dazukofs/Kconfig"
 source "fs/hfs/Kconfig"
 source "fs/hfsplus/Kconfig"
 source "fs/befs/Kconfig"

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCHv2 2/5] VFS: DazukoFS, stackable-fs, file access control
  2009-02-03 19:15 ` [PATCHv2 1/5] " John Ogness
@ 2009-02-03 19:17   ` John Ogness
  2009-02-03 19:18     ` [PATCHv2 3/5] " John Ogness
                       ` (2 more replies)
  2009-02-12 15:27   ` [PATCHv2 1/5] " Jan Engelhardt
  1 sibling, 3 replies; 24+ messages in thread
From: John Ogness @ 2009-02-03 19:17 UTC (permalink / raw)
  To: linux-kernel; +Cc: viro, malware-list, eparis, hch, alan

Patch 2: Creates /dev/dazukofs.0 for userspace applications to perform
         file access control. At this point, all applications are
         considered to be working together (in the same group).

Patched against 2.6.29-rc3.

Signed-off-by: John Ogness <dazukocode@ogness.net>
---
 Documentation/filesystems/dazukofs.txt |   89 +++
 fs/dazukofs/Kconfig                    |    3 
 fs/dazukofs/Makefile                   |    3 
 fs/dazukofs/dev.c                      |   84 +++
 fs/dazukofs/dev.h                      |   37 +
 fs/dazukofs/event.c                    |  630 +++++++++++++++++++++++
 fs/dazukofs/event.h                    |   32 +
 fs/dazukofs/file.c                     |    7 
 fs/dazukofs/group_dev.c                |  159 +++++
 fs/dazukofs/super.c                    |   16 
 10 files changed, 1047 insertions(+), 13 deletions(-)
Index: linux-2.6.28/fs/dazukofs/dev.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.28/fs/dazukofs/dev.c	2009-02-03 18:10:46.000000000 +0100
@@ -0,0 +1,84 @@
+/* dazukofs: access control stackable filesystem
+
+   Copyright (C) 2008-2009 John Ogness
+     Author: John Ogness <dazukocode@ogness.net>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*/
+
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+
+#include "dazukofs_fs.h"
+#include "event.h"
+#include "dev.h"
+
+static struct class *dazukofs_class;
+
+static int dev_major;
+static int dev_minor_start;
+static int dev_minor_end;
+
+int dazukofs_dev_init(void)
+{
+	int err;
+	dev_t devt;
+
+	err = dazukofs_init_events();
+	if (err)
+		goto error_out1;
+
+	err = alloc_chrdev_region(&devt, 0, 1, DEVICE_NAME);
+	if (err)
+		goto error_out2;
+	dev_major = MAJOR(devt);
+	dev_minor_start = MINOR(devt);
+
+	dazukofs_class = class_create(THIS_MODULE, DEVICE_NAME);
+	if (IS_ERR(dazukofs_class)) {
+		err = PTR_ERR(dazukofs_class);
+		goto error_out3;
+	}
+
+	dev_minor_end = dazukofs_group_dev_init(dev_major,
+						dev_minor_start + 1,
+						dazukofs_class);
+	if (dev_minor_end < 0) {
+		err = dev_minor_end;
+		goto error_out4;
+	}
+
+	return 0;
+
+error_out4:
+	class_destroy(dazukofs_class);
+error_out3:
+	unregister_chrdev_region(MKDEV(dev_major, dev_minor_start), 1);
+error_out2:
+	dazukofs_destroy_events();
+error_out1:
+	return err;
+}
+
+void dazukofs_dev_destroy(void)
+{
+	dazukofs_group_dev_destroy(dev_major, dev_minor_start + 1,
+				   dev_minor_end, dazukofs_class);
+	class_destroy(dazukofs_class);
+	unregister_chrdev_region(MKDEV(dev_major, dev_minor_start), 1);
+	dazukofs_destroy_events();
+}
Index: linux-2.6.28/fs/dazukofs/group_dev.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.28/fs/dazukofs/group_dev.c	2009-02-03 18:10:46.000000000 +0100
@@ -0,0 +1,159 @@
+/* dazukofs: access control stackable filesystem
+
+   Copyright (C) 2008-2009 John Ogness
+     Author: John Ogness <dazukocode@ogness.net>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*/
+
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+
+#include "dazukofs_fs.h"
+#include "event.h"
+#include "dev.h"
+
+static ssize_t dazukofs_group_read(struct file *file,
+				   char __user *buffer, size_t length,
+				   loff_t *pos)
+{
+#define DAZUKOFS_MIN_READ_BUFFER 43
+	char tmp[DAZUKOFS_MIN_READ_BUFFER];
+	ssize_t tmp_used;
+	pid_t pid;
+	int fd;
+	int err;
+	unsigned long event_id;
+
+	if (*pos > 0)
+		return 0;
+
+	if (length < DAZUKOFS_MIN_READ_BUFFER)
+		return -EINVAL;
+
+	err = dazukofs_get_event(&event_id, &fd, &pid);
+	if (err) {
+		if (err == -ERESTARTSYS)
+			return -EINTR;
+		return err;
+	}
+
+	tmp_used = snprintf(tmp, sizeof(tmp)-1, "id=%lu\nfd=%d\npid=%d\n",
+			    event_id, fd, pid);
+	if (tmp_used >= sizeof(tmp))
+		return -EINVAL;
+
+	if (copy_to_user(buffer, tmp, tmp_used))
+		return -EFAULT;
+
+	*pos = tmp_used;
+
+	return tmp_used;
+}
+
+static ssize_t dazukofs_group_write(struct file *file,
+				    const char __user *buffer, size_t length,
+				    loff_t *pos)
+{
+#define DAZUKOFS_MAX_WRITE_BUFFER 19
+	char tmp[DAZUKOFS_MAX_WRITE_BUFFER];
+	int response;
+	unsigned long event_id;
+	char *p;
+	char *p2;
+	int ret;
+
+	if (length >= DAZUKOFS_MAX_WRITE_BUFFER)
+		length = DAZUKOFS_MAX_WRITE_BUFFER - 1;
+
+	if (copy_from_user(tmp, buffer, length))
+		return -EFAULT;
+	tmp[length] = 0;
+
+	p = strstr(tmp, "id=");
+	if (!p)
+		return -EINVAL;
+	event_id = simple_strtoul(p + 3, &p2, 10);
+
+	/*
+	 * checkpatch.pl recommends using strict_strtoul() instead of
+	 * simple_strtoul(). However, we _want_ a function that stops
+	 * on non-number characters rather than errors out.
+	 */
+
+	p = strstr(p2, "r=");
+	if (!p)
+		return -EINVAL;
+	response = (*(p + 2)) - '0';
+
+	ret = dazukofs_return_event(event_id, response);
+	if (ret == 0) {
+		*pos += length;
+		ret = length;
+	} else if (ret == -ERESTARTSYS) {
+		ret = -EINTR;
+	}
+
+	return ret;
+}
+
+static struct cdev group_cdev;
+
+static const struct file_operations group_fops = {
+	.owner		= THIS_MODULE,
+	.read		= dazukofs_group_read,
+	.write		= dazukofs_group_write,
+};
+
+int dazukofs_group_dev_init(int dev_major, int dev_minor_start,
+			    struct class *dazukofs_class)
+{
+	int err;
+	struct device *dev;
+	int dev_minor_end = dev_minor_start;
+
+	cdev_init(&group_cdev, &group_fops);
+	group_cdev.owner = THIS_MODULE;
+	err = cdev_add(&group_cdev, MKDEV(dev_major, dev_minor_start), 1);
+	if (err)
+		goto error_out1;
+
+	dev = device_create(dazukofs_class, NULL,
+			    MKDEV(dev_major, dev_minor_end), NULL,
+			    "%s.%d", DEVICE_NAME, 0);
+	if (IS_ERR(dev)) {
+		err = PTR_ERR(dev);
+		goto error_out2;
+	}
+	dev_minor_end++;
+
+	return dev_minor_end;
+
+error_out2:
+	device_destroy(dazukofs_class, MKDEV(dev_major, 0));
+error_out1:
+	cdev_del(&group_cdev);
+	return err;
+}
+
+void dazukofs_group_dev_destroy(int dev_major, int dev_minor_start,
+				int dev_minor_end,
+				struct class *dazukofs_class)
+{
+	device_destroy(dazukofs_class, MKDEV(dev_major, 0));
+	cdev_del(&group_cdev);
+}
Index: linux-2.6.28/fs/dazukofs/event.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.28/fs/dazukofs/event.c	2009-02-03 18:10:46.000000000 +0100
@@ -0,0 +1,630 @@
+/* dazukofs: access control stackable filesystem
+
+   Copyright (C) 2008-2009 John Ogness
+     Author: John Ogness <dazukocode@ogness.net>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*/
+
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/freezer.h>
+#include <linux/cred.h>
+
+#include "dazukofs_fs.h"
+
+struct dazukofs_proc {
+	struct list_head list;
+	struct task_struct *curr;
+};
+
+struct dazukofs_event {
+	unsigned long event_id;
+	struct dentry *dentry;
+	struct vfsmount *mnt;
+	pid_t pid;
+	wait_queue_head_t queue;
+
+	/* protects: deny, deprecated, assigned */
+	struct mutex assigned_mutex;
+
+	int deny;
+	int deprecated;
+	int assigned;
+};
+
+struct dazukofs_event_container {
+	struct list_head list;
+	struct dazukofs_event *event;
+	struct file *file;
+	int fd;
+};
+
+struct dazukofs_group {
+	struct dazukofs_event_container todo_list;
+	wait_queue_head_t queue;
+	struct dazukofs_event_container working_list;
+};
+
+static struct dazukofs_group reg_group;
+
+/* protects: grp->members, last_event_id,
+ *	     todo_list, working_list */
+static struct mutex work_mutex;
+
+static struct mutex proc_mutex;
+static struct dazukofs_proc proc_list;
+
+static struct kmem_cache *dazukofs_event_container_cachep;
+static struct kmem_cache *dazukofs_event_cachep;
+
+static int last_event_id;
+
+/**
+ * dazukofs_init_events - initialize event handling infrastructure
+ *
+ * Description: This is called once to initialize all the structures
+ * needed to manage event handling.
+ *
+ * Returns 0 on success.
+ */
+int dazukofs_init_events(void)
+{
+	mutex_init(&proc_mutex);
+	mutex_init(&work_mutex);
+	INIT_LIST_HEAD(&proc_list.list);
+
+	init_waitqueue_head(&reg_group.queue);
+	INIT_LIST_HEAD(&reg_group.todo_list.list);
+	INIT_LIST_HEAD(&reg_group.working_list.list);
+
+	dazukofs_event_container_cachep =
+		kmem_cache_create("dazukofs_event_container_cache",
+				  sizeof(struct dazukofs_event_container), 0,
+				  SLAB_HWCACHE_ALIGN, NULL);
+	if (!dazukofs_event_container_cachep)
+		goto error_out;
+
+	dazukofs_event_cachep =
+		kmem_cache_create("dazukofs_event_cache",
+				  sizeof(struct dazukofs_event), 0,
+				  SLAB_HWCACHE_ALIGN, NULL);
+	if (!dazukofs_event_cachep)
+		goto error_out;
+
+	return 0;
+
+error_out:
+	if (dazukofs_event_container_cachep)
+		kmem_cache_destroy(dazukofs_event_container_cachep);
+	if (dazukofs_event_cachep)
+		kmem_cache_destroy(dazukofs_event_cachep);
+	return -ENOMEM;
+}
+
+/**
+ * release_event - release (and possible free) an event
+ * @evt: the event to release
+ * @decrement_assigned: flag to signal if the assigned count should be
+ *                      decremented (only for registered processes)
+ * @deny: flag if file access event should be denied
+ *
+ * Description: This function will decrement the assigned count for the
+ * event. The "decrement_assigned" flag is used to distinguish between
+ * the anonymous process accessing a file and the registered process. The
+ * assigned count is only incremented for registered process (although the
+ * anonymous process will also have a handle to the event).
+ *
+ * For the anonymous process (decrement_assigned = false):
+ * If the assigned count is not zero, there are registered processes that
+ * have a handle to this event. The event is marked deprecated. Otherwise
+ * we free the event.
+ *
+ * For a registered process (decrement_assigned = true):
+ * The assigned count is decremented. If it is now zero and the event is
+ * not deprecated, then the anonymous process still has a handle. In this
+ * case we wake the anonymous process. Otherwise we free the event.
+ *
+ * Aside from releasing the event, the deny status of the event is also
+ * updated. The "normal" release process involves the registered processes
+ * first releasing (and providing their deny values) and finally the
+ * anonymous process will release (and free) the event after reading the
+ * deny value.
+ */
+static void release_event(struct dazukofs_event *evt, int decrement_assigned,
+			  int deny)
+{
+	int free_event = 0;
+
+	mutex_lock(&evt->assigned_mutex);
+	if (deny)
+		evt->deny |= 1;
+
+	if (decrement_assigned) {
+		evt->assigned--;
+		if (evt->assigned == 0) {
+			if (!evt->deprecated)
+				wake_up(&evt->queue);
+			else
+				free_event = 1;
+		}
+	} else {
+		if (evt->assigned == 0)
+			free_event = 1;
+		else
+			evt->deprecated = 1;
+	}
+	mutex_unlock(&evt->assigned_mutex);
+
+	if (free_event) {
+		dput(evt->dentry);
+		mntput(evt->mnt);
+		kmem_cache_free(dazukofs_event_cachep, evt);
+	}
+}
+
+/**
+ * __clear_group_event_list - cleanup/release event list
+ * @event_list - the list to clear
+ *
+ * Description: All events (and their containers) will be released/freed
+ * for the given event list. The event list will be an empty (yet still
+ * valid) list after this function is finished.
+ *
+ * IMPORTANT: This function requires work_mutex to be held!
+ */
+static void __clear_group_event_list(struct list_head *event_list)
+{
+	struct dazukofs_event_container *ec;
+	struct list_head *pos;
+	struct list_head *q;
+
+	list_for_each_safe(pos, q, event_list) {
+		ec = list_entry(pos, struct dazukofs_event_container, list);
+		list_del(pos);
+
+		release_event(ec->event, 1, 0);
+
+		kmem_cache_free(dazukofs_event_container_cachep, ec);
+	}
+}
+
+/**
+ * __remove_group - clear all activity associated with the group
+ * @grp: the group to clear
+ *
+ * Description: All pending and in-progress events are released/freed.
+ * Any processes waiting on the queue are woken.
+ *
+ * IMPORTANT: This function requires work_mutex to be held!
+ */
+static void __remove_group(struct dazukofs_group *grp)
+{
+	__clear_group_event_list(&grp->working_list.list);
+	__clear_group_event_list(&grp->todo_list.list);
+
+	/* notify all registered process waiting for an event */
+	wake_up_all(&grp->queue);
+}
+
+/**
+ * dazukofs_destroy_events - cleanup/shutdown event handling infrastructure
+ *
+ * Description: Release all pending events, free all allocated structures.
+ */
+void dazukofs_destroy_events(void)
+{
+	/* free the group items */
+	mutex_lock(&work_mutex);
+	__remove_group(&reg_group);
+	mutex_unlock(&work_mutex);
+
+	/* free everything else */
+	kmem_cache_destroy(dazukofs_event_container_cachep);
+	kmem_cache_destroy(dazukofs_event_cachep);
+}
+
+/**
+ * check_recursion - check if current process is recursing
+ *
+ * Description: A list of anonymous processes is managed to prevent
+ * access event recursion. This function checks if the current process is
+ * a part of that list.
+ *
+ * If the current process is found in the process list, it is removed.
+ *
+ * NOTE: The proc structure is not freed. It is only removed from the
+ *       list. Since it is a recursive call, the caller can free the
+ *       structure after the call chain is finished.
+ *
+ * Returns 0 if this is a recursive process call.
+ */
+static int check_recursion(void)
+{
+	struct dazukofs_proc *proc;
+	struct list_head *pos;
+	int found = 0;
+
+	mutex_lock(&proc_mutex);
+	list_for_each(pos, &proc_list.list) {
+		proc = list_entry(pos, struct dazukofs_proc, list);
+		if (proc->curr == current) {
+			found = 1;
+			list_del(pos);
+			break;
+		}
+	}
+	mutex_unlock(&proc_mutex);
+
+	/* process event if not found */
+	return !found;
+}
+
+/**
+ * event_assigned - check if event is (still) assigned
+ * @event: event to check
+ *
+ * Description: This function checks if an event is still assigned. An
+ * assigned event means that it is sitting on the todo or working list
+ * of a group.
+ *
+ * Returns the number assigned count.
+ */
+static int event_assigned(struct dazukofs_event *event)
+{
+	int val;
+	mutex_lock(&event->assigned_mutex);
+	val = event->assigned;
+	mutex_unlock(&event->assigned_mutex);
+	return val;
+}
+
+/**
+ * check_access_precheck - check if an access event should be generated
+ *
+ * Description: Check if the current process should cause an access event
+ * to be generated.
+ *
+ * Returns 0 if an access event should be generated.
+ */
+static int check_access_precheck(void)
+{
+	/* am I a recursion process? */
+	if (!check_recursion())
+		return -1;
+
+	return 0;
+}
+
+/**
+ * assign_event_to_group - post an event to be processed
+ * @evt: the event to be posted
+ * @ec: the container for the event
+ *
+ * Description: This function will assign a unique id to the event.
+ * The event will be associated with its container and placed on the
+ * group's todo list. The group will also be woken to handle the new
+ * event.
+ */
+static void
+assign_event_to_group(struct dazukofs_event *evt,
+		      struct dazukofs_event_container *ec) {
+	struct dazukofs_group *grp = &reg_group;
+
+	mutex_lock(&work_mutex);
+	mutex_lock(&evt->assigned_mutex);
+
+	/* assign the event a "unique" id */
+
+	last_event_id++;
+	evt->event_id = last_event_id;
+
+	ec->event = evt;
+	evt->assigned = 1;
+	list_add_tail(&ec->list, &grp->todo_list.list);
+
+	/* notify someone to handle the event */
+	wake_up(&grp->queue);
+
+	mutex_unlock(&evt->assigned_mutex);
+	mutex_unlock(&work_mutex);
+}
+
+/**
+ * allocate_event_and_container - allocate an event and event container
+ * @evt: event pointer to be assigned a new event
+ * @ec: event container to be assigned a new container
+ *
+ * Description: New event and event container structures are allocated
+ * and initialized.
+ *
+ * Returns 0 on success.
+ */
+static int allocate_event_and_container(struct dazukofs_event **evt,
+					struct dazukofs_event_container **ec)
+{
+	*evt = kmem_cache_zalloc(dazukofs_event_cachep, GFP_KERNEL);
+	if (!*evt)
+		return -1;
+	init_waitqueue_head(&(*evt)->queue);
+	mutex_init(&(*evt)->assigned_mutex);
+
+	/* allocate container now while we don't have a lock */
+	*ec = kmem_cache_zalloc(dazukofs_event_container_cachep, GFP_KERNEL);
+	if (!*ec)
+		goto error_out;
+
+	return 0;
+
+error_out:
+	kmem_cache_free(dazukofs_event_cachep, *evt);
+	*evt = NULL;
+	return -1;
+}
+
+/**
+ * dazukofs_check_access - check for allowed file access
+ * @dentry: the dentry associated with the file access
+ * @mnt: the vfsmount associated with the file access
+ *
+ * Description: This is the only function used by the stackable filesystem
+ * layer to check if a file may be accessed.
+ *
+ * Returns 0 if the file access is allowed.
+ */
+int dazukofs_check_access(struct dentry *dentry, struct vfsmount *mnt)
+{
+	struct dazukofs_event_container *ec;
+	struct dazukofs_event *evt;
+	int err;
+
+	if (check_access_precheck())
+		return 0;
+
+	/* at this point, the access should be handled */
+
+	if (allocate_event_and_container(&evt, &ec)) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	evt->dentry = dget(dentry);
+	evt->mnt = mntget(mnt);
+	evt->pid = current->pid;
+
+	assign_event_to_group(evt, ec);
+
+	/* wait until event completely processed or signal */
+	err = wait_event_freezable(evt->queue, event_assigned(evt) == 0);
+
+	if (evt->deny)
+		err = -EPERM;
+
+	release_event(evt, 0, 0);
+out:
+	return err;
+}
+
+/**
+ * dazukofs_return_event - return checked file access results
+ * @event_id: the id of the event
+ * @deny: a flag indicating if file access should be denied
+ *
+ * Description: This function is called by the device layer when returning
+ * results from a checked file access event. If the event_id was valid, the
+ * event container will be freed and the event released.
+ *
+ * Returns 0 on success.
+ */
+int dazukofs_return_event(unsigned long event_id, int deny)
+{
+	struct dazukofs_group *grp = &reg_group;
+	struct dazukofs_event_container *ec;
+	struct dazukofs_event *evt = NULL;
+	struct list_head *pos;
+	int found = 0;
+	int ret = 0;
+
+	mutex_lock(&work_mutex);
+	list_for_each(pos, &grp->working_list.list) {
+		ec = list_entry(pos, struct dazukofs_event_container, list);
+		evt = ec->event;
+		if (evt->event_id == event_id) {
+			found = 1;
+			list_del(pos);
+			kmem_cache_free(dazukofs_event_container_cachep, ec);
+			break;
+		}
+	}
+	mutex_unlock(&work_mutex);
+
+	if (found)
+		release_event(evt, 1, deny);
+	else
+		ret = -EFAULT;
+	return ret;
+}
+
+/**
+ * unclaim_event - return an event to the todo list
+ * @grp: group to which the event is assigned
+ * @ec: event container of the event to be returned
+ *
+ * Description: This function puts the given event container on the todo
+ * list and wake the group.
+ */
+static void unclaim_event(struct dazukofs_group *grp,
+			  struct dazukofs_event_container *ec)
+{
+	/* put the event on the todo list */
+	mutex_lock(&work_mutex);
+	list_add(&ec->list, &grp->todo_list.list);
+	mutex_unlock(&work_mutex);
+
+	/* wake up someone else to handle the event */
+	wake_up(&grp->queue);
+}
+
+/**
+ * claim_event - grab an event from the todo list
+ * @grp: the group
+ *
+ * Description: Take the first event from the todo list and move it to the
+ * working list. The event is then returned to its called for processing.
+ *
+ * Returns the claimed event.
+ */
+static struct dazukofs_event_container *claim_event(struct dazukofs_group *grp)
+{
+	struct dazukofs_event_container *ec = NULL;
+
+	/* move first todo-item to working list */
+	mutex_lock(&work_mutex);
+	if (!list_empty(&grp->todo_list.list)) {
+		ec = list_first_entry(&grp->todo_list.list,
+				      struct dazukofs_event_container, list);
+		list_del(&ec->list);
+		list_add(&ec->list, &grp->working_list.list);
+	}
+	mutex_unlock(&work_mutex);
+
+	return ec;
+}
+
+/**
+ * mask_proc - mask the current process
+ * @proc: process structure to use for the list
+ *
+ * Description: Assign the current process to the provided proc structure
+ * and add the structure to the list. The list is used to prevent
+ * generating recursive file access events. The process is removed from
+ * the list with the check_recursion() function.
+ */
+static void mask_proc(struct dazukofs_proc *proc)
+{
+	proc->curr = current;
+	mutex_lock(&proc_mutex);
+	list_add(&proc->list, &proc_list.list);
+	mutex_unlock(&proc_mutex);
+}
+
+/**
+ * open_file - open a file for the current process (avoiding recursion)
+ * @ec: event container to store opened file descriptor
+ *
+ * Description: This function will open a file using the information within
+ * the provided event container. The calling process will be temporarily
+ * masked so that the file open does not generate a file access event.
+ *
+ * Returns 0 on success.
+ */
+static int open_file(struct dazukofs_event_container *ec)
+{
+	struct dazukofs_event *evt = ec->event;
+	struct dazukofs_proc proc;
+	int ret;
+
+	/* open the file read-only */
+
+	ec->fd = get_unused_fd();
+	if (ec->fd < 0) {
+		ret = ec->fd;
+		goto error_out1;
+	}
+
+	/* add myself to be ignored on file open (to avoid recursion) */
+	mask_proc(&proc);
+
+	ec->file = dentry_open(dget(evt->dentry), mntget(evt->mnt),
+			       O_RDONLY, current_cred());
+	if (IS_ERR(ec->file)) {
+		check_recursion();  /* remove myself from proc_list */
+		ret = PTR_ERR(ec->file);
+		goto error_out2;
+	}
+
+	fd_install(ec->fd, ec->file);
+
+	return 0;
+
+error_out2:
+	put_unused_fd(ec->fd);
+error_out1:
+	return ret;
+}
+
+/**
+ * is_event_available - check if an event is available for processing
+ * @grp: the group
+ *
+ * Description: This function simply checks if there are any events posted
+ * in the group's todo list.
+ *
+ * Returns 0 if there are no events in the todo list.
+ */
+static int is_event_available(struct dazukofs_group *grp)
+{
+	int ret = 0;
+
+	mutex_lock(&work_mutex);
+	if (!list_empty(&grp->todo_list.list))
+		ret = 1;
+	mutex_unlock(&work_mutex);
+
+	return ret;
+}
+
+/**
+ * dazukofs_get_event - get an event to process
+ * @event_id: to be filled in with the new event id
+ * @fd: to be filled in with the opened file descriptor
+ * @pid: to be filled in with the pid of the process generating the event
+ *
+ * Description: This function is called by the device layer to get a new
+ * file access event to process. It waits until an event has been
+ * posted in the todo list (and is successfully claimed by this process).
+ *
+ * Returns 0 on success.
+ */
+int dazukofs_get_event(unsigned long *event_id, int *fd, pid_t *pid)
+{
+	struct dazukofs_group *grp = &reg_group;
+	struct dazukofs_event_container *ec;
+	int ret = 0;
+
+	while (1) {
+		ret = wait_event_freezable(grp->queue,
+					   is_event_available(grp));
+		if (ret != 0)
+			break;
+
+		ec = claim_event(grp);
+		if (ec) {
+			ret = open_file(ec);
+			if (ret == 0) {
+				*event_id = ec->event->event_id;
+				*fd = ec->fd;
+				*pid = ec->event->pid;
+				break;
+			} else {
+				unclaim_event(grp, ec);
+			}
+		}
+	}
+	return ret;
+}
Index: linux-2.6.28/fs/dazukofs/dev.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.28/fs/dazukofs/dev.h	2009-02-03 18:10:46.000000000 +0100
@@ -0,0 +1,37 @@
+/* dazukofs: access control stackable filesystem
+
+   Copyright (C) 2008-2009 John Ogness
+     Author: John Ogness <dazukocode@ogness.net>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*/
+
+#ifndef __DEV_H
+#define __DEV_H
+
+#include <linux/device.h>
+
+#define DEVICE_NAME	"dazukofs"
+
+extern int dazukofs_dev_init(void);
+extern void dazukofs_dev_destroy(void);
+
+extern int dazukofs_group_dev_init(int dev_major, int dev_minor_start,
+				   struct class *dazukofs_class);
+extern void dazukofs_group_dev_destroy(int dev_major, int dev_minor_start,
+				       int dev_minor_end,
+				       struct class *dazukofs_class);
+
+#endif /* __DEV_H */
Index: linux-2.6.28/fs/dazukofs/Makefile
===================================================================
--- linux-2.6.28.orig/fs/dazukofs/Makefile	2009-02-03 18:07:44.000000000 +0100
+++ linux-2.6.28/fs/dazukofs/Makefile	2009-02-03 18:10:46.000000000 +0100
@@ -4,4 +4,5 @@
 
 obj-$(CONFIG_DAZUKOFS_FS) += dazukofs.o
 
-dazukofs-objs := super.o inode.o file.o dentry.o mmap.o
+dazukofs-objs := super.o inode.o file.o dentry.o mmap.o event.o \
+		 dev.o group_dev.o
Index: linux-2.6.28/fs/dazukofs/super.c
===================================================================
--- linux-2.6.28.orig/fs/dazukofs/super.c	2009-02-03 18:07:52.000000000 +0100
+++ linux-2.6.28/fs/dazukofs/super.c	2009-02-03 18:10:46.000000000 +0100
@@ -28,6 +28,7 @@
 #include <linux/mount.h>
 
 #include "dazukofs_fs.h"
+#include "dev.h"
 
 static struct kmem_cache *dazukofs_inode_info_cachep;
 static struct kmem_cache *dazukofs_sb_info_cachep;
@@ -304,19 +305,25 @@
 {
 	int err;
 
-	err = init_caches();
+	err = dazukofs_dev_init();
 	if (err)
 		goto error_out1;
 
-	err = register_filesystem(&dazukofs_fs_type);
+	err = init_caches();
 	if (err)
 		goto error_out2;
 
+	err = register_filesystem(&dazukofs_fs_type);
+	if (err)
+		goto error_out3;
+
 	printk(KERN_INFO "dazukofs: loaded\n");
 	return 0;
 
-error_out2:
+error_out3:
 	destroy_caches();
+error_out2:
+	dazukofs_dev_destroy();
 error_out1:
 	return err;
 }
@@ -325,11 +332,12 @@
 {
 	unregister_filesystem(&dazukofs_fs_type);
 	destroy_caches();
+	dazukofs_dev_destroy();
 	printk(KERN_INFO "dazukofs: unloaded\n");
 }
 
 MODULE_AUTHOR("John Ogness");
-MODULE_DESCRIPTION("pass-through stackable filesystem");
+MODULE_DESCRIPTION("access control stackable filesystem");
 MODULE_LICENSE("GPL");
 module_init(init_dazukofs_fs)
 module_exit(exit_dazukofs_fs)
Index: linux-2.6.28/fs/dazukofs/file.c
===================================================================
--- linux-2.6.28.orig/fs/dazukofs/file.c	2009-02-03 18:08:42.000000000 +0100
+++ linux-2.6.28/fs/dazukofs/file.c	2009-02-03 18:10:46.000000000 +0100
@@ -29,6 +29,7 @@
 #include <linux/cred.h>
 
 #include "dazukofs_fs.h"
+#include "event.h"
 
 /**
  * Description: Called when the VFS needs to move the file position index.
@@ -167,7 +168,11 @@
 	struct dentry *lower_dentry = dget(get_lower_dentry(dentry));
 	struct vfsmount *lower_mnt = mntget(get_lower_mnt(dentry));
 	struct file *lower_file;
-	int err = 0;
+	int err;
+
+	err = dazukofs_check_access(file->f_dentry, file->f_vfsmnt);
+	if (err)
+		goto error_out1;
 
 	set_file_private(file, kmem_cache_zalloc(dazukofs_file_info_cachep,
 						 GFP_KERNEL));
Index: linux-2.6.28/fs/dazukofs/event.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.28/fs/dazukofs/event.h	2009-02-03 18:10:46.000000000 +0100
@@ -0,0 +1,32 @@
+/* dazukofs: access control stackable filesystem
+
+   Copyright (C) 2008 John Ogness
+     Author: John Ogness <dazukocode@ogness.net>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*/
+
+#ifndef __EVENT_H
+#define __EVENT_H
+
+extern int dazukofs_init_events(void);
+extern void dazukofs_destroy_events(void);
+
+extern int dazukofs_get_event(unsigned long *event_id, int *fd, pid_t *pid);
+extern int dazukofs_return_event(unsigned long event_id, int deny);
+
+extern int dazukofs_check_access(struct dentry *dentry, struct vfsmount *mnt);
+
+#endif /* __EVENT_H */
Index: linux-2.6.28/fs/dazukofs/Kconfig
===================================================================
--- linux-2.6.28.orig/fs/dazukofs/Kconfig	2009-02-03 18:07:44.000000000 +0100
+++ linux-2.6.28/fs/dazukofs/Kconfig	2009-02-03 18:10:46.000000000 +0100
@@ -2,7 +2,8 @@
 	tristate "DazukoFS filesystem layer support (EXPERIMENTAL)"
 	depends on EXPERIMENTAL
 	help
-	  A pass-through stackable filesystem (also referred to as nullfs).
+	  A stackable filesystem to allow userspace applications to perform
+	  online file access control.
 	  See <file:Documentation/filesystems/dazukofs.txt> to learn more
 	  about DazukoFS.
 
Index: linux-2.6.28/Documentation/filesystems/dazukofs.txt
===================================================================
--- linux-2.6.28.orig/Documentation/filesystems/dazukofs.txt	2009-02-03 18:07:44.000000000 +0100
+++ linux-2.6.28/Documentation/filesystems/dazukofs.txt	2009-02-03 18:10:46.000000000 +0100
@@ -2,9 +2,10 @@
  ABOUT DAZUKOFS
 ================
 
-DazukoFS is a pass-through stackable filesystem. A filesystem that does
-not perform any special modification but simply passes VFS calls to and
-from the lower filesystem is typically known as nullfs.
+DazukoFS is a stackable filesystem to allow userspace applications to
+perform online file access control. It was originally developed to
+support online virus scanners, but could be useful for any application
+that wishes to perform online file access control.
 
 
 
@@ -19,7 +20,19 @@
 # mount -t dazukofs /opt /opt
 
 A process that accesses files in /opt will now be accessing them through
-DazukoFS. The stackable filesystem can then be unmounted with:
+DazukoFS. Such file access events will be detected by a process that is
+registered with DazukoFS. That process can then choose to allow or deny
+the access.
+
+IMPORTANT: DazukoFS does not allow file access events unless a registered
+           application has approved them. This means that no file access
+           can take place on a DazukoFS mount until an application has
+           registered to perform file access control with DazukoFS.
+           Attempts to access files on a DazukoFS mount will result in the
+           process blocking until a registered application has approved
+           the access.
+
+The stackable filesystem can then be unmounted with:
 
 # umount /opt
 
@@ -56,10 +69,11 @@
 If files are modified directly in /opt, the DazukoFS layer will not know
 about it. When DazukoFS later tries to access those files, it may result
 in corrupt data or kernel crashes. As long as /opt is modified ONLY through
-DazukoFS, there should not be any problems.
+DazukoFS (i.e. through /mnt), there should not be any problems.
 
 This method of mounting DazukoFS may be interesting for servers that export
-a part of the filesystem and the service is in a chroot environment.
+a part of the filesystem and the exporting service is within a chroot
+environment.
 
 
 
@@ -79,3 +93,66 @@
 Please report problems to the dazuko-devel mailing list
 (subscription required):
      http://lists.nongnu.org/mailman/listinfo/dazuko-devel
+
+
+
+=======================
+ DAZUKOFS APPLICATIONS
+=======================
+
+This last section is meant for developers of DazukoFS applications. This
+section will discuss the methods used for interacting with DazukoFS in
+order to perform online file access control.
+
+Although this section describes the low-level communication between
+application and kernel, be aware that a userspace library libdazukofs
+exists that has already implemented this communication. Using the
+library makes it very easy to write applications for DazukoFS. The
+library can be found on the Dazuko website: http://www.dazuko.org
+
+An application can register itself to receive notification about DazukoFS
+file access events. The application then also has the authority to block
+the file access.
+
+For each file access event, only one of the registered applications will
+be notified.
+
+By opening the device /dev/dazukofs.0 an application has registered itself.
+A read on the device will block until a file access event on DazukoFS has
+taken place. When a file access event has occured, the read will return with
+information about the file access event. For example:
+
+id=11
+fd=4
+pid=3226
+
+This means that the particular file access event has been given the id 11.
+The file descriptor 4 has been opened for the registered process. This file
+descriptor allows the registered process read-only access to the file being
+accessed. The pid of the accessing process is 3226.
+
+Using this information, the registered application must determine if the
+access should be denied or allowed. The application must then respond with
+an answer. This is done by writing to the device:
+
+id=11 r=0
+
+"r" is the response. A value of 0 means to allow the access. A value of 1
+means to deny the access.
+
+IMPORTANT: The application is responsible for closing the file descriptor
+           that was opened by DazukoFS.
+
+Since DazukoFS will open the file being accessed, the registered process
+only requires read/write permissions to the device in order to perform
+online file access control. The file is opened even if the registered
+application normally would not have access to the file. This allows an
+unprivileged process to perform file access control for any file on the
+system.
+
+By closing the device, the application will unregister itself.
+
+NOTE: It is not necessary for the device to be open while the application
+      decides if access should be allowed. In fact, it doesn't even have to
+      be the same process that responds. DazukoFS is only interested in a
+      response for the given event id.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCHv2 3/5] VFS: DazukoFS, stackable-fs, file access control
  2009-02-03 19:17   ` [PATCHv2 2/5] " John Ogness
@ 2009-02-03 19:18     ` John Ogness
  2009-02-03 19:19       ` [PATCHv2 4/5] " John Ogness
                         ` (2 more replies)
  2009-02-12 16:00     ` [PATCHv2 2/5] " Jan Engelhardt
  2009-02-12 20:14     ` Eric W. Biederman
  2 siblings, 3 replies; 24+ messages in thread
From: John Ogness @ 2009-02-03 19:18 UTC (permalink / raw)
  To: linux-kernel; +Cc: viro, malware-list, eparis, hch, alan

Patch 3: Creates /dev/dazukofs.ctrl to allow for groups to be added,
         listed, and deleted. Also, /dev/dazukofs.[0-9] devices are
         created to support multiple groups.

Patched against 2.6.29-rc3.

Signed-off-by: John Ogness <dazukocode@ogness.net>
---
 Documentation/filesystems/dazukofs.txt |   73 +++
 fs/dazukofs/Makefile                   |    2 
 fs/dazukofs/ctrl_dev.c                 |  198 ++++++++++
 fs/dazukofs/dev.c                      |   18 
 fs/dazukofs/dev.h                      |    6 
 fs/dazukofs/event.c                    |  445 ++++++++++++++++++++---
 fs/dazukofs/event.h                    |   10 
 fs/dazukofs/group_dev.c                |  107 ++++-
 8 files changed, 775 insertions(+), 84 deletions(-)
Index: linux-2.6.28/fs/dazukofs/event.c
===================================================================
--- linux-2.6.28.orig/fs/dazukofs/event.c	2009-02-03 18:10:46.000000000 +0100
+++ linux-2.6.28/fs/dazukofs/event.c	2009-02-03 18:11:01.000000000 +0100
@@ -26,6 +26,7 @@
 #include <linux/freezer.h>
 #include <linux/cred.h>
 
+#include "dev.h"
 #include "dazukofs_fs.h"
 
 struct dazukofs_proc {
@@ -56,20 +57,31 @@
 };
 
 struct dazukofs_group {
+	struct list_head list;
+	char *name;
+	size_t name_length;
+	unsigned long group_id;
 	struct dazukofs_event_container todo_list;
 	wait_queue_head_t queue;
 	struct dazukofs_event_container working_list;
+	atomic_t use_count;
+	int deprecated;
 };
 
-static struct dazukofs_group reg_group;
+static struct dazukofs_group group_list;
+static int group_count;
+
+/* protects: group_list, group_count */
+static rwlock_t group_count_rwlock;
 
-/* protects: grp->members, last_event_id,
+/* protects: group_list, grp->members, last_event_id,
  *	     todo_list, working_list */
 static struct mutex work_mutex;
 
 static struct mutex proc_mutex;
 static struct dazukofs_proc proc_list;
 
+static struct kmem_cache *dazukofs_group_cachep;
 static struct kmem_cache *dazukofs_event_container_cachep;
 static struct kmem_cache *dazukofs_event_cachep;
 
@@ -87,11 +99,17 @@
 {
 	mutex_init(&proc_mutex);
 	mutex_init(&work_mutex);
+	rwlock_init(&group_count_rwlock);
+
 	INIT_LIST_HEAD(&proc_list.list);
+	INIT_LIST_HEAD(&group_list.list);
 
-	init_waitqueue_head(&reg_group.queue);
-	INIT_LIST_HEAD(&reg_group.todo_list.list);
-	INIT_LIST_HEAD(&reg_group.working_list.list);
+	dazukofs_group_cachep =
+		kmem_cache_create("dazukofs_group_cache",
+				  sizeof(struct dazukofs_group), 0,
+				  SLAB_HWCACHE_ALIGN, NULL);
+	if (!dazukofs_group_cachep)
+		goto error_out;
 
 	dazukofs_event_container_cachep =
 		kmem_cache_create("dazukofs_event_container_cache",
@@ -110,6 +128,8 @@
 	return 0;
 
 error_out:
+	if (dazukofs_group_cachep)
+		kmem_cache_destroy(dazukofs_group_cachep);
 	if (dazukofs_event_container_cachep)
 		kmem_cache_destroy(dazukofs_event_container_cachep);
 	if (dazukofs_event_cachep)
@@ -211,10 +231,17 @@
  * Description: All pending and in-progress events are released/freed.
  * Any processes waiting on the queue are woken.
  *
+ * The actual group structure is not deleted, but rather marked as
+ * deprecated. Deprecated group structures are deleted as new
+ * groups are added.
+ *
  * IMPORTANT: This function requires work_mutex to be held!
  */
 static void __remove_group(struct dazukofs_group *grp)
 {
+	grp->deprecated = 1;
+	group_count--;
+
 	__clear_group_event_list(&grp->working_list.list);
 	__clear_group_event_list(&grp->todo_list.list);
 
@@ -229,17 +256,272 @@
  */
 void dazukofs_destroy_events(void)
 {
-	/* free the group items */
-	mutex_lock(&work_mutex);
-	__remove_group(&reg_group);
-	mutex_unlock(&work_mutex);
+	struct dazukofs_group *grp;
+	struct list_head *pos;
+	struct list_head *q;
+
+	/*
+	 * We are not using any locks here because we assume
+	 * everything else has been already cleaned up by
+	 * the device layer.
+	 */
+
+	/* free the groups */
+	list_for_each_safe(pos, q, &group_list.list) {
+		grp = list_entry(pos, struct dazukofs_group, list);
+		list_del(pos);
+
+		__remove_group(grp);
+
+		/* free group name */
+		kfree(grp->name);
+
+		/* free group */
+		kmem_cache_free(dazukofs_group_cachep, grp);
+	}
 
 	/* free everything else */
+	kmem_cache_destroy(dazukofs_group_cachep);
 	kmem_cache_destroy(dazukofs_event_container_cachep);
 	kmem_cache_destroy(dazukofs_event_cachep);
 }
 
 /**
+ * __check_for_group - check if a group exists
+ * @name: a group name to check for
+ * @id: a group id to check for
+ * @already_exists: will be set if the group already exists
+ *
+ * Description: This function checks names and id's to see if a group may
+ * be created. If the id already exists, but with a different group name,
+ * the group cannot be created. If the group name exists, but with a
+ * different id, the group cannot be created.
+ *
+ * If the group name exists and the id is already that which is requested,
+ * the function returns success, but sets the already_exists flag.
+ *
+ * IMPORTANT: This function requires work_mutex to be held!
+ *
+ * Returns 0 if the group exists or may be created.
+ */
+static int __check_for_group(const char *name, int id, int *already_exists)
+{
+	struct dazukofs_group *grp;
+	struct list_head *pos;
+	struct list_head *q;
+	int id_available = 1;
+
+	*already_exists = 0;
+
+	list_for_each_safe(pos, q, &group_list.list) {
+		grp = list_entry(pos, struct dazukofs_group, list);
+		if (grp->deprecated) {
+			/* cleanup deprecated groups */
+			if (atomic_read(&grp->use_count) == 0) {
+				list_del(pos);
+				kfree(grp->name);
+				kmem_cache_free(dazukofs_group_cachep, grp);
+			}
+		} else {
+			if (strcmp(name, grp->name) == 0) {
+				*already_exists = 1;
+				break;
+			} else if (grp->group_id == id) {
+				id_available = 0;
+				break;
+			}
+		}
+	}
+
+	if (*already_exists)
+		return 0;
+
+	if (id_available) {
+		/* we have found a free id */
+		return 0;
+	}
+
+	return -1;
+}
+
+/**
+ * __create_group - allocate and initialize a group structure
+ * @name: the name of the new group
+ * @id: the id of the new group
+ *
+ * Description: This function allocates and initializes a group
+ * structure. The group_count should be locked to ensure that
+ * the group id remains available until the group can be
+ * added to the group list.
+ *
+ * Returns the newly created and initialized group structure.
+ */
+static struct dazukofs_group *__create_group(const char *name, int id)
+{
+	struct dazukofs_group *grp;
+
+	grp = kmem_cache_zalloc(dazukofs_group_cachep, GFP_KERNEL);
+	if (!grp)
+		return NULL;
+
+	atomic_set(&grp->use_count, 0);
+	grp->group_id = id;
+	grp->name = kstrdup(name, GFP_KERNEL);
+	if (!grp->name) {
+		kmem_cache_free(dazukofs_group_cachep, grp);
+		return NULL;
+	}
+	grp->name_length = strlen(name);
+	init_waitqueue_head(&grp->queue);
+	INIT_LIST_HEAD(&grp->todo_list.list);
+	INIT_LIST_HEAD(&grp->working_list.list);
+	return grp;
+}
+
+/**
+ * dazukofs_add_group - add a new group
+ * @name: the name of the group to add
+ *
+ * Description: This function is called by the device layer to add a new
+ * group. It returns success if the group has been successfully created
+ * or if the group already exists.
+ *
+ * Returns 0 on success.
+ */
+int dazukofs_add_group(const char *name)
+{
+	int ret = 0;
+	int already_exists;
+	int available_id = 0;
+	struct dazukofs_group *grp;
+
+	write_lock(&group_count_rwlock);
+
+	mutex_lock(&work_mutex);
+	while (__check_for_group(name, available_id, &already_exists) != 0) {
+		/* try again with the next id */
+		available_id++;
+	}
+	mutex_unlock(&work_mutex);
+
+	if (already_exists)
+		goto out;
+
+	/* if we are here, the group doesn't already exist */
+
+	/* do we have room for a new group? */
+	if (group_count == GROUP_COUNT) {
+		ret = -EPERM;
+		goto out;
+	}
+
+	grp = __create_group(name, available_id);
+	if (!grp) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	mutex_lock(&work_mutex);
+	list_add_tail(&grp->list, &group_list.list);
+	mutex_unlock(&work_mutex);
+
+	group_count++;
+out:
+	write_unlock(&group_count_rwlock);
+	return ret;
+}
+
+/**
+ * dazukofs_remove_group - remove a group
+ * @name: the name of the group to remove
+ *
+ * Description: This function is called by the device layer to remove a
+ * group. It returns success if the group has been deleted or the group
+ * does not exist.
+ *
+ * Returns 0 on success.
+ */
+int dazukofs_remove_group(const char *name)
+{
+	int ret = 0;
+	struct dazukofs_group *grp;
+	struct list_head *pos;
+
+	write_lock(&group_count_rwlock);
+
+	if (group_count == 0)
+		goto out;
+
+	mutex_lock(&work_mutex);
+	/* set group deprecated */
+	list_for_each(pos, &group_list.list) {
+		grp = list_entry(pos, struct dazukofs_group, list);
+		if (!grp->deprecated && strcmp(name, grp->name) == 0) {
+			__remove_group(grp);
+			break;
+		}
+	}
+	mutex_unlock(&work_mutex);
+out:
+	write_unlock(&group_count_rwlock);
+	return ret;
+}
+
+/**
+ * dazukofs_get_groups - get the names and id's of active groups as strings
+ * @buf: to be assigned the list of groups as a single printable string
+ *
+ * Description: This function will allocate a string that includes all the
+ * active (not deprecated) groups and their id's. This function is called
+ * by the device layer for presenting userspace with the list of groups.
+ *
+ * This function will allocate memory that must be freed by the caller.
+ *
+ * Returns 0 on success.
+ */
+int dazukofs_get_groups(char **buf)
+{
+	struct dazukofs_group *grp;
+	char *tmp;
+	struct list_head *pos;
+	size_t buflen;
+	size_t allocsize = 256;
+
+tryagain:
+	*buf = kzalloc(allocsize, GFP_KERNEL);
+	if (!*buf)
+		return -ENOMEM;
+	tmp = *buf;
+	buflen = 1;
+
+	mutex_lock(&work_mutex);
+	list_for_each(pos, &group_list.list) {
+		grp = list_entry(pos, struct dazukofs_group, list);
+		if (!grp->deprecated)
+			buflen += grp->name_length + 3;
+	}
+	if (buflen < allocsize) {
+		list_for_each(pos, &group_list.list) {
+			grp = list_entry(pos, struct dazukofs_group, list);
+			if (!grp->deprecated) {
+				snprintf(tmp, (allocsize - 1) - (tmp - *buf),
+					 "%lu:%s\n", grp->group_id,
+					 grp->name);
+				tmp += grp->name_length + 3;
+			}
+		}
+		mutex_unlock(&work_mutex);
+	} else {
+		mutex_unlock(&work_mutex);
+		allocsize *= 2;
+		kfree(*buf);
+		goto tryagain;
+	}
+
+	return 0;
+}
+
+/**
  * check_recursion - check if current process is recursing
  *
  * Description: A list of anonymous processes is managed to prevent
@@ -296,14 +578,19 @@
 
 /**
  * check_access_precheck - check if an access event should be generated
+ * @grp_count: the current number of groups
  *
  * Description: Check if the current process should cause an access event
  * to be generated.
  *
  * Returns 0 if an access event should be generated.
  */
-static int check_access_precheck(void)
+static int check_access_precheck(int grp_count)
 {
+	/* do we have any groups? */
+	if (grp_count == 0)
+		return -1;
+
 	/* am I a recursion process? */
 	if (!check_recursion())
 		return -1;
@@ -312,19 +599,22 @@
 }
 
 /**
- * assign_event_to_group - post an event to be processed
+ * assign_event_to_groups - post an event to be processed
  * @evt: the event to be posted
- * @ec: the container for the event
+ * @ec_array: the containers for the event
  *
  * Description: This function will assign a unique id to the event.
- * The event will be associated with its container and placed on the
- * group's todo list. The group will also be woken to handle the new
- * event.
+ * The event will be associated with each container and the container is
+ * placed on each group's todo list. Each group will also be woken to
+ * handle the new event.
  */
 static void
-assign_event_to_group(struct dazukofs_event *evt,
-		      struct dazukofs_event_container *ec) {
-	struct dazukofs_group *grp = &reg_group;
+assign_event_to_groups(struct dazukofs_event *evt,
+		       struct dazukofs_event_container *ec_array[])
+{
+	struct dazukofs_group *grp;
+	struct list_head *pos;
+	int i;
 
 	mutex_lock(&work_mutex);
 	mutex_lock(&evt->assigned_mutex);
@@ -334,44 +624,67 @@
 	last_event_id++;
 	evt->event_id = last_event_id;
 
-	ec->event = evt;
-	evt->assigned = 1;
-	list_add_tail(&ec->list, &grp->todo_list.list);
+	/* assign the event to each group */
+	i = 0;
+	list_for_each(pos, &group_list.list) {
+		grp = list_entry(pos, struct dazukofs_group, list);
+		if (!grp->deprecated) {
+			ec_array[i]->event = evt;
+
+			evt->assigned++;
+			list_add_tail(&ec_array[i]->list,
+				      &grp->todo_list.list);
 
-	/* notify someone to handle the event */
-	wake_up(&grp->queue);
+			/* notify someone to handle the event */
+			wake_up(&grp->queue);
+
+			i++;
+		}
+	}
 
 	mutex_unlock(&evt->assigned_mutex);
 	mutex_unlock(&work_mutex);
 }
 
 /**
- * allocate_event_and_container - allocate an event and event container
+ * allocate_event_and_containers - allocate an event and event containers
  * @evt: event pointer to be assigned a new event
- * @ec: event container to be assigned a new container
+ * @ec: event container array to be filled with new array of containers
+ * @grp_count: the number of groups (size of the array)
  *
  * Description: New event and event container structures are allocated
  * and initialized.
  *
  * Returns 0 on success.
  */
-static int allocate_event_and_container(struct dazukofs_event **evt,
-					struct dazukofs_event_container **ec)
+static int
+allocate_event_and_containers(struct dazukofs_event **evt,
+			      struct dazukofs_event_container *ec_array[],
+			      int grp_count)
 {
+	int i;
+
 	*evt = kmem_cache_zalloc(dazukofs_event_cachep, GFP_KERNEL);
 	if (!*evt)
 		return -1;
 	init_waitqueue_head(&(*evt)->queue);
 	mutex_init(&(*evt)->assigned_mutex);
 
-	/* allocate container now while we don't have a lock */
-	*ec = kmem_cache_zalloc(dazukofs_event_container_cachep, GFP_KERNEL);
-	if (!*ec)
-		goto error_out;
+	/* allocate containers now while we don't have a lock */
+	for (i = 0; i < grp_count; i++) {
+		ec_array[i] = kmem_cache_zalloc(
+				dazukofs_event_container_cachep, GFP_KERNEL);
+		if (!ec_array[i])
+			goto error_out;
+	}
 
 	return 0;
 
 error_out:
+	for (i--; i >= 0; i--) {
+		kmem_cache_free(dazukofs_event_container_cachep, ec_array[i]);
+		ec_array[i] = NULL;
+	}
 	kmem_cache_free(dazukofs_event_cachep, *evt);
 	*evt = NULL;
 	return -1;
@@ -389,16 +702,21 @@
  */
 int dazukofs_check_access(struct dentry *dentry, struct vfsmount *mnt)
 {
-	struct dazukofs_event_container *ec;
+	struct dazukofs_event_container *ec_array[GROUP_COUNT];
 	struct dazukofs_event *evt;
 	int err;
 
-	if (check_access_precheck())
+	read_lock(&group_count_rwlock);
+
+	if (check_access_precheck(group_count)) {
+		read_unlock(&group_count_rwlock);
 		return 0;
+	}
 
 	/* at this point, the access should be handled */
 
-	if (allocate_event_and_container(&evt, &ec)) {
+	if (allocate_event_and_containers(&evt, ec_array, group_count)) {
+		read_unlock(&group_count_rwlock);
 		err = -ENOMEM;
 		goto out;
 	}
@@ -407,7 +725,9 @@
 	evt->mnt = mntget(mnt);
 	evt->pid = current->pid;
 
-	assign_event_to_group(evt, ec);
+	assign_event_to_groups(evt, ec_array);
+
+	read_unlock(&group_count_rwlock);
 
 	/* wait until event completely processed or signal */
 	err = wait_event_freezable(evt->queue, event_assigned(evt) == 0);
@@ -422,6 +742,7 @@
 
 /**
  * dazukofs_return_event - return checked file access results
+ * @group_id: id of the group the event came from
  * @event_id: the id of the event
  * @deny: a flag indicating if file access should be denied
  *
@@ -431,9 +752,10 @@
  *
  * Returns 0 on success.
  */
-int dazukofs_return_event(unsigned long event_id, int deny)
+int dazukofs_return_event(unsigned long group_id, unsigned long event_id,
+			  int deny)
 {
-	struct dazukofs_group *grp = &reg_group;
+	struct dazukofs_group *grp;
 	struct dazukofs_event_container *ec;
 	struct dazukofs_event *evt = NULL;
 	struct list_head *pos;
@@ -441,6 +763,20 @@
 	int ret = 0;
 
 	mutex_lock(&work_mutex);
+	list_for_each(pos, &group_list.list) {
+		grp = list_entry(pos, struct dazukofs_group, list);
+		if (!grp->deprecated && grp->group_id == group_id) {
+			found = 1;
+			break;
+		}
+	}
+	if (!found) {
+		ret = -EFAULT;
+		mutex_unlock(&work_mutex);
+		goto out;
+	}
+
+	found = 0;
 	list_for_each(pos, &grp->working_list.list) {
 		ec = list_entry(pos, struct dazukofs_event_container, list);
 		evt = ec->event;
@@ -457,6 +793,7 @@
 		release_event(evt, 1, deny);
 	else
 		ret = -EFAULT;
+out:
 	return ret;
 }
 
@@ -591,6 +928,7 @@
 
 /**
  * dazukofs_get_event - get an event to process
+ * @group_id: id of the group we belong to
  * @event_id: to be filled in with the new event id
  * @fd: to be filled in with the opened file descriptor
  * @pid: to be filled in with the pid of the process generating the event
@@ -601,18 +939,43 @@
  *
  * Returns 0 on success.
  */
-int dazukofs_get_event(unsigned long *event_id, int *fd, pid_t *pid)
+int dazukofs_get_event(unsigned long group_id, unsigned long *event_id,
+		       int *fd, pid_t *pid)
 {
-	struct dazukofs_group *grp = &reg_group;
+	struct dazukofs_group *grp;
 	struct dazukofs_event_container *ec;
+	struct list_head *pos;
+	int found = 0;
 	int ret = 0;
 
+	mutex_lock(&work_mutex);
+	list_for_each(pos, &group_list.list) {
+		grp = list_entry(pos, struct dazukofs_group, list);
+		if (!grp->deprecated && grp->group_id == group_id) {
+			atomic_inc(&grp->use_count);
+			found = 1;
+			break;
+		}
+	}
+	mutex_unlock(&work_mutex);
+
+	if (!found) {
+		ret = -EFAULT;
+		goto out;
+	}
+
 	while (1) {
 		ret = wait_event_freezable(grp->queue,
-					   is_event_available(grp));
+					   is_event_available(grp) ||
+					   grp->deprecated);
 		if (ret != 0)
 			break;
 
+		if (grp->deprecated) {
+			ret = -EFAULT;
+			break;
+		}
+
 		ec = claim_event(grp);
 		if (ec) {
 			ret = open_file(ec);
@@ -626,5 +989,7 @@
 			}
 		}
 	}
+	atomic_dec(&grp->use_count);
+out:
 	return ret;
 }
Index: linux-2.6.28/fs/dazukofs/Makefile
===================================================================
--- linux-2.6.28.orig/fs/dazukofs/Makefile	2009-02-03 18:10:46.000000000 +0100
+++ linux-2.6.28/fs/dazukofs/Makefile	2009-02-03 18:11:01.000000000 +0100
@@ -5,4 +5,4 @@
 obj-$(CONFIG_DAZUKOFS_FS) += dazukofs.o
 
 dazukofs-objs := super.o inode.o file.o dentry.o mmap.o event.o \
-		 dev.o group_dev.o
+		 dev.o group_dev.o ctrl_dev.o
Index: linux-2.6.28/fs/dazukofs/ctrl_dev.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.28/fs/dazukofs/ctrl_dev.c	2009-02-03 18:11:01.000000000 +0100
@@ -0,0 +1,198 @@
+/* dazukofs: access control stackable filesystem
+
+   Copyright (C) 2008 John Ogness
+     Author: John Ogness <dazukocode@ogness.net>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*/
+
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+
+#include "event.h"
+#include "dev.h"
+
+static int dazukofs_ctrl_open(struct inode *inode, struct file *file)
+{
+	file->private_data = NULL;
+	return 0;
+}
+
+static int dazukofs_ctrl_release(struct inode *inode, struct file *file)
+{
+	/*
+	 * checkpatch.pl recommends not checking for NULL before freeing
+	 * the data because kfree(NULL) is allowed. However, that is
+	 * poor style and leads to sloppy programming.
+	 */
+
+	if (file->private_data)
+		kfree(file->private_data);
+
+	return 0;
+}
+
+static ssize_t dazukofs_ctrl_read(struct file *file, char __user *buffer,
+				  size_t length, loff_t *pos)
+{
+	char *buf = file->private_data;
+	size_t buflen;
+	int err;
+
+	if (!file->private_data) {
+		err = dazukofs_get_groups(&buf);
+		if (err)
+			return err;
+		file->private_data = buf;
+	}
+	buflen = strlen(buf);
+
+	if (*pos >= buflen)
+		return 0;
+
+	if (length > buflen - *pos)
+		length = buflen - *pos;
+
+	if (copy_to_user(buffer, buf + *pos, length))
+		return -EFAULT;
+
+	*pos += length;
+
+	return length;
+}
+
+#define DAZUKOFS_ALLOWED_GROUPCHARS \
+	"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-"
+static int is_valid_char(char c)
+{
+	if (strchr(DAZUKOFS_ALLOWED_GROUPCHARS, c) != NULL)
+		return 1;
+	return 0;
+}
+
+static int process_command(char *buf, const char *key,
+			   int (*func)(const char *),
+			   int *retcode)
+{
+	char *p;
+	char *p2;
+
+	p = strstr(buf, key);
+	if (!p)
+		return -1;
+
+	p += strlen(key);
+
+	for (p2 = p; is_valid_char(*p2); p2++)
+		;
+
+	if (p == p2) {
+		*retcode = -EINVAL;
+	} else {
+		*p2 = 0;
+		*retcode = func(p);
+		*p2 = ' ';
+	}
+
+	return 0;
+}
+
+static ssize_t dazukofs_ctrl_write(struct file *file,
+				   const char __user *buffer, size_t length,
+				   loff_t *pos)
+{
+#define DAZUKOFS_MAX_WRITE_BUFFER 32
+	char tmp[DAZUKOFS_MAX_WRITE_BUFFER];
+	int match = 0;
+	int ret = -EINVAL;
+	int cp_len = length;
+
+	if (cp_len >= DAZUKOFS_MAX_WRITE_BUFFER)
+		cp_len = DAZUKOFS_MAX_WRITE_BUFFER - 1;
+
+	if (copy_from_user(tmp, buffer, cp_len))
+		return -EFAULT;
+
+	tmp[cp_len] = 0;
+
+	if (!match || (match && ret >= 0)) {
+		if (process_command(tmp, "del=",
+				    dazukofs_remove_group, &ret) == 0) {
+			match = 1;
+		}
+	}
+
+	if (!match || (match && ret >= 0)) {
+		if (process_command(tmp, "add=",
+				    dazukofs_add_group, &ret) == 0) {
+			match = 1;
+		}
+	}
+
+	if (ret >= 0) {
+		*pos += length;
+		ret = length;
+	}
+
+	return ret;
+}
+
+static struct cdev ctrl_cdev;
+
+static const struct file_operations ctrl_fops = {
+	.owner		= THIS_MODULE,
+	.open		= dazukofs_ctrl_open,
+	.release	= dazukofs_ctrl_release,
+	.read		= dazukofs_ctrl_read,
+	.write		= dazukofs_ctrl_write,
+};
+
+int dazukofs_ctrl_dev_init(int dev_major, int dev_minor,
+			   struct class *dazukofs_class)
+{
+	int err = 0;
+	struct device *dev;
+
+	/* setup cdev for control */
+	cdev_init(&ctrl_cdev, &ctrl_fops);
+	ctrl_cdev.owner = THIS_MODULE;
+	err = cdev_add(&ctrl_cdev, MKDEV(dev_major, dev_minor), 1);
+	if (err)
+		goto error_out1;
+
+	/* create control device */
+	dev = device_create(dazukofs_class, NULL, MKDEV(dev_major, dev_minor),
+			    NULL, "%s.ctrl", DEVICE_NAME);
+	if (IS_ERR(dev)) {
+		err = PTR_ERR(dev);
+		goto error_out2;
+	}
+
+	return 0;
+
+error_out2:
+	cdev_del(&ctrl_cdev);
+error_out1:
+	return err;
+}
+
+void dazukofs_ctrl_dev_destroy(int dev_major, int dev_minor,
+			       struct class *dazukofs_class)
+{
+	device_destroy(dazukofs_class, MKDEV(dev_major, dev_minor));
+	cdev_del(&ctrl_cdev);
+}
Index: linux-2.6.28/fs/dazukofs/group_dev.c
===================================================================
--- linux-2.6.28.orig/fs/dazukofs/group_dev.c	2009-02-03 18:10:46.000000000 +0100
+++ linux-2.6.28/fs/dazukofs/group_dev.c	2009-02-03 18:11:01.000000000 +0100
@@ -27,7 +27,7 @@
 #include "event.h"
 #include "dev.h"
 
-static ssize_t dazukofs_group_read(struct file *file,
+static ssize_t dazukofs_group_read(int group_id, struct file *file,
 				   char __user *buffer, size_t length,
 				   loff_t *pos)
 {
@@ -45,7 +45,7 @@
 	if (length < DAZUKOFS_MIN_READ_BUFFER)
 		return -EINVAL;
 
-	err = dazukofs_get_event(&event_id, &fd, &pid);
+	err = dazukofs_get_event(group_id, &event_id, &fd, &pid);
 	if (err) {
 		if (err == -ERESTARTSYS)
 			return -EINTR;
@@ -65,7 +65,7 @@
 	return tmp_used;
 }
 
-static ssize_t dazukofs_group_write(struct file *file,
+static ssize_t dazukofs_group_write(int group_id, struct file *file,
 				    const char __user *buffer, size_t length,
 				    loff_t *pos)
 {
@@ -100,7 +100,7 @@
 		return -EINVAL;
 	response = (*(p + 2)) - '0';
 
-	ret = dazukofs_return_event(event_id, response);
+	ret = dazukofs_return_event(group_id, event_id, response);
 	if (ret == 0) {
 		*pos += length;
 		ret = length;
@@ -111,12 +111,50 @@
 	return ret;
 }
 
-static struct cdev group_cdev;
+#define DECLARE_GROUP_FOPS(group_id) \
+static ssize_t \
+dazukofs_group_read_##group_id(struct file *file, char __user *buffer, \
+			       size_t length, loff_t *pos) \
+{ \
+	return dazukofs_group_read(group_id, file, buffer, length, pos); \
+} \
+static ssize_t \
+dazukofs_group_write_##group_id(struct file *file, \
+				const char __user *buffer, size_t length, \
+				loff_t *pos) \
+{ \
+	return dazukofs_group_write(group_id, file, buffer, length, pos); \
+} \
+static const struct file_operations group_fops_##group_id = { \
+	.owner		= THIS_MODULE, \
+	.read		= dazukofs_group_read_##group_id, \
+	.write		= dazukofs_group_write_##group_id, \
+};
 
-static const struct file_operations group_fops = {
-	.owner		= THIS_MODULE,
-	.read		= dazukofs_group_read,
-	.write		= dazukofs_group_write,
+DECLARE_GROUP_FOPS(0)
+DECLARE_GROUP_FOPS(1)
+DECLARE_GROUP_FOPS(2)
+DECLARE_GROUP_FOPS(3)
+DECLARE_GROUP_FOPS(4)
+DECLARE_GROUP_FOPS(5)
+DECLARE_GROUP_FOPS(6)
+DECLARE_GROUP_FOPS(7)
+DECLARE_GROUP_FOPS(8)
+DECLARE_GROUP_FOPS(9)
+
+static struct cdev groups_cdev[GROUP_COUNT];
+
+static const struct file_operations *group_fops[GROUP_COUNT] = {
+	&group_fops_0,
+	&group_fops_1,
+	&group_fops_2,
+	&group_fops_3,
+	&group_fops_4,
+	&group_fops_5,
+	&group_fops_6,
+	&group_fops_7,
+	&group_fops_8,
+	&group_fops_9,
 };
 
 int dazukofs_group_dev_init(int dev_major, int dev_minor_start,
@@ -124,29 +162,41 @@
 {
 	int err;
 	struct device *dev;
+	int i;
+	int cdev_count;
 	int dev_minor_end = dev_minor_start;
 
-	cdev_init(&group_cdev, &group_fops);
-	group_cdev.owner = THIS_MODULE;
-	err = cdev_add(&group_cdev, MKDEV(dev_major, dev_minor_start), 1);
-	if (err)
-		goto error_out1;
-
-	dev = device_create(dazukofs_class, NULL,
-			    MKDEV(dev_major, dev_minor_end), NULL,
-			    "%s.%d", DEVICE_NAME, 0);
-	if (IS_ERR(dev)) {
-		err = PTR_ERR(dev);
-		goto error_out2;
+	/* setup cdevs for groups */
+	for (cdev_count = 0; cdev_count < GROUP_COUNT; cdev_count++) {
+		cdev_init(&groups_cdev[cdev_count], group_fops[cdev_count]);
+		groups_cdev[cdev_count].owner = THIS_MODULE;
+		err = cdev_add(&groups_cdev[cdev_count],
+			       MKDEV(dev_major, dev_minor_start + cdev_count),
+			       GROUP_COUNT);
+		if (err)
+			goto error_out1;
+	}
+
+	/* create group devices */
+	for (i = 0; i < GROUP_COUNT; i++) {
+		dev = device_create(dazukofs_class, NULL,
+				    MKDEV(dev_major, dev_minor_end), NULL,
+				    "%s.%d", DEVICE_NAME, i);
+		if (IS_ERR(dev)) {
+			err = PTR_ERR(dev);
+			goto error_out2;
+		}
+		dev_minor_end++;
 	}
-	dev_minor_end++;
 
 	return dev_minor_end;
 
 error_out2:
-	device_destroy(dazukofs_class, MKDEV(dev_major, 0));
+	for (i = dev_minor_start; i < dev_minor_end; i++)
+		device_destroy(dazukofs_class, MKDEV(dev_major, i));
 error_out1:
-	cdev_del(&group_cdev);
+	for (i = 0; i < cdev_count; i++)
+		cdev_del(&groups_cdev[i]);
 	return err;
 }
 
@@ -154,6 +204,11 @@
 				int dev_minor_end,
 				struct class *dazukofs_class)
 {
-	device_destroy(dazukofs_class, MKDEV(dev_major, 0));
-	cdev_del(&group_cdev);
+	int i;
+
+	for (i = dev_minor_start; i < dev_minor_end; i++)
+		device_destroy(dazukofs_class, MKDEV(dev_major, i));
+
+	for (i = 0; i < GROUP_COUNT; i++)
+		cdev_del(&groups_cdev[i]);
 }
Index: linux-2.6.28/fs/dazukofs/dev.c
===================================================================
--- linux-2.6.28.orig/fs/dazukofs/dev.c	2009-02-03 18:10:46.000000000 +0100
+++ linux-2.6.28/fs/dazukofs/dev.c	2009-02-03 18:11:01.000000000 +0100
@@ -42,7 +42,7 @@
 	if (err)
 		goto error_out1;
 
-	err = alloc_chrdev_region(&devt, 0, 1, DEVICE_NAME);
+	err = alloc_chrdev_region(&devt, 0, 1 + GROUP_COUNT, DEVICE_NAME);
 	if (err)
 		goto error_out2;
 	dev_major = MAJOR(devt);
@@ -54,20 +54,28 @@
 		goto error_out3;
 	}
 
+	err = dazukofs_ctrl_dev_init(dev_major, dev_minor_start,
+				     dazukofs_class);
+	if (err)
+		goto error_out4;
+
 	dev_minor_end = dazukofs_group_dev_init(dev_major,
 						dev_minor_start + 1,
 						dazukofs_class);
 	if (dev_minor_end < 0) {
 		err = dev_minor_end;
-		goto error_out4;
+		goto error_out5;
 	}
 
 	return 0;
 
+error_out5:
+	dazukofs_ctrl_dev_destroy(dev_major, dev_minor_start, dazukofs_class);
 error_out4:
 	class_destroy(dazukofs_class);
 error_out3:
-	unregister_chrdev_region(MKDEV(dev_major, dev_minor_start), 1);
+	unregister_chrdev_region(MKDEV(dev_major, dev_minor_start),
+				 1 + GROUP_COUNT);
 error_out2:
 	dazukofs_destroy_events();
 error_out1:
@@ -78,7 +86,9 @@
 {
 	dazukofs_group_dev_destroy(dev_major, dev_minor_start + 1,
 				   dev_minor_end, dazukofs_class);
+	dazukofs_ctrl_dev_destroy(dev_major, dev_minor_start, dazukofs_class);
 	class_destroy(dazukofs_class);
-	unregister_chrdev_region(MKDEV(dev_major, dev_minor_start), 1);
+	unregister_chrdev_region(MKDEV(dev_major, dev_minor_start),
+				 1 + GROUP_COUNT);
 	dazukofs_destroy_events();
 }
Index: linux-2.6.28/fs/dazukofs/dev.h
===================================================================
--- linux-2.6.28.orig/fs/dazukofs/dev.h	2009-02-03 18:10:46.000000000 +0100
+++ linux-2.6.28/fs/dazukofs/dev.h	2009-02-03 18:11:01.000000000 +0100
@@ -24,6 +24,7 @@
 #include <linux/device.h>
 
 #define DEVICE_NAME	"dazukofs"
+#define GROUP_COUNT	10
 
 extern int dazukofs_dev_init(void);
 extern void dazukofs_dev_destroy(void);
@@ -34,4 +35,9 @@
 				       int dev_minor_end,
 				       struct class *dazukofs_class);
 
+extern int dazukofs_ctrl_dev_init(int dev_major, int dev_minor,
+				  struct class *dazukofs_class);
+extern void dazukofs_ctrl_dev_destroy(int dev_major, int dev_minor,
+				      struct class *dazukofs_class);
+
 #endif /* __DEV_H */
Index: linux-2.6.28/fs/dazukofs/event.h
===================================================================
--- linux-2.6.28.orig/fs/dazukofs/event.h	2009-02-03 18:10:46.000000000 +0100
+++ linux-2.6.28/fs/dazukofs/event.h	2009-02-03 18:11:01.000000000 +0100
@@ -24,9 +24,15 @@
 extern int dazukofs_init_events(void);
 extern void dazukofs_destroy_events(void);
 
-extern int dazukofs_get_event(unsigned long *event_id, int *fd, pid_t *pid);
-extern int dazukofs_return_event(unsigned long event_id, int deny);
+extern int dazukofs_get_event(unsigned long group_id,
+			      unsigned long *event_id, int *fd, pid_t *pid);
+extern int dazukofs_return_event(unsigned long group_id,
+				 unsigned long event_id, int deny);
 
 extern int dazukofs_check_access(struct dentry *dentry, struct vfsmount *mnt);
 
+extern int dazukofs_get_groups(char **buf);
+extern int dazukofs_add_group(const char *name);
+extern int dazukofs_remove_group(const char *name);
+
 #endif /* __EVENT_H */
Index: linux-2.6.28/Documentation/filesystems/dazukofs.txt
===================================================================
--- linux-2.6.28.orig/Documentation/filesystems/dazukofs.txt	2009-02-03 18:10:46.000000000 +0100
+++ linux-2.6.28/Documentation/filesystems/dazukofs.txt	2009-02-03 18:11:01.000000000 +0100
@@ -24,13 +24,10 @@
 registered with DazukoFS. That process can then choose to allow or deny
 the access.
 
-IMPORTANT: DazukoFS does not allow file access events unless a registered
-           application has approved them. This means that no file access
-           can take place on a DazukoFS mount until an application has
-           registered to perform file access control with DazukoFS.
-           Attempts to access files on a DazukoFS mount will result in the
-           process blocking until a registered application has approved
-           the access.
+IMPORTANT: Initially, DazukoFS will allow all file access events to occur
+           until an application registers to perform file access control.
+           Once an application has registered with DazukoFS, all file access
+           events must be authorized by the registered application.
 
 The stackable filesystem can then be unmounted with:
 
@@ -114,10 +111,41 @@
 file access events. The application then also has the authority to block
 the file access.
 
-For each file access event, only one of the registered applications will
-be notified.
+DazukoFS supports multiple groups. A group is a set of registered processes
+that work together. For each file access event, only one of the registered
+processes of a group will be notified. By registering multiple processes
+within the same group, an application will be able to perform file access
+control for multiple files simultaneously.
 
-By opening the device /dev/dazukofs.0 an application has registered itself.
+A list of registered groups can be seen by reading from the
+/dev/dazukofs.ctrl device. For example:
+
+0:Group_A
+1:Group_B
+
+This means that the group named "Group_A" has been assigned the group id 0
+and the group name "Group_B" has been assigned the group id 1. Groups can
+be added by writing to the /dev/dazukofs.ctrl device. For example, writing:
+
+add=My_New_Group
+
+will create a new group, which will be assigned an available group id. The
+creation of the group should be verified by reading from the
+/dev/dazukofs.ctrl device. This is also necessary to see which group id was
+assigned to the new group.
+
+0:Group_A
+1:Group_B
+2:My_New_Group
+
+The new group "My_New_Group" has been assigned the group id 2. Group names
+are restricted to the characters: a-z A-Z 0-9 - _
+
+Each group has their own device /dev/dazukofs.N in order to interact with
+DazukoFS (where 'N' is the group id). For "My_New_Group" we are assigned
+/dev/dazukofs.2 to use.
+
+By opening the device /dev/dazukofs.N an application has registered itself.
 A read on the device will block until a file access event on DazukoFS has
 taken place. When a file access event has occured, the read will return with
 information about the file access event. For example:
@@ -155,4 +183,27 @@
 NOTE: It is not necessary for the device to be open while the application
       decides if access should be allowed. In fact, it doesn't even have to
       be the same process that responds. DazukoFS is only interested in a
-      response for the given event id.
+      response that matches the pending event id.
+
+A group can be deleted by writing to the /dev/dazukofs.ctrl device. For
+example, writing:
+
+del=My_New_Group
+
+When a group is deleted, any processes registered with that group will
+be interrupted. Further reads on /dev/dazukofs.N will result in an error
+(until some other group has been assigned that group id).
+
+The deletion of the group should be verified by reading from the
+/dev/dazukofs.ctrl device.
+
+0:Group_A
+1:Group_B
+
+If no groups have been added, DazukoFS will allow all file access events. If,
+however, at least one group is added, DazukoFS will expect one process from
+each group to handle every file access event. Even if no processes are
+registered but one or more groups exist, DazukoFS will still wait for file
+access events to be handled by each group. For this reason it is important
+that an application deletes a group it has created, once it should no longer
+perform online file access control.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCHv2 4/5] VFS: DazukoFS, stackable-fs, file access control
  2009-02-03 19:18     ` [PATCHv2 3/5] " John Ogness
@ 2009-02-03 19:19       ` John Ogness
  2009-02-03 19:20         ` [PATCHv2 5/5] " John Ogness
  2009-02-12 20:20       ` [PATCHv2 3/5] " Eric W. Biederman
  2009-02-21 18:11       ` [malware-list] " Frantisek Hrbata
  2 siblings, 1 reply; 24+ messages in thread
From: John Ogness @ 2009-02-03 19:19 UTC (permalink / raw)
  To: linux-kernel; +Cc: viro, malware-list, eparis, hch, alan

Patch 4: Adds a new (optional) command to allow registered processes
         to be tracked. The tracking allows processes to be
         automatically unregistered if they crash and also allows
         groups to be automatically deleted if no more processes are
         registered.

Patched against 2.6.29-rc3.

Signed-off-by: John Ogness <dazukocode@ogness.net>
---
 Documentation/filesystems/dazukofs.txt |   24 ++++-
 fs/dazukofs/ctrl_dev.c                 |   15 ++-
 fs/dazukofs/event.c                    |  103 +++++++++++++++++++++--
 fs/dazukofs/event.h                    |    7 +
 fs/dazukofs/group_dev.c                |   30 ++++++
 5 files changed, 162 insertions(+), 17 deletions(-)
Index: linux-2.6.28/fs/dazukofs/ctrl_dev.c
===================================================================
--- linux-2.6.28.orig/fs/dazukofs/ctrl_dev.c	2009-02-03 18:11:01.000000000 +0100
+++ linux-2.6.28/fs/dazukofs/ctrl_dev.c	2009-02-03 18:12:14.000000000 +0100
@@ -85,7 +85,7 @@
 }
 
 static int process_command(char *buf, const char *key,
-			   int (*func)(const char *),
+			   int (*func)(const char *, int), int arg2,
 			   int *retcode)
 {
 	char *p;
@@ -104,7 +104,7 @@
 		*retcode = -EINVAL;
 	} else {
 		*p2 = 0;
-		*retcode = func(p);
+		*retcode = func(p, arg2);
 		*p2 = ' ';
 	}
 
@@ -131,14 +131,21 @@
 
 	if (!match || (match && ret >= 0)) {
 		if (process_command(tmp, "del=",
-				    dazukofs_remove_group, &ret) == 0) {
+				    dazukofs_remove_group, 0, &ret) == 0) {
 			match = 1;
 		}
 	}
 
 	if (!match || (match && ret >= 0)) {
 		if (process_command(tmp, "add=",
-				    dazukofs_add_group, &ret) == 0) {
+				    dazukofs_add_group, 0, &ret) == 0) {
+			match = 1;
+		}
+	}
+
+	if (!match || (match && ret >= 0)) {
+		if (process_command(tmp, "addtrack=",
+				    dazukofs_add_group, 1, &ret) == 0) {
 			match = 1;
 		}
 	}
Index: linux-2.6.28/fs/dazukofs/event.c
===================================================================
--- linux-2.6.28.orig/fs/dazukofs/event.c	2009-02-03 18:11:01.000000000 +0100
+++ linux-2.6.28/fs/dazukofs/event.c	2009-02-03 18:12:14.000000000 +0100
@@ -65,6 +65,8 @@
 	wait_queue_head_t queue;
 	struct dazukofs_event_container working_list;
 	atomic_t use_count;
+	int tracking;
+	int track_count;
 	int deprecated;
 };
 
@@ -287,9 +289,10 @@
 }
 
 /**
- * __check_for_group - check if a group exists
+ * __check_for_group - check if a group exists and set tracking
  * @name: a group name to check for
  * @id: a group id to check for
+ * @track: flag set if tracking is to be used
  * @already_exists: will be set if the group already exists
  *
  * Description: This function checks names and id's to see if a group may
@@ -300,11 +303,17 @@
  * If the group name exists and the id is already that which is requested,
  * the function returns success, but sets the already_exists flag.
  *
+ * NOTE: Although the function name may imply read-only, this function
+ *       _will_ set a group to track if the group is found to exist and
+ *       tracking should be set. We do this because it is convenient
+ *       since the work_mutex is already locked.
+ *
  * IMPORTANT: This function requires work_mutex to be held!
  *
  * Returns 0 if the group exists or may be created.
  */
-static int __check_for_group(const char *name, int id, int *already_exists)
+static int __check_for_group(const char *name, int id, int track,
+			     int *already_exists)
 {
 	struct dazukofs_group *grp;
 	struct list_head *pos;
@@ -325,6 +334,8 @@
 		} else {
 			if (strcmp(name, grp->name) == 0) {
 				*already_exists = 1;
+				if (track)
+					grp->tracking = 1;
 				break;
 			} else if (grp->group_id == id) {
 				id_available = 0;
@@ -348,6 +359,7 @@
  * __create_group - allocate and initialize a group structure
  * @name: the name of the new group
  * @id: the id of the new group
+ * @track: flag set if tracking is to be used
  *
  * Description: This function allocates and initializes a group
  * structure. The group_count should be locked to ensure that
@@ -356,7 +368,8 @@
  *
  * Returns the newly created and initialized group structure.
  */
-static struct dazukofs_group *__create_group(const char *name, int id)
+static struct dazukofs_group *__create_group(const char *name, int id,
+					     int track)
 {
 	struct dazukofs_group *grp;
 
@@ -375,20 +388,27 @@
 	init_waitqueue_head(&grp->queue);
 	INIT_LIST_HEAD(&grp->todo_list.list);
 	INIT_LIST_HEAD(&grp->working_list.list);
+	if (track)
+		grp->tracking = 1;
 	return grp;
 }
 
 /**
  * dazukofs_add_group - add a new group
  * @name: the name of the group to add
+ * @track: flag set if tracking is to be used
  *
  * Description: This function is called by the device layer to add a new
  * group. It returns success if the group has been successfully created
  * or if the group already exists.
  *
+ * If the group already exists and is not tracking, but "track" is set,
+ * the group will be changed to start tracking (actually done in the
+ * function __check_for_group()).
+ *
  * Returns 0 on success.
  */
-int dazukofs_add_group(const char *name)
+int dazukofs_add_group(const char *name, int track)
 {
 	int ret = 0;
 	int already_exists;
@@ -398,7 +418,8 @@
 	write_lock(&group_count_rwlock);
 
 	mutex_lock(&work_mutex);
-	while (__check_for_group(name, available_id, &already_exists) != 0) {
+	while (__check_for_group(name, available_id, track,
+				 &already_exists) != 0) {
 		/* try again with the next id */
 		available_id++;
 	}
@@ -415,7 +436,7 @@
 		goto out;
 	}
 
-	grp = __create_group(name, available_id);
+	grp = __create_group(name, available_id, track);
 	if (!grp) {
 		ret = -ENOMEM;
 		goto out;
@@ -434,14 +455,17 @@
 /**
  * dazukofs_remove_group - remove a group
  * @name: the name of the group to remove
+ * @unsued: argument not used
  *
  * Description: This function is called by the device layer to remove a
  * group. It returns success if the group has been deleted or the group
  * does not exist.
  *
+ * The unused argument exists for convenience to the device layer.
+ *
  * Returns 0 on success.
  */
-int dazukofs_remove_group(const char *name)
+int dazukofs_remove_group(const char *name, int unused)
 {
 	int ret = 0;
 	struct dazukofs_group *grp;
@@ -741,6 +765,71 @@
 }
 
 /**
+ * dazukofs_group_open_tracking - begin tracking this process
+ * @group_id: id of the group we belong to
+ *
+ * Description: This function is called by the device layer to begin
+ * tracking the current process (if tracking for that group is enabled).
+ *
+ * Tracking simply means to keep track if there are any processes still
+ * registered with the group, so we use a simple counter for that.
+ * dazukofs_group_release_tracking() must be called when this process
+ * unregisters.
+ *
+ * Returns 0 if tracking is _not_ enabled.
+ */
+int dazukofs_group_open_tracking(unsigned long group_id)
+{
+	struct dazukofs_group *grp;
+	struct list_head *pos;
+	int tracking = 0;
+
+	mutex_lock(&work_mutex);
+	list_for_each(pos, &group_list.list) {
+		grp = list_entry(pos, struct dazukofs_group, list);
+		if (!grp->deprecated && grp->group_id == group_id) {
+			if (grp->tracking) {
+				atomic_inc(&grp->use_count);
+				grp->track_count++;
+				tracking = 1;
+			}
+			break;
+		}
+	}
+	mutex_unlock(&work_mutex);
+	return tracking;
+}
+
+/**
+ * dazukofs_group_release_tracking - stop tracking this process
+ * @group_id: id of the group we belong to
+ *
+ * Description: This function is called by the device layer when a process
+ * is no longer registered and thus tracking for this process should end
+ * (if tracking for the group is enabled).
+ */
+void dazukofs_group_release_tracking(unsigned long group_id)
+{
+	struct dazukofs_group *grp;
+	struct list_head *pos;
+
+	mutex_lock(&work_mutex);
+	list_for_each(pos, &group_list.list) {
+		grp = list_entry(pos, struct dazukofs_group, list);
+		if (!grp->deprecated && grp->group_id == group_id) {
+			if (grp->tracking) {
+				atomic_dec(&grp->use_count);
+				grp->track_count--;
+				if (grp->track_count == 0)
+					__remove_group(grp);
+			}
+			break;
+		}
+	}
+	mutex_unlock(&work_mutex);
+}
+
+/**
  * dazukofs_return_event - return checked file access results
  * @group_id: id of the group the event came from
  * @event_id: the id of the event
Index: linux-2.6.28/fs/dazukofs/event.h
===================================================================
--- linux-2.6.28.orig/fs/dazukofs/event.h	2009-02-03 18:11:01.000000000 +0100
+++ linux-2.6.28/fs/dazukofs/event.h	2009-02-03 18:12:14.000000000 +0100
@@ -31,8 +31,11 @@
 
 extern int dazukofs_check_access(struct dentry *dentry, struct vfsmount *mnt);
 
+extern int dazukofs_group_open_tracking(unsigned long group_id);
+extern void dazukofs_group_release_tracking(unsigned long group_id);
+
 extern int dazukofs_get_groups(char **buf);
-extern int dazukofs_add_group(const char *name);
-extern int dazukofs_remove_group(const char *name);
+extern int dazukofs_add_group(const char *name, int track);
+extern int dazukofs_remove_group(const char *name, int unused);
 
 #endif /* __EVENT_H */
Index: linux-2.6.28/fs/dazukofs/group_dev.c
===================================================================
--- linux-2.6.28.orig/fs/dazukofs/group_dev.c	2009-02-03 18:11:01.000000000 +0100
+++ linux-2.6.28/fs/dazukofs/group_dev.c	2009-02-03 18:12:14.000000000 +0100
@@ -27,6 +27,24 @@
 #include "event.h"
 #include "dev.h"
 
+static int dazukofs_group_open(int group_id, struct inode *inode,
+			       struct file *file)
+{
+	if (dazukofs_group_open_tracking(group_id))
+		file->private_data = file;
+	else
+		file->private_data = NULL;
+	return 0;
+}
+
+static int dazukofs_group_release(int group_id, struct inode *inode,
+				  struct file *file)
+{
+	if (file->private_data)
+		dazukofs_group_release_tracking(group_id);
+	return 0;
+}
+
 static ssize_t dazukofs_group_read(int group_id, struct file *file,
 				   char __user *buffer, size_t length,
 				   loff_t *pos)
@@ -112,6 +130,16 @@
 }
 
 #define DECLARE_GROUP_FOPS(group_id) \
+static int \
+dazukofs_group_open_##group_id(struct inode *inode, struct file *file) \
+{ \
+	return dazukofs_group_open(group_id, inode, file); \
+} \
+static int \
+dazukofs_group_release_##group_id(struct inode *inode, struct file *file) \
+{ \
+	return dazukofs_group_release(group_id, inode, file); \
+} \
 static ssize_t \
 dazukofs_group_read_##group_id(struct file *file, char __user *buffer, \
 			       size_t length, loff_t *pos) \
@@ -127,6 +155,8 @@
 } \
 static const struct file_operations group_fops_##group_id = { \
 	.owner		= THIS_MODULE, \
+	.open		= dazukofs_group_open_##group_id, \
+	.release	= dazukofs_group_release_##group_id, \
 	.read		= dazukofs_group_read_##group_id, \
 	.write		= dazukofs_group_write_##group_id, \
 };
Index: linux-2.6.28/Documentation/filesystems/dazukofs.txt
===================================================================
--- linux-2.6.28.orig/Documentation/filesystems/dazukofs.txt	2009-02-03 18:11:01.000000000 +0100
+++ linux-2.6.28/Documentation/filesystems/dazukofs.txt	2009-02-03 18:12:14.000000000 +0100
@@ -180,10 +180,26 @@
 
 By closing the device, the application will unregister itself.
 
-NOTE: It is not necessary for the device to be open while the application
-      decides if access should be allowed. In fact, it doesn't even have to
-      be the same process that responds. DazukoFS is only interested in a
-      response that matches the pending event id.
+To provide crash protection for applications, groups can be added using
+the "addtrack" keyword instead of "add". The keyword "addtrack" tells
+DazukoFS to add the group and track the number of registered processes in
+that group. Tracking begins as soon as the first process has registered
+with DazukoFS. Once all processes of the group have unregistered, DazukoFS
+will automatically delete the group.
+
+If an application crashes, is killed, or ends without closing the device,
+DazukoFS will still unregister that process. Using "addtrack" will ensure
+that a created group is automatically deleted if the application is not
+able to shutdown in a clean manner.
+
+NOTE: If "addtrack" is used, it is necessary for the device to be kept open
+      the entire time the application is performing online file access
+      control. Otherwise the group may become unintentionally deleted.
+
+      If "add" is used, it is not necessary for the device to be kept open
+      while the application decides if access should be allowed. Actually,
+      DazukoFS doesn't care which process responds to a file access event.
+      DazukoFS is only interested in a response for the given event id.
 
 A group can be deleted by writing to the /dev/dazukofs.ctrl device. For
 example, writing:

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCHv2 5/5] VFS: DazukoFS, stackable-fs, file access control
  2009-02-03 19:19       ` [PATCHv2 4/5] " John Ogness
@ 2009-02-03 19:20         ` John Ogness
  2009-02-12 20:24           ` Eric W. Biederman
  0 siblings, 1 reply; 24+ messages in thread
From: John Ogness @ 2009-02-03 19:20 UTC (permalink / raw)
  To: linux-kernel; +Cc: viro, malware-list, eparis, hch, alan

Patch 5: Creates /dev/dazukofs.ign as an (optional) mechanism for any
         processes to hide themselves from DazukoFS file access
         control.

Patched against 2.6.29-rc3.

Signed-off-by: John Ogness <dazukocode@ogness.net>
---
 Documentation/filesystems/dazukofs.txt |   24 ++
 fs/dazukofs/Makefile                   |    2 
 fs/dazukofs/dev.c                      |   22 +-
 fs/dazukofs/dev.h                      |    6 
 fs/dazukofs/event.c                    |    4 
 fs/dazukofs/ign_dev.c                  |  186 +++++++++++++++++++++++
 6 files changed, 237 insertions(+), 7 deletions(-)
Index: linux-2.6.28/fs/dazukofs/ign_dev.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.28/fs/dazukofs/ign_dev.c	2009-02-03 18:12:18.000000000 +0100
@@ -0,0 +1,186 @@
+/* dazukofs: access control stackable filesystem
+
+   Copyright (C) 2008 John Ogness
+     Author: John Ogness <dazukocode@ogness.net>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*/
+
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+
+#include "dazukofs_fs.h"
+#include "dev.h"
+
+struct dazukofs_proc {
+	struct list_head list;
+	struct task_struct *curr;
+};
+
+static struct dazukofs_proc ign_list;
+static struct mutex ign_list_mutex;
+static struct kmem_cache *dazukofs_ign_cachep;
+
+int dazukofs_check_ignore_process(void)
+{
+	struct list_head *pos;
+	struct dazukofs_proc *proc;
+	int found = 0;
+
+	mutex_lock(&ign_list_mutex);
+	list_for_each(pos, &ign_list.list) {
+		proc = list_entry(pos, struct dazukofs_proc, list);
+		if (proc->curr == current) {
+			found = 1;
+			break;
+		}
+	}
+	mutex_unlock(&ign_list_mutex);
+
+	return !found;
+}
+
+static int dazukofs_add_ign(struct file *file)
+{
+	struct dazukofs_proc *proc =
+		kmem_cache_zalloc(dazukofs_ign_cachep, GFP_KERNEL);
+	if (!proc) {
+		file->private_data = NULL;
+		return -ENOMEM;
+	}
+
+	file->private_data = proc;
+	proc->curr = current;
+
+	mutex_lock(&ign_list_mutex);
+	list_add(&proc->list, &ign_list.list);
+	mutex_unlock(&ign_list_mutex);
+
+	return 0;
+}
+
+static void dazukofs_remove_ign(struct file *file)
+{
+	struct list_head *pos;
+	struct dazukofs_proc *proc = NULL;
+	struct dazukofs_proc *check_proc = file->private_data;
+	int found = 0;
+
+	if (!check_proc)
+		return;
+
+	mutex_lock(&ign_list_mutex);
+	list_for_each(pos, &ign_list.list) {
+		proc = list_entry(pos, struct dazukofs_proc, list);
+		if (proc->curr == check_proc->curr) {
+			found = 1;
+			list_del(pos);
+			break;
+		}
+	}
+	mutex_unlock(&ign_list_mutex);
+
+	if (found) {
+		file->private_data = NULL;
+		kmem_cache_free(dazukofs_ign_cachep, proc);
+	}
+}
+
+static int dazukofs_ign_open(struct inode *inode, struct file *file)
+{
+	return dazukofs_add_ign(file);
+}
+
+static int dazukofs_ign_release(struct inode *inode, struct file *file)
+{
+	dazukofs_remove_ign(file);
+	return 0;
+}
+
+static void dazukofs_destroy_ignlist(void)
+{
+	struct list_head *pos;
+	struct list_head *q;
+	struct dazukofs_proc *proc;
+
+	list_for_each_safe(pos, q, &ign_list.list) {
+		proc = list_entry(pos, struct dazukofs_proc, list);
+		list_del(pos);
+		kmem_cache_free(dazukofs_ign_cachep, proc);
+	}
+}
+
+static struct cdev ign_cdev;
+
+static const struct file_operations ign_fops = {
+	.owner		= THIS_MODULE,
+	.open		= dazukofs_ign_open,
+	.release	= dazukofs_ign_release,
+};
+
+int dazukofs_ign_dev_init(int dev_major, int dev_minor,
+			  struct class *dazukofs_class)
+{
+	int err = 0;
+	struct device *dev;
+
+	INIT_LIST_HEAD(&ign_list.list);
+	mutex_init(&ign_list_mutex);
+
+	dazukofs_ign_cachep =
+		kmem_cache_create("dazukofs_ign_cache",
+				  sizeof(struct dazukofs_proc), 0,
+				  SLAB_HWCACHE_ALIGN, NULL);
+	if (!dazukofs_ign_cachep) {
+		err = -ENOMEM;
+		goto error_out1;
+	}
+
+	/* setup cdev for ignore */
+	cdev_init(&ign_cdev, &ign_fops);
+	ign_cdev.owner = THIS_MODULE;
+	err = cdev_add(&ign_cdev, MKDEV(dev_major, dev_minor), 1);
+	if (err)
+		goto error_out2;
+
+	/* create ignore device */
+	dev = device_create(dazukofs_class, NULL, MKDEV(dev_major, dev_minor),
+			    NULL, "%s.ign", DEVICE_NAME);
+	if (IS_ERR(dev)) {
+		err = PTR_ERR(dev);
+		goto error_out3;
+	}
+
+	return 0;
+
+error_out3:
+	cdev_del(&ign_cdev);
+error_out2:
+	dazukofs_destroy_ignlist();
+	kmem_cache_destroy(dazukofs_ign_cachep);
+error_out1:
+	return err;
+}
+
+void dazukofs_ign_dev_destroy(int dev_major, int dev_minor,
+			      struct class *dazukofs_class)
+{
+	device_destroy(dazukofs_class, MKDEV(dev_major, dev_minor));
+	cdev_del(&ign_cdev);
+	dazukofs_destroy_ignlist();
+	kmem_cache_destroy(dazukofs_ign_cachep);
+}
Index: linux-2.6.28/fs/dazukofs/Makefile
===================================================================
--- linux-2.6.28.orig/fs/dazukofs/Makefile	2009-02-03 18:11:01.000000000 +0100
+++ linux-2.6.28/fs/dazukofs/Makefile	2009-02-03 18:12:18.000000000 +0100
@@ -5,4 +5,4 @@
 obj-$(CONFIG_DAZUKOFS_FS) += dazukofs.o
 
 dazukofs-objs := super.o inode.o file.o dentry.o mmap.o event.o \
-		 dev.o group_dev.o ctrl_dev.o
+		 dev.o group_dev.o ctrl_dev.o ign_dev.o
Index: linux-2.6.28/fs/dazukofs/event.c
===================================================================
--- linux-2.6.28.orig/fs/dazukofs/event.c	2009-02-03 18:12:14.000000000 +0100
+++ linux-2.6.28/fs/dazukofs/event.c	2009-02-03 18:12:18.000000000 +0100
@@ -619,6 +619,10 @@
 	if (!check_recursion())
 		return -1;
 
+	/* am I an ignored process? */
+	if (!dazukofs_check_ignore_process())
+		return -1;
+
 	return 0;
 }
 
Index: linux-2.6.28/fs/dazukofs/dev.c
===================================================================
--- linux-2.6.28.orig/fs/dazukofs/dev.c	2009-02-03 18:11:01.000000000 +0100
+++ linux-2.6.28/fs/dazukofs/dev.c	2009-02-03 18:12:18.000000000 +0100
@@ -42,7 +42,7 @@
 	if (err)
 		goto error_out1;
 
-	err = alloc_chrdev_region(&devt, 0, 1 + GROUP_COUNT, DEVICE_NAME);
+	err = alloc_chrdev_region(&devt, 0, 2 + GROUP_COUNT, DEVICE_NAME);
 	if (err)
 		goto error_out2;
 	dev_major = MAJOR(devt);
@@ -59,23 +59,31 @@
 	if (err)
 		goto error_out4;
 
+	err = dazukofs_ign_dev_init(dev_major, dev_minor_start + 1,
+				    dazukofs_class);
+	if (err)
+		goto error_out5;
+
 	dev_minor_end = dazukofs_group_dev_init(dev_major,
-						dev_minor_start + 1,
+						dev_minor_start + 2,
 						dazukofs_class);
 	if (dev_minor_end < 0) {
 		err = dev_minor_end;
-		goto error_out5;
+		goto error_out6;
 	}
 
 	return 0;
 
+error_out6:
+	dazukofs_ign_dev_destroy(dev_major, dev_minor_start + 1,
+				 dazukofs_class);
 error_out5:
 	dazukofs_ctrl_dev_destroy(dev_major, dev_minor_start, dazukofs_class);
 error_out4:
 	class_destroy(dazukofs_class);
 error_out3:
 	unregister_chrdev_region(MKDEV(dev_major, dev_minor_start),
-				 1 + GROUP_COUNT);
+				 2 + GROUP_COUNT);
 error_out2:
 	dazukofs_destroy_events();
 error_out1:
@@ -84,11 +92,13 @@
 
 void dazukofs_dev_destroy(void)
 {
-	dazukofs_group_dev_destroy(dev_major, dev_minor_start + 1,
+	dazukofs_group_dev_destroy(dev_major, dev_minor_start + 2,
 				   dev_minor_end, dazukofs_class);
+	dazukofs_ign_dev_destroy(dev_major, dev_minor_start + 1,
+				 dazukofs_class);
 	dazukofs_ctrl_dev_destroy(dev_major, dev_minor_start, dazukofs_class);
 	class_destroy(dazukofs_class);
 	unregister_chrdev_region(MKDEV(dev_major, dev_minor_start),
-				 1 + GROUP_COUNT);
+				 2 + GROUP_COUNT);
 	dazukofs_destroy_events();
 }
Index: linux-2.6.28/fs/dazukofs/dev.h
===================================================================
--- linux-2.6.28.orig/fs/dazukofs/dev.h	2009-02-03 18:11:01.000000000 +0100
+++ linux-2.6.28/fs/dazukofs/dev.h	2009-02-03 18:12:18.000000000 +0100
@@ -40,4 +40,10 @@
 extern void dazukofs_ctrl_dev_destroy(int dev_major, int dev_minor,
 				      struct class *dazukofs_class);
 
+extern int dazukofs_ign_dev_init(int dev_major, int dev_minor,
+				 struct class *dazukofs_class);
+extern void dazukofs_ign_dev_destroy(int dev_major, int dev_minor,
+				     struct class *dazukofs_class);
+extern int dazukofs_check_ignore_process(void);
+
 #endif /* __DEV_H */
Index: linux-2.6.28/Documentation/filesystems/dazukofs.txt
===================================================================
--- linux-2.6.28.orig/Documentation/filesystems/dazukofs.txt	2009-02-03 18:12:14.000000000 +0100
+++ linux-2.6.28/Documentation/filesystems/dazukofs.txt	2009-02-03 18:12:18.000000000 +0100
@@ -223,3 +223,27 @@
 access events to be handled by each group. For this reason it is important
 that an application deletes a group it has created, once it should no longer
 perform online file access control.
+
+All processes on the system that try to access files on a DazukoFS mount will
+require authorization (if at least one group exists). This is also true for
+registered process that try to access files on a DazukoFS mount.
+
+IMPORTANT: If registered processes access files on a DazukoFS mount, they
+           will cause new file access events that must be authorized. This
+           could lead to deadlock if not properly considered.
+
+Since the registered process receives an open file descriptor to the file
+being accessed, there should be no need for that process to open other
+files. However, if the process must open additional files (and these
+files potentially lie on a DazukoFS mount), it is possible for processes
+to hide themselves from DazukoFS.
+
+By opening the /dev/dazukofs.ign device, a process will be ignored by
+DazukoFS. It does not matter if the process is registered or not. No data
+must be written or read from the device. It simply needs to be opened.
+
+WARNING: Make sure the permissions for /dev/dazukofs.ign are securely
+         set. Otherwise, any process could potentially hide itself.
+
+As soon as the /dev/dazukofs.ign device is closed, the process is no
+longer hidden.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 1/5] VFS: DazukoFS, stackable-fs, file access control
  2009-02-03 19:15 ` [PATCHv2 1/5] " John Ogness
  2009-02-03 19:17   ` [PATCHv2 2/5] " John Ogness
@ 2009-02-12 15:27   ` Jan Engelhardt
  2009-02-12 15:31     ` Al Viro
  2009-02-13 19:31     ` John Ogness
  1 sibling, 2 replies; 24+ messages in thread
From: Jan Engelhardt @ 2009-02-12 15:27 UTC (permalink / raw)
  To: John Ogness; +Cc: linux-kernel, viro, malware-list, eparis, hch, alan


On Tuesday 2009-02-03 20:15, John Ogness wrote:

>Patch 1: Introduces DazukoFS as a nullfs. This is the raw stackable
>         filesystem part of DazukoFS and does nothing except stack and
>         pass filesystem calls to the lower filesystem.
>
>Patched against 2.6.29-rc3.
>
>Index: linux-2.6.28/Documentation/filesystems/dazukofs.txt
>===================================================================
>--- /dev/null	1970-01-01 00:00:00.000000000 +0000
>+++ linux-2.6.28/Documentation/filesystems/dazukofs.txt	2009-02-03 18:07:44.000000000 +0100
>+
>+=====================
>+ MOUNTING/UNMOUNTING
>+=====================
>+
>+DazukoFS is typically mounted on top of an existing directory. For example,
>+to stack DazukoFS on top of the /opt directory, the following mount(8)
>+command can be given:
>+
>+# mount -t dazukofs /opt /opt
[...]
>+# mount -t dazukofs /opt /mnt
>+
>+When accessing files within /mnt, you will be accessing files in /opt
>+(through DazukoFS). When accessing files directly in /opt, DazukoFS will not
>+be involved.
>+
>+THIS HAS POTENTIAL PROBLEMS!

This ought to be done better in light of the problem that one
could issue `mount -t dazukofs /mnt /opt`
In kernel code, I'm thinking something like

static int foofs_dothis(...)
{
	buf = call d_path on sb->s_root
	call namei_lookup on buf
	use lookup's resulting dentry as source directory
}

This assumes at least that namei_lookup will not return the new SB's
dentry for the given path.
Iff that works out somehow, the 'device' part in the mount command
line is practically ignored like it is for like procfs.



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 1/5] VFS: DazukoFS, stackable-fs, file access control
  2009-02-12 15:27   ` [PATCHv2 1/5] " Jan Engelhardt
@ 2009-02-12 15:31     ` Al Viro
  2009-02-12 15:59       ` Jan Engelhardt
  2009-02-13 19:31     ` John Ogness
  1 sibling, 1 reply; 24+ messages in thread
From: Al Viro @ 2009-02-12 15:31 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: John Ogness, linux-kernel, malware-list, eparis, hch, alan

On Thu, Feb 12, 2009 at 04:27:14PM +0100, Jan Engelhardt wrote:

> This ought to be done better in light of the problem that one
> could issue `mount -t dazukofs /mnt /opt`
> In kernel code, I'm thinking something like
> 
> static int foofs_dothis(...)
> {
> 	buf = call d_path on sb->s_root

Call what on what?  There is no such thing as the mountpoint of superblock.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 1/5] VFS: DazukoFS, stackable-fs, file access control
  2009-02-12 15:31     ` Al Viro
@ 2009-02-12 15:59       ` Jan Engelhardt
  2009-02-12 16:47         ` Al Viro
  0 siblings, 1 reply; 24+ messages in thread
From: Jan Engelhardt @ 2009-02-12 15:59 UTC (permalink / raw)
  To: Al Viro; +Cc: John Ogness, linux-kernel, malware-list, eparis, hch, alan


On Thursday 2009-02-12 16:31, Al Viro wrote:
>On Thu, Feb 12, 2009 at 04:27:14PM +0100, Jan Engelhardt wrote:
>
>> This ought to be done better in light of the problem that one
>> could issue `mount -t dazukofs /mnt /opt`
>> In kernel code, I'm thinking something like
>> 
>> static int foofs_dothis(...)
>> {
>> 	buf = call d_path on sb->s_root
>
>Call what on what?  There is no such thing as the mountpoint of superblock.
>
In fs/namespace.c:do_new_mount there is 'path'. If 'path' was passed
down to the filesystem, path->dentry, I would assume, could be used
by the filesystem as a pointer to the original directory which
dazuko is going to shadow.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 2/5] VFS: DazukoFS, stackable-fs, file access control
  2009-02-03 19:17   ` [PATCHv2 2/5] " John Ogness
  2009-02-03 19:18     ` [PATCHv2 3/5] " John Ogness
@ 2009-02-12 16:00     ` Jan Engelhardt
  2009-02-13 19:33       ` John Ogness
  2009-02-12 20:14     ` Eric W. Biederman
  2 siblings, 1 reply; 24+ messages in thread
From: Jan Engelhardt @ 2009-02-12 16:00 UTC (permalink / raw)
  To: John Ogness; +Cc: linux-kernel, viro, malware-list, eparis, hch, alan


On Tuesday 2009-02-03 20:17, John Ogness wrote:
>Index: linux-2.6.28/Documentation/filesystems/dazukofs.txt
>===================================================================
>--- linux-2.6.28.orig/Documentation/filesystems/dazukofs.txt	2009-02-03 18:07:44.000000000 +0100
>+++ linux-2.6.28/Documentation/filesystems/dazukofs.txt	2009-02-03 18:10:46.000000000 +0100
>@@ -2,9 +2,10 @@
>+
>+By opening the device /dev/dazukofs.0 an application has registered itself.
>+A read on the device will block until a file access event on DazukoFS has
>+taken place. When a file access event has occured, the read will return with
>+information about the file access event. For example:

Given that there are more device files to come, I would
put this into a separate directory, /dev/dazukofs/0, much
like dm-crypt does for its devices.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 1/5] VFS: DazukoFS, stackable-fs, file access control
  2009-02-12 15:59       ` Jan Engelhardt
@ 2009-02-12 16:47         ` Al Viro
  0 siblings, 0 replies; 24+ messages in thread
From: Al Viro @ 2009-02-12 16:47 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: John Ogness, linux-kernel, malware-list, eparis, hch, alan

On Thu, Feb 12, 2009 at 04:59:41PM +0100, Jan Engelhardt wrote:
> 
> On Thursday 2009-02-12 16:31, Al Viro wrote:
> >On Thu, Feb 12, 2009 at 04:27:14PM +0100, Jan Engelhardt wrote:
> >
> >> This ought to be done better in light of the problem that one
> >> could issue `mount -t dazukofs /mnt /opt`
> >> In kernel code, I'm thinking something like
> >> 
> >> static int foofs_dothis(...)
> >> {
> >> 	buf = call d_path on sb->s_root
> >
> >Call what on what?  There is no such thing as the mountpoint of superblock.
> >
> In fs/namespace.c:do_new_mount there is 'path'. If 'path' was passed
> down to the filesystem, path->dentry, I would assume, could be used
> by the filesystem as a pointer to the original directory which
> dazuko is going to shadow.

It will not be passed dow to the filesystem, simply because said filesystem
can be silently moved elesewhere and whatever it used to be mounted on -
unmounted.

What's more, whatever it shadows might itself be mounted in any number of
places, so...

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 2/5] VFS: DazukoFS, stackable-fs, file access control
  2009-02-03 19:17   ` [PATCHv2 2/5] " John Ogness
  2009-02-03 19:18     ` [PATCHv2 3/5] " John Ogness
  2009-02-12 16:00     ` [PATCHv2 2/5] " Jan Engelhardt
@ 2009-02-12 20:14     ` Eric W. Biederman
  2009-02-13 19:39       ` John Ogness
  2 siblings, 1 reply; 24+ messages in thread
From: Eric W. Biederman @ 2009-02-12 20:14 UTC (permalink / raw)
  To: John Ogness; +Cc: linux-kernel, viro, malware-list, eparis, hch, alan

John Ogness <dazukocode@ogness.net> writes:

> Patch 2: Creates /dev/dazukofs.0 for userspace applications to perform
>          file access control. At this point, all applications are
>          considered to be working together (in the same group).

Please use struct pid properly.

There is no guarantee that whoever opens /dev/dazukofs.0 will
be in the global pid namespace.

So you need to properly store a struct pid reference in your
data structures, and call pid_vnr just before you pass the value to userspace.

As it is your code is broken.


Eric

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 3/5] VFS: DazukoFS, stackable-fs, file access control
  2009-02-03 19:18     ` [PATCHv2 3/5] " John Ogness
  2009-02-03 19:19       ` [PATCHv2 4/5] " John Ogness
@ 2009-02-12 20:20       ` Eric W. Biederman
  2009-02-17  8:55         ` John Ogness
  2009-02-21 18:11       ` [malware-list] " Frantisek Hrbata
  2 siblings, 1 reply; 24+ messages in thread
From: Eric W. Biederman @ 2009-02-12 20:20 UTC (permalink / raw)
  To: John Ogness; +Cc: linux-kernel, viro, malware-list, eparis, hch, alan

John Ogness <dazukocode@ogness.net> writes:

> Patch 3: Creates /dev/dazukofs.ctrl to allow for groups to be added,
>          listed, and deleted. Also, /dev/dazukofs.[0-9] devices are
>          created to support multiple groups.

Great another global namespace for process groups.  What is your plan
for making this work in a container?

Eric

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 5/5] VFS: DazukoFS, stackable-fs, file access control
  2009-02-03 19:20         ` [PATCHv2 5/5] " John Ogness
@ 2009-02-12 20:24           ` Eric W. Biederman
  0 siblings, 0 replies; 24+ messages in thread
From: Eric W. Biederman @ 2009-02-12 20:24 UTC (permalink / raw)
  To: John Ogness; +Cc: linux-kernel, viro, malware-list, eparis, hch, alan

John Ogness <dazukocode@ogness.net> writes:

> Patch 5: Creates /dev/dazukofs.ign as an (optional) mechanism for any
>          processes to hide themselves from DazukoFS file access
>          control.

Your reference counting is broken.  What happens if a process exits
and you are ignoring it. 

When fixing this use a struct pid * not struct task_struct *.  That
is what struct pid is for there is no need to pin the entire task
struct into memory after the process exits just because
you decided to ignore it.

Eric

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 1/5] VFS: DazukoFS, stackable-fs, file access control
  2009-02-12 15:27   ` [PATCHv2 1/5] " Jan Engelhardt
  2009-02-12 15:31     ` Al Viro
@ 2009-02-13 19:31     ` John Ogness
  2009-02-13 19:48       ` Al Viro
  2009-02-13 20:00       ` Jan Engelhardt
  1 sibling, 2 replies; 24+ messages in thread
From: John Ogness @ 2009-02-13 19:31 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: linux-kernel, viro, malware-list, eparis, hch, alan

On 2009-02-12, Jan Engelhardt <jengelh@medozas.de> wrote:
>>+DazukoFS is typically mounted on top of an existing directory. For example,
>>+to stack DazukoFS on top of the /opt directory, the following mount(8)
>>+command can be given:
>>+
>>+# mount -t dazukofs /opt /opt
> [...]
>>+# mount -t dazukofs /opt /mnt
>>+
>>+When accessing files within /mnt, you will be accessing files in /opt
>>+(through DazukoFS). When accessing files directly in /opt, DazukoFS will not
>>+be involved.
>>+
>>+THIS HAS POTENTIAL PROBLEMS!
>
> This ought to be done better in light of the problem that one
> could issue `mount -t dazukofs /mnt /opt`
> In kernel code, I'm thinking something like
>
> static int foofs_dothis(...)
> {
> 	buf = call d_path on sb->s_root
> 	call namei_lookup on buf
> 	use lookup's resulting dentry as source directory
> }
>
> This assumes at least that namei_lookup will not return the new SB's
> dentry for the given path.
> Iff that works out somehow, the 'device' part in the mount command
> line is practically ignored like it is for like procfs.

If we want to forbid users from being able to do this, I think it
would be simpler just to fail the mount if it is _not_ specified for
direct stacking. This forces users to use a syntax that makes it very
clear what they are doing.

Since mounting to a different directory could cause problems, I think
it will be good to forbid it. I will make this change for the next
patch series.

John Ogness

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 2/5] VFS: DazukoFS, stackable-fs, file access control
  2009-02-12 16:00     ` [PATCHv2 2/5] " Jan Engelhardt
@ 2009-02-13 19:33       ` John Ogness
  0 siblings, 0 replies; 24+ messages in thread
From: John Ogness @ 2009-02-13 19:33 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: linux-kernel, viro, malware-list, eparis, hch, alan

On 2009-02-12, Jan Engelhardt <jengelh@medozas.de> wrote:
>>+By opening the device /dev/dazukofs.0 an application has registered itself.
>>+A read on the device will block until a file access event on DazukoFS has
>>+taken place. When a file access event has occured, the read will return with
>>+information about the file access event. For example:
>
> Given that there are more device files to come, I would
> put this into a separate directory, /dev/dazukofs/0, much
> like dm-crypt does for its devices.

I was not sure if I should leave it up to udev rules to organize
things. I agree that putting everything under a directory is cleaner.
I will make this change for the next patch series.

John Ogness

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 2/5] VFS: DazukoFS, stackable-fs, file access control
  2009-02-12 20:14     ` Eric W. Biederman
@ 2009-02-13 19:39       ` John Ogness
  0 siblings, 0 replies; 24+ messages in thread
From: John Ogness @ 2009-02-13 19:39 UTC (permalink / raw)
  To: Eric W. Biederman
  Cc: John Ogness, linux-kernel, viro, malware-list, eparis, hch, alan

On 2009-02-12, ebiederm@xmission.com (Eric W. Biederman) wrote:
> Please use struct pid properly.
>
> There is no guarantee that whoever opens /dev/dazukofs.0 will
> be in the global pid namespace.
>
> So you need to properly store a struct pid reference in your data
> structures, and call pid_vnr just before you pass the value to
> userspace.

Thanks. I was not aware of pid.h. I will make this change.

John Ogness

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 1/5] VFS: DazukoFS, stackable-fs, file access control
  2009-02-13 19:31     ` John Ogness
@ 2009-02-13 19:48       ` Al Viro
  2009-02-13 20:00       ` Jan Engelhardt
  1 sibling, 0 replies; 24+ messages in thread
From: Al Viro @ 2009-02-13 19:48 UTC (permalink / raw)
  To: John Ogness; +Cc: Jan Engelhardt, linux-kernel, malware-list, eparis, hch, alan

On Fri, Feb 13, 2009 at 08:31:03PM +0100, John Ogness wrote:

> If we want to forbid users from being able to do this, I think it
> would be simpler just to fail the mount if it is _not_ specified for
> direct stacking. This forces users to use a syntax that makes it very
> clear what they are doing.
> 
> Since mounting to a different directory could cause problems, I think
> it will be good to forbid it. I will make this change for the next
> patch series.

And just how are you going to do that?  Note that underlying directory
may be visible in many places to start with...

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 1/5] VFS: DazukoFS, stackable-fs, file access control
  2009-02-13 19:31     ` John Ogness
  2009-02-13 19:48       ` Al Viro
@ 2009-02-13 20:00       ` Jan Engelhardt
  2009-02-13 20:25         ` Al Viro
  1 sibling, 1 reply; 24+ messages in thread
From: Jan Engelhardt @ 2009-02-13 20:00 UTC (permalink / raw)
  To: John Ogness; +Cc: linux-kernel, viro, malware-list, eparis, hch, alan


On Friday 2009-02-13 20:31, John Ogness wrote:
>
>If we want to forbid users from being able to do this, I think it
>would be simpler just to fail the mount if it is _not_ specified for
>direct stacking. This forces users to use a syntax that makes it very
>clear what they are doing.
>
>Since mounting to a different directory could cause problems, I think
>it will be good to forbid it. I will make this change for the next
>patch series.

You could write an additional mount helper (and putting that into
/sbin/mount.dazukofs) that does all the security checks:

 - that the device is the same as mountpoint
 - that the device belonging to the underlying '/mnt' is not
   mounted anywhere else (in this namespace, at least)
 - exit(1) otherwise

Sure, it may not protect against all the cases Al can come up with,
but it is better than having nothing.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 1/5] VFS: DazukoFS, stackable-fs, file access control
  2009-02-13 20:00       ` Jan Engelhardt
@ 2009-02-13 20:25         ` Al Viro
  2009-02-14  8:43           ` John Ogness
  0 siblings, 1 reply; 24+ messages in thread
From: Al Viro @ 2009-02-13 20:25 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: John Ogness, linux-kernel, malware-list, eparis, hch, alan

On Fri, Feb 13, 2009 at 09:00:36PM +0100, Jan Engelhardt wrote:

> You could write an additional mount helper (and putting that into
> /sbin/mount.dazukofs) that does all the security checks:
> 
>  - that the device is the same as mountpoint
>  - that the device belonging to the underlying '/mnt' is not
>    mounted anywhere else (in this namespace, at least)
>  - exit(1) otherwise
> 
> Sure, it may not protect against all the cases Al can come up with,
> but it is better than having nothing.

It's still racy, at the very least.  Folks, seriously, you can not
rely on the underlying tree being inaccessible elsewhere.  Anything
that does stacking has to cope with that possibility; it's not
bypassable by userland helpers.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 1/5] VFS: DazukoFS, stackable-fs, file access control
  2009-02-13 20:25         ` Al Viro
@ 2009-02-14  8:43           ` John Ogness
  0 siblings, 0 replies; 24+ messages in thread
From: John Ogness @ 2009-02-14  8:43 UTC (permalink / raw)
  To: Al Viro; +Cc: Jan Engelhardt, linux-kernel, malware-list, eparis, hch, alan

On 2009-02-13, Al Viro <viro@ZenIV.linux.org.uk> wrote:
>> You could write an additional mount helper (and putting that into
>> /sbin/mount.dazukofs) that does all the security checks:
>> 
>>  - that the device is the same as mountpoint
>>  - that the device belonging to the underlying '/mnt' is not
>>    mounted anywhere else (in this namespace, at least)
>>  - exit(1) otherwise
>> 
>> Sure, it may not protect against all the cases Al can come up with,
>> but it is better than having nothing.
>
> It's still racy, at the very least.  Folks, seriously, you can not
> rely on the underlying tree being inaccessible elsewhere.  Anything
> that does stacking has to cope with that possibility; it's not
> bypassable by userland helpers.

Indeed. As long as the stackable filesystem is synchronizing with the
lower layer before doing read actions and synchronizes after all
actions, there should be no problem.

Currently I see a problem when a new directory is created directly on
the lower mount (rather than through the stackable filesystem). A
refcount for "." as seen from the stackable filesystem is then
incorrect. I will investigate this.

And I will look to see why there is any danger at all. There should
not be.

John Ogness

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 3/5] VFS: DazukoFS, stackable-fs, file access control
  2009-02-12 20:20       ` [PATCHv2 3/5] " Eric W. Biederman
@ 2009-02-17  8:55         ` John Ogness
  2009-02-18  0:41           ` Eric W. Biederman
  0 siblings, 1 reply; 24+ messages in thread
From: John Ogness @ 2009-02-17  8:55 UTC (permalink / raw)
  To: Eric W. Biederman; +Cc: linux-kernel, viro, malware-list, eparis, hch, alan

On 2009-02-12, ebiederm@xmission.com (Eric W. Biederman) wrote:
>> Patch 3: Creates /dev/dazukofs.ctrl to allow for groups to be added,
>>          listed, and deleted. Also, /dev/dazukofs.[0-9] devices are
>>          created to support multiple groups.
>
> Great another global namespace for process groups.  What is your
> plan for making this work in a container?

It is good that you mention it. This is something that I totally
missed. I am not yet certain what kind of a role DazukoFS should have
within containers.  I need to learn the container API's and see what
kinds of options are available. The next patchset (which it seems
won't be coming too quickly now) will address this in some form.

John Ogness

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 3/5] VFS: DazukoFS, stackable-fs, file access control
  2009-02-17  8:55         ` John Ogness
@ 2009-02-18  0:41           ` Eric W. Biederman
  0 siblings, 0 replies; 24+ messages in thread
From: Eric W. Biederman @ 2009-02-18  0:41 UTC (permalink / raw)
  To: John Ogness; +Cc: linux-kernel, viro, malware-list, eparis, hch, alan

John Ogness <dazukocode@ogness.net> writes:

> On 2009-02-12, ebiederm@xmission.com (Eric W. Biederman) wrote:
>>> Patch 3: Creates /dev/dazukofs.ctrl to allow for groups to be added,
>>>          listed, and deleted. Also, /dev/dazukofs.[0-9] devices are
>>>          created to support multiple groups.
>>
>> Great another global namespace for process groups.  What is your
>> plan for making this work in a container?
>
> It is good that you mention it. This is something that I totally
> missed. I am not yet certain what kind of a role DazukoFS should have
> within containers.  I need to learn the container API's and see what
> kinds of options are available. The next patchset (which it seems
> won't be coming too quickly now) will address this in some form.

Per filesystem would be fine.

Eric

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [malware-list] [PATCHv2 3/5] VFS: DazukoFS, stackable-fs, file access control
  2009-02-03 19:18     ` [PATCHv2 3/5] " John Ogness
  2009-02-03 19:19       ` [PATCHv2 4/5] " John Ogness
  2009-02-12 20:20       ` [PATCHv2 3/5] " Eric W. Biederman
@ 2009-02-21 18:11       ` Frantisek Hrbata
  2 siblings, 0 replies; 24+ messages in thread
From: Frantisek Hrbata @ 2009-02-21 18:11 UTC (permalink / raw)
  To: John Ogness; +Cc: linux-kernel, hch, alan, viro, malware-list

On Tue, 3 Feb 2009 20:18:29 +0100
John Ogness <dazukocode@ogness.net> wrote:

> +/**
> + * dazukofs_add_group - add a new group
> + * @name: the name of the group to add
> + *
> + * Description: This function is called by the device layer to add a new
> + * group. It returns success if the group has been successfully created
> + * or if the group already exists.
> + *
> + * Returns 0 on success.
> + */
> +int dazukofs_add_group(const char *name)
> +{
> +       int ret = 0;
> +       int already_exists;
> +       int available_id = 0;
> +       struct dazukofs_group *grp;
> +
> +       write_lock(&group_count_rwlock);
> +
> +       mutex_lock(&work_mutex);

Hi,

you cannot hold spinlock and try to acquire mutex.

write_lock() -> preempt_disable() vs mutex_lock -> schedule()

-FH

^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2009-02-21 18:20 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-02-03 19:14 [PATCHv2 0/5] VFS: DazukoFS, stackable-fs, file access control John Ogness
2009-02-03 19:15 ` [PATCHv2 1/5] " John Ogness
2009-02-03 19:17   ` [PATCHv2 2/5] " John Ogness
2009-02-03 19:18     ` [PATCHv2 3/5] " John Ogness
2009-02-03 19:19       ` [PATCHv2 4/5] " John Ogness
2009-02-03 19:20         ` [PATCHv2 5/5] " John Ogness
2009-02-12 20:24           ` Eric W. Biederman
2009-02-12 20:20       ` [PATCHv2 3/5] " Eric W. Biederman
2009-02-17  8:55         ` John Ogness
2009-02-18  0:41           ` Eric W. Biederman
2009-02-21 18:11       ` [malware-list] " Frantisek Hrbata
2009-02-12 16:00     ` [PATCHv2 2/5] " Jan Engelhardt
2009-02-13 19:33       ` John Ogness
2009-02-12 20:14     ` Eric W. Biederman
2009-02-13 19:39       ` John Ogness
2009-02-12 15:27   ` [PATCHv2 1/5] " Jan Engelhardt
2009-02-12 15:31     ` Al Viro
2009-02-12 15:59       ` Jan Engelhardt
2009-02-12 16:47         ` Al Viro
2009-02-13 19:31     ` John Ogness
2009-02-13 19:48       ` Al Viro
2009-02-13 20:00       ` Jan Engelhardt
2009-02-13 20:25         ` Al Viro
2009-02-14  8:43           ` John Ogness

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.