2008-12-21 14:55:10

by John Ogness

[permalink] [raw]
Subject: [PATCH 0/5] VFS: DazukoFS, stackable-fs, file access control

Hello,

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


2008-12-21 14:57:35

by John Ogness

[permalink] [raw]
Subject: [PATCH 1/5] VFS: DazukoFS, stackable-fs, file access control

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.28-rc9.

Signed-off-by: John Ogness <[email protected]>
---
Documentation/filesystems/dazukofs.txt | 81 ++
fs/Kconfig | 11
fs/Makefile | 1
fs/dazukofs/Makefile | 7
fs/dazukofs/dazukofs_fs.h | 163 ++++
fs/dazukofs/dentry.c | 183 +++++
fs/dazukofs/file.c | 340 +++++++++
fs/dazukofs/inode.c | 832 +++++++++++++++++++++++
fs/dazukofs/mmap.c | 117 +++
fs/dazukofs/super.c | 337 +++++++++
10 files changed, 2072 insertions(+)
Index: linux-2.6.27/fs/dazukofs/dazukofs_fs.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.27/fs/dazukofs/dazukofs_fs.h 2008-12-21 15:09:53.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 John Ogness
+ Author: John Ogness <[email protected]>
+
+ 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
+
+extern struct kmem_cache *dazukofs_dentry_info_cachep;
+extern struct kmem_cache *dazukofs_file_info_cachep;
+extern struct file_operations dazukofs_main_fops;
+extern const struct file_operations dazukofs_dir_fops;
+extern struct dentry_operations dazukofs_dops;
+extern 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_INFO(struct super_block *upper_sb)
+{
+ return upper_sb->s_fs_info;
+}
+
+static inline void SET_SB_INFO(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 ((struct dazukofs_sb_info *)upper_sb->s_fs_info)->lower_sb;
+}
+
+static inline void SET_LOWER_SB(struct super_block *upper_sb,
+ struct super_block *lower_sb)
+{
+ ((struct dazukofs_sb_info *)upper_sb->s_fs_info)->lower_sb = lower_sb;
+}
+
+static inline
+struct dazukofs_inode_info *GET_INODE_INFO(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 ((struct dazukofs_inode_info *)
+ container_of(upper_inode, struct dazukofs_inode_info,
+ vfs_inode))->lower_inode;
+}
+
+static inline void SET_LOWER_INODE(struct inode *upper_inode,
+ struct inode *lower_inode)
+{
+ ((struct dazukofs_inode_info *)
+ container_of(upper_inode, struct dazukofs_inode_info,
+ vfs_inode))->lower_inode = lower_inode;
+}
+
+static inline
+struct dazukofs_dentry_info *GET_DENTRY_INFO(struct dentry *upper_dentry)
+{
+ return upper_dentry->d_fsdata;
+}
+
+static inline void SET_DENTRY_INFO(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 ((struct dazukofs_dentry_info *)
+ upper_dentry->d_fsdata)->lower_dentry;
+}
+
+static inline struct vfsmount *GET_LOWER_MNT(struct dentry *upper_dentry)
+{
+ return ((struct dazukofs_dentry_info *)
+ upper_dentry->d_fsdata)->lower_mnt;
+}
+
+static inline void SET_LOWER_DENTRY(struct dentry *upper_dentry,
+ struct dentry *lower_dentry,
+ struct vfsmount *lower_mnt)
+{
+ ((struct dazukofs_dentry_info *)upper_dentry->d_fsdata)->lower_dentry =
+ lower_dentry;
+ ((struct dazukofs_dentry_info *)upper_dentry->d_fsdata)->lower_mnt =
+ lower_mnt;
+}
+
+static inline struct dazukofs_file_info *GET_FILE_INFO(struct file *upper_file)
+{
+ return upper_file->private_data;
+}
+
+static inline void SET_FILE_INFO(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 ((struct dazukofs_file_info *)
+ upper_file->private_data)->lower_file;
+}
+
+static inline void SET_LOWER_FILE(struct file *upper_file,
+ struct file *lower_file)
+{
+ ((struct dazukofs_file_info *)upper_file->private_data)->lower_file =
+ lower_file;
+}
+
+#endif /* __DAZUKOFS_FS_H */
Index: linux-2.6.27/fs/dazukofs/dentry.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.27/fs/dazukofs/dentry.c 2008-12-21 15:09:53.000000000 +0100
@@ -0,0 +1,183 @@
+/* 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 John Ogness
+ Author: John Ogness <[email protected]>
+
+ 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;
+ struct dentry *lower_dentry;
+ struct vfsmount *vfsmount_save;
+ struct dentry *dentry_save;
+ int valid;
+
+ valid = 1;
+
+ lower_dentry = GET_LOWER_DENTRY(dentry);
+
+ if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate)
+ goto out;
+
+ lower_mnt = GET_LOWER_MNT(dentry);
+
+ vfsmount_save = nd->path.mnt;
+ dentry_save = nd->path.dentry;
+
+ 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;
+
+ lower_inode = GET_LOWER_INODE(dentry->d_inode);
+
+ fsstack_copy_attr_all(dentry->d_inode, lower_inode, NULL);
+ }
+out:
+ 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 || !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_INFO(dentry)) {
+ dput(GET_LOWER_DENTRY(dentry));
+ mntput(GET_LOWER_MNT(dentry));
+
+ kmem_cache_free(dazukofs_dentry_info_cachep,
+ GET_DENTRY_INFO(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 && 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.27/fs/dazukofs/super.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.27/fs/dazukofs/super.c 2008-12-21 15:09:53.000000000 +0100
@@ -0,0 +1,337 @@
+/* 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 John Ogness
+ Author: John Ogness <[email protected]>
+
+ 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;
+
+ 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_INFO(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_INFO(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_INFO(sb->s_root, di);
+
+ SET_SB_INFO(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 = 0;
+
+ 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.27/fs/dazukofs/file.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.27/fs/dazukofs/file.c 2008-12-21 15:09:53.000000000 +0100
@@ -0,0 +1,340 @@
+/* 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 John Ogness
+ Author: John Ogness <[email protected]>
+
+ 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 "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 = -EINVAL;
+ 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 = -EINVAL;
+ struct file *lower_file = GET_LOWER_FILE(file);
+ loff_t pos_copy = *ppos;
+
+ if (!lower_file->f_op || !lower_file->f_op->read)
+ goto out;
+
+ 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));
+out:
+ 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 = -EINVAL;
+ 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)
+ goto out;
+
+ 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);
+out:
+ 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);
+ int err = -ENOTTY;
+
+ if (!lower_file || !lower_file->f_op || !lower_file->f_op->ioctl ||
+ !lower_inode) {
+ goto out;
+ }
+
+ err = lower_file->f_op->ioctl(lower_inode, lower_file, cmd, arg);
+out:
+ return err;
+}
+
+/**
+ * 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_INFO(file, kmem_cache_zalloc(dazukofs_file_info_cachep,
+ GFP_KERNEL));
+ if (!GET_FILE_INFO(file)) {
+ err = -ENOMEM;
+ goto error_out1;
+ }
+
+ lower_file = dentry_open(lower_dentry, lower_mnt, file->f_flags);
+ 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 0;
+
+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);
+ int err = 0;
+
+ if (!lower_file || !lower_file->f_op || !lower_file->f_op->flush)
+ goto out;
+
+ err = lower_file->f_op->flush(lower_file, td);
+out:
+ return err;
+}
+
+/**
+ * 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_INFO(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);
+ int err = -EINVAL;
+
+ if (!lower_file || !lower_file->f_op || !lower_file->f_op->fsync)
+ goto out;
+
+ err = lower_file->f_op->fsync(lower_file, lower_dentry, datasync);
+out:
+ return err;
+}
+
+/**
+ * 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);
+ int err = 0;
+
+ if (!lower_file || !lower_file->f_op || !lower_file->f_op->fasync)
+ goto out;
+
+ err = lower_file->f_op->fasync(fd, lower_file, flag);
+out:
+ return err;
+}
+
+/**
+ * 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
+ */
+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.27/fs/dazukofs/inode.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.27/fs/dazukofs/inode.c 2008-12-21 15:09:53.000000000 +0100
@@ -0,0 +1,832 @@
+/* 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 John Ogness
+ Author: John Ogness <[email protected]>
+
+ 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);
+ int err = 0;
+
+ if (!lower_inode) {
+ err = -ESTALE;
+ goto out;
+ }
+
+ if (lower_inode->i_sb != GET_LOWER_SB(sb)) {
+ iput(lower_inode);
+ err = -EXDEV;
+ goto out;
+ }
+
+ inode = iget5_locked(sb, (unsigned long)lower_inode,
+ dazukofs_inode_test, dazukofs_inode_set,
+ lower_inode);
+
+ if (!inode) {
+ iput(lower_inode);
+ err = -EACCES;
+ goto out;
+ }
+
+ 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);
+out:
+ return err;
+}
+
+/**
+ * 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_INFO(dentry, kmem_cache_zalloc(dazukofs_dentry_info_cachep,
+ GFP_KERNEL));
+
+ if (!GET_DENTRY_INFO(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 = -ENOENT;
+
+ 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 = -ENOENT;
+
+ 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 = -ENOENT;
+
+ 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 = -ENOENT;
+
+ 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 = 0;
+
+ if (!lower_dentry_inode) {
+ err = -ENOENT;
+ d_drop(dentry);
+ goto out;
+ }
+
+ if (!lower_dentry_inode->i_op ||
+ !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 = 0;
+
+ if (!lower_dentry_inode) {
+ err = -ENOENT;
+ d_drop(dentry);
+ goto out;
+ }
+
+ if (!lower_dentry_inode->i_op ||
+ !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 = 0;
+
+ if (!lower_dentry_inode) {
+ err = -ENOENT;
+ d_drop(dentry);
+ goto out;
+ }
+
+ if (!lower_dentry_inode->i_op ||
+ !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 = 0;
+
+ if (!lower_dentry_inode) {
+ err = -ENOENT;
+ d_drop(dentry);
+ goto out;
+ }
+
+ if (!lower_dentry_inode->i_op ||
+ !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 = 0;
+
+ if (!lower_dentry_inode) {
+ err = -ENOENT;
+ d_drop(dentry);
+ goto out;
+ }
+
+ if (!lower_dentry_inode->i_op ||
+ !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 = -ENOENT;
+
+ 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.27/fs/dazukofs/mmap.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.27/fs/dazukofs/mmap.c 2008-12-21 15:09:53.000000000 +0100
@@ -0,0 +1,117 @@
+/* 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 John Ogness
+ Author: John Ogness <[email protected]>
+
+ 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
+ */
+struct address_space_operations dazukofs_aops = {
+ .readpage = dazukofs_readpage,
+};
Index: linux-2.6.27/fs/dazukofs/Makefile
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.27/fs/dazukofs/Makefile 2008-12-21 15:09:53.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.27/fs/Kconfig
===================================================================
--- linux-2.6.27.orig/fs/Kconfig 2008-12-21 15:09:19.000000000 +0100
+++ linux-2.6.27/fs/Kconfig 2008-12-21 15:09:53.000000000 +0100
@@ -816,6 +816,17 @@
To compile this file system support as a module, choose M here: the
module will be called ecryptfs.

+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.
+
config HFS_FS
tristate "Apple Macintosh file system support (EXPERIMENTAL)"
depends on BLOCK && EXPERIMENTAL
Index: linux-2.6.27/fs/Makefile
===================================================================
--- linux-2.6.27.orig/fs/Makefile 2008-12-21 15:09:19.000000000 +0100
+++ linux-2.6.27/fs/Makefile 2008-12-21 15:09:53.000000000 +0100
@@ -86,6 +86,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.27/Documentation/filesystems/dazukofs.txt
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.27/Documentation/filesystems/dazukofs.txt 2008-12-21 15:09:53.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

2008-12-21 14:58:45

by John Ogness

[permalink] [raw]
Subject: [PATCH 2/5] VFS: DazukoFS, stackable-fs, file access control

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.28-rc9.

Signed-off-by: John Ogness <[email protected]>
---
Documentation/filesystems/dazukofs.txt | 89 +++
fs/Kconfig | 3
fs/dazukofs/Makefile | 3
fs/dazukofs/dev.c | 85 +++
fs/dazukofs/dev.h | 37 +
fs/dazukofs/event.c | 628 +++++++++++++++++++++++
fs/dazukofs/event.h | 32 +
fs/dazukofs/file.c | 7
fs/dazukofs/group_dev.c | 159 +++++
fs/dazukofs/super.c | 16
10 files changed, 1046 insertions(+), 13 deletions(-)
Index: linux-2.6.27/fs/dazukofs/dev.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.27/fs/dazukofs/dev.c 2008-12-21 15:10:09.000000000 +0100
@@ -0,0 +1,85 @@
+/* dazukofs: access control stackable filesystem
+
+ Copyright (C) 2008 John Ogness
+ Author: John Ogness <[email protected]>
+
+ 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 = 0;
+ 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);
+
+ /* create class */
+ 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.27/fs/dazukofs/group_dev.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.27/fs/dazukofs/group_dev.c 2008-12-21 15:10:09.000000000 +0100
@@ -0,0 +1,159 @@
+/* dazukofs: access control stackable filesystem
+
+ Copyright (C) 2008 John Ogness
+ Author: John Ogness <[email protected]>
+
+ 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 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 = 0;
+ 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.27/fs/dazukofs/event.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.27/fs/dazukofs/event.c 2008-12-21 15:10:09.000000000 +0100
@@ -0,0 +1,628 @@
+/* dazukofs: access control stackable filesystem
+
+ Copyright (C) 2008 John Ogness
+ Author: John Ogness <[email protected]>
+
+ 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 "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 = 0;
+
+ /* 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);
+ 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.27/fs/dazukofs/dev.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.27/fs/dazukofs/dev.h 2008-12-21 15:10:09.000000000 +0100
@@ -0,0 +1,37 @@
+/* dazukofs: access control stackable filesystem
+
+ Copyright (C) 2008 John Ogness
+ Author: John Ogness <[email protected]>
+
+ 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
+
+struct class;
+
+#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.27/fs/dazukofs/Makefile
===================================================================
--- linux-2.6.27.orig/fs/dazukofs/Makefile 2008-12-21 15:09:53.000000000 +0100
+++ linux-2.6.27/fs/dazukofs/Makefile 2008-12-21 15:10:09.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.27/fs/dazukofs/super.c
===================================================================
--- linux-2.6.27.orig/fs/dazukofs/super.c 2008-12-21 15:09:53.000000000 +0100
+++ linux-2.6.27/fs/dazukofs/super.c 2008-12-21 15:10:09.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;
@@ -306,19 +307,25 @@
{
int err = 0;

- 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;
}
@@ -327,11 +334,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.27/fs/dazukofs/file.c
===================================================================
--- linux-2.6.27.orig/fs/dazukofs/file.c 2008-12-21 15:09:53.000000000 +0100
+++ linux-2.6.27/fs/dazukofs/file.c 2008-12-21 15:10:09.000000000 +0100
@@ -28,6 +28,7 @@
#include <linux/fs_stack.h>

#include "dazukofs_fs.h"
+#include "event.h"

/**
* Description: Called when the VFS needs to move the file position index.
@@ -174,7 +175,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_INFO(file, kmem_cache_zalloc(dazukofs_file_info_cachep,
GFP_KERNEL));
Index: linux-2.6.27/fs/dazukofs/event.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.27/fs/dazukofs/event.h 2008-12-21 15:10:09.000000000 +0100
@@ -0,0 +1,32 @@
+/* dazukofs: access control stackable filesystem
+
+ Copyright (C) 2008 John Ogness
+ Author: John Ogness <[email protected]>
+
+ 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.27/fs/Kconfig
===================================================================
--- linux-2.6.27.orig/fs/Kconfig 2008-12-21 15:09:53.000000000 +0100
+++ linux-2.6.27/fs/Kconfig 2008-12-21 15:10:09.000000000 +0100
@@ -820,7 +820,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.27/Documentation/filesystems/dazukofs.txt
===================================================================
--- linux-2.6.27.orig/Documentation/filesystems/dazukofs.txt 2008-12-21 15:09:53.000000000 +0100
+++ linux-2.6.27/Documentation/filesystems/dazukofs.txt 2008-12-21 15:10:09.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.

2008-12-21 14:59:49

by John Ogness

[permalink] [raw]
Subject: [PATCH 3/5] VFS: DazukoFS, stackable-fs, file access control

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.28-rc9.

Signed-off-by: John Ogness <[email protected]>
---
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 | 537 +++++++++++++++++++++--
fs/dazukofs/event.h | 10
fs/dazukofs/group_dev.c | 107 +++-
8 files changed, 867 insertions(+), 84 deletions(-)
Index: linux-2.6.27/fs/dazukofs/event.c
===================================================================
--- linux-2.6.27.orig/fs/dazukofs/event.c 2008-12-21 15:10:09.000000000 +0100
+++ linux-2.6.27/fs/dazukofs/event.c 2008-12-21 15:10:24.000000000 +0100
@@ -25,6 +25,7 @@
#include <linux/mount.h>
#include <linux/freezer.h>

+#include "dev.h"
#include "dazukofs_fs.h"

struct dazukofs_proc {
@@ -55,20 +56,34 @@
};

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 wait_queue_head_t __group_count_queue;
+static int __group_count;
+static int __group_count_busy; /* if set, __group_count is protected */

-/* protects: grp->members, last_event_id,
+/* protects: group_list, grp->members, last_event_id,
* todo_list, working_list */
static struct mutex work_mutex;

+/* protects: __group_count_busy */
+static struct mutex group_count_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;

@@ -86,11 +101,19 @@
{
mutex_init(&proc_mutex);
mutex_init(&work_mutex);
+ mutex_init(&group_count_mutex);
+
INIT_LIST_HEAD(&proc_list.list);
+ INIT_LIST_HEAD(&group_list.list);
+
+ init_waitqueue_head(&__group_count_queue);

- 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",
@@ -109,6 +132,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)
@@ -117,6 +142,81 @@
}

/**
+ * capture_group_count - try to lock the group count
+ * @cache: a black-box argument to make sure that a process does not try
+ * lock the group count recursively
+ *
+ * Description: This function will attempt to lock the group count. If it
+ * is already locked, this function returns an error. Kernel locking is
+ * only used to protect the "busy" flag. The group count is handled this
+ * way because it needs to be protected AND there is a lot of sleeping
+ * and blocking between the "get" and "put" of the group count.
+ *
+ * The cache argument is needed because of the way wait_event_freezable()
+ * in get_group_count() is implememted. Even if the condition is
+ * successful, it will be called one more time. Both calls must return
+ * success. The cache is set on success. On the next call we notice that
+ * it is set and immediately return success again.
+ *
+ * Returns 0 if the group count lock was acquired.
+ */
+static int capture_group_count(int *cache)
+{
+ int err = 0;
+
+ if (*cache)
+ return 0;
+
+ mutex_lock(&group_count_mutex);
+ if (__group_count_busy)
+ err = -1;
+ else
+ __group_count_busy = 1;
+ mutex_unlock(&group_count_mutex);
+
+ if (err == 0)
+ *cache = 1;
+ return err;
+}
+
+/**
+ * get_group_count - lock the group count (blocking)
+ *
+ * Description: This function will block until the group count lock has been
+ * acquired (or the process was interrupted). If successful, a process is
+ * required to later call put_group_count() to free the lock.
+ *
+ * It is allowed to sleep/block while this lock is held. However, no other
+ * processes may be able to acquire the lock during this time.
+ *
+ * Returns 0 if the group count lock was acquired.
+ */
+static int get_group_count(void)
+{
+ int cache = 0;
+ int ret = wait_event_freezable(__group_count_queue,
+ capture_group_count(&cache) == 0);
+ if (ret == 0)
+ ret = __group_count;
+ return ret;
+}
+
+/**
+ * put_group_count - releases the group count lock
+ *
+ * Description: This function will release the group count lock. The
+ * function must only be called after a successful call to
+ * get_group_count().
+ */
+static void put_group_count(void)
+{
+ mutex_lock(&group_count_mutex);
+ __group_count_busy = 0;
+ mutex_unlock(&group_count_mutex);
+ wake_up(&__group_count_queue);
+}
+
+/**
* release_event - release (and possible free) an event
* @evt: the event to release
* @decrement_assigned: flag to signal if the assigned count should be
@@ -210,10 +310,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);

@@ -228,17 +335,282 @@
*/
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;
+ int grp_count = get_group_count();
+
+ if (grp_count < 0) {
+ ret = grp_count;
+ goto out1;
+ }
+
+ 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 out2;
+
+ /* if we are here, the group doesn't already exist */
+
+ /* do we have room for a new group? */
+ if (grp_count == GROUP_COUNT) {
+ ret = -EPERM;
+ goto out2;
+ }
+
+ grp = create_group(name, available_id);
+ if (!grp) {
+ ret = -ENOMEM;
+ goto out2;
+ }
+
+ mutex_lock(&work_mutex);
+ list_add_tail(&grp->list, &group_list.list);
+ mutex_unlock(&work_mutex);
+
+ __group_count++;
+out2:
+ put_group_count();
+out1:
+ 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;
+ int grp_count = get_group_count();
+
+ if (grp_count < 0) {
+ ret = grp_count;
+ goto out1;
+ }
+
+ if (grp_count == 0)
+ goto out2;
+
+ 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);
+out2:
+ put_group_count();
+out1:
+ 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
@@ -295,14 +667,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;
@@ -311,19 +688,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);
@@ -333,44 +713,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;
@@ -388,16 +791,23 @@
*/
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 grp_count = get_group_count();
int err;

- if (check_access_precheck())
+ if (grp_count < 0)
+ return grp_count;
+
+ if (check_access_precheck(grp_count)) {
+ put_group_count();
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, grp_count)) {
+ put_group_count();
err = -ENOMEM;
goto out;
}
@@ -406,7 +816,9 @@
evt->mnt = mntget(mnt);
evt->pid = current->pid;

- assign_event_to_group(evt, ec);
+ assign_event_to_groups(evt, ec_array);
+
+ put_group_count();

/* wait until event completely processed or signal */
err = wait_event_freezable(evt->queue, event_assigned(evt) == 0);
@@ -421,6 +833,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
*
@@ -430,9 +843,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;
@@ -440,6 +854,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;
@@ -456,6 +884,7 @@
release_event(evt, 1, deny);
else
ret = -EFAULT;
+out:
return ret;
}

@@ -589,6 +1018,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
@@ -599,18 +1029,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);
@@ -624,5 +1079,7 @@
}
}
}
+ atomic_dec(&grp->use_count);
+out:
return ret;
}
Index: linux-2.6.27/fs/dazukofs/Makefile
===================================================================
--- linux-2.6.27.orig/fs/dazukofs/Makefile 2008-12-21 15:10:09.000000000 +0100
+++ linux-2.6.27/fs/dazukofs/Makefile 2008-12-21 15:10:24.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.27/fs/dazukofs/ctrl_dev.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.27/fs/dazukofs/ctrl_dev.c 2008-12-21 15:10:24.000000000 +0100
@@ -0,0 +1,198 @@
+/* dazukofs: access control stackable filesystem
+
+ Copyright (C) 2008 John Ogness
+ Author: John Ogness <[email protected]>
+
+ 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 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.27/fs/dazukofs/group_dev.c
===================================================================
--- linux-2.6.27.orig/fs/dazukofs/group_dev.c 2008-12-21 15:10:09.000000000 +0100
+++ linux-2.6.27/fs/dazukofs/group_dev.c 2008-12-21 15:10:24.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 struct file_operations group_fops_##group_id = { \
+ .owner = THIS_MODULE, \
+ .read = dazukofs_group_read_##group_id, \
+ .write = dazukofs_group_write_##group_id, \
+};

-static 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 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 = 0;
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.27/fs/dazukofs/dev.c
===================================================================
--- linux-2.6.27.orig/fs/dazukofs/dev.c 2008-12-21 15:10:09.000000000 +0100
+++ linux-2.6.27/fs/dazukofs/dev.c 2008-12-21 15:10:24.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);
@@ -55,20 +55,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:
@@ -79,7 +87,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.27/fs/dazukofs/dev.h
===================================================================
--- linux-2.6.27.orig/fs/dazukofs/dev.h 2008-12-21 15:10:09.000000000 +0100
+++ linux-2.6.27/fs/dazukofs/dev.h 2008-12-21 15:10:24.000000000 +0100
@@ -24,6 +24,7 @@
struct class;

#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.27/fs/dazukofs/event.h
===================================================================
--- linux-2.6.27.orig/fs/dazukofs/event.h 2008-12-21 15:10:09.000000000 +0100
+++ linux-2.6.27/fs/dazukofs/event.h 2008-12-21 15:10:24.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.27/Documentation/filesystems/dazukofs.txt
===================================================================
--- linux-2.6.27.orig/Documentation/filesystems/dazukofs.txt 2008-12-21 15:10:09.000000000 +0100
+++ linux-2.6.27/Documentation/filesystems/dazukofs.txt 2008-12-21 15:10:24.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.

2008-12-21 15:01:12

by John Ogness

[permalink] [raw]
Subject: [PATCH 4/5] VFS: DazukoFS, stackable-fs, file access control

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.28-rc9.

Signed-off-by: John Ogness <[email protected]>
---
Documentation/filesystems/dazukofs.txt | 24 ++++-
fs/dazukofs/ctrl_dev.c | 15 ++-
fs/dazukofs/event.c | 102 +++++++++++++++++++++--
fs/dazukofs/event.h | 7 +
fs/dazukofs/group_dev.c | 30 ++++++
5 files changed, 161 insertions(+), 17 deletions(-)
Index: linux-2.6.27/fs/dazukofs/ctrl_dev.c
===================================================================
--- linux-2.6.27.orig/fs/dazukofs/ctrl_dev.c 2008-12-21 15:10:24.000000000 +0100
+++ linux-2.6.27/fs/dazukofs/ctrl_dev.c 2008-12-21 15:10:38.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.27/fs/dazukofs/event.c
===================================================================
--- linux-2.6.27.orig/fs/dazukofs/event.c 2008-12-21 15:10:24.000000000 +0100
+++ linux-2.6.27/fs/dazukofs/event.c 2008-12-21 15:10:38.000000000 +0100
@@ -64,6 +64,8 @@
wait_queue_head_t queue;
struct dazukofs_event_container working_list;
atomic_t use_count;
+ int tracking;
+ int track_count;
int deprecated;
};

@@ -366,9 +368,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
@@ -379,11 +382,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;
@@ -404,6 +413,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;
@@ -427,6 +438,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
@@ -435,7 +447,7 @@
*
* 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;

@@ -454,20 +466,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;
@@ -481,7 +500,8 @@
}

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++;
}
@@ -498,7 +518,7 @@
goto out2;
}

- grp = create_group(name, available_id);
+ grp = create_group(name, available_id, track);
if (!grp) {
ret = -ENOMEM;
goto out2;
@@ -518,14 +538,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;
@@ -832,6 +855,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.27/fs/dazukofs/event.h
===================================================================
--- linux-2.6.27.orig/fs/dazukofs/event.h 2008-12-21 15:10:24.000000000 +0100
+++ linux-2.6.27/fs/dazukofs/event.h 2008-12-21 15:10:38.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.27/fs/dazukofs/group_dev.c
===================================================================
--- linux-2.6.27.orig/fs/dazukofs/group_dev.c 2008-12-21 15:10:24.000000000 +0100
+++ linux-2.6.27/fs/dazukofs/group_dev.c 2008-12-21 15:10:38.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 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.27/Documentation/filesystems/dazukofs.txt
===================================================================
--- linux-2.6.27.orig/Documentation/filesystems/dazukofs.txt 2008-12-21 15:10:24.000000000 +0100
+++ linux-2.6.27/Documentation/filesystems/dazukofs.txt 2008-12-21 15:10:38.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:

2008-12-21 15:04:34

by John Ogness

[permalink] [raw]
Subject: [PATCH 5/5] VFS: DazukoFS, stackable-fs, file access control

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.28-rc9.

Signed-off-by: John Ogness <[email protected]>
---
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.27/fs/dazukofs/ign_dev.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.27/fs/dazukofs/ign_dev.c 2008-12-21 15:10:49.000000000 +0100
@@ -0,0 +1,186 @@
+/* dazukofs: access control stackable filesystem
+
+ Copyright (C) 2008 John Ogness
+ Author: John Ogness <[email protected]>
+
+ 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 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.27/fs/dazukofs/Makefile
===================================================================
--- linux-2.6.27.orig/fs/dazukofs/Makefile 2008-12-21 15:10:24.000000000 +0100
+++ linux-2.6.27/fs/dazukofs/Makefile 2008-12-21 15:10:49.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.27/fs/dazukofs/event.c
===================================================================
--- linux-2.6.27.orig/fs/dazukofs/event.c 2008-12-21 15:10:38.000000000 +0100
+++ linux-2.6.27/fs/dazukofs/event.c 2008-12-21 15:10:49.000000000 +0100
@@ -707,6 +707,10 @@
if (!check_recursion())
return -1;

+ /* am I an ignored process? */
+ if (!dazukofs_check_ignore_process())
+ return -1;
+
return 0;
}

Index: linux-2.6.27/fs/dazukofs/dev.c
===================================================================
--- linux-2.6.27.orig/fs/dazukofs/dev.c 2008-12-21 15:10:24.000000000 +0100
+++ linux-2.6.27/fs/dazukofs/dev.c 2008-12-21 15:10:49.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);
@@ -60,23 +60,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:
@@ -85,11 +93,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.27/fs/dazukofs/dev.h
===================================================================
--- linux-2.6.27.orig/fs/dazukofs/dev.h 2008-12-21 15:10:24.000000000 +0100
+++ linux-2.6.27/fs/dazukofs/dev.h 2008-12-21 15:10:49.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.27/Documentation/filesystems/dazukofs.txt
===================================================================
--- linux-2.6.27.orig/Documentation/filesystems/dazukofs.txt 2008-12-21 15:10:38.000000000 +0100
+++ linux-2.6.27/Documentation/filesystems/dazukofs.txt 2008-12-21 15:10:49.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.

2008-12-21 15:48:51

by Bastian Blank

[permalink] [raw]
Subject: Re: [PATCH 1/5] VFS: DazukoFS, stackable-fs, file access control

On Sun, Dec 21, 2008 at 03:56:42PM +0100, John Ogness wrote:
> +#ifndef __DAZUKOFS_FS_H
> +#define __DAZUKOFS_FS_H

There are several (okay, all) includes missing.

> +static inline
> +struct dazukofs_sb_info *GET_SB_INFO(struct super_block *upper_sb)

Coding-style.

> +static inline void SET_LOWER_INODE(struct inode *upper_inode,
> + struct inode *lower_inode)
> +{
> + ((struct dazukofs_inode_info *)
> + container_of(upper_inode, struct dazukofs_inode_info,
> + vfs_inode))->lower_inode = lower_inode;
> +}

Please make such cast cascades explicit:

| struct dazukofs_inode_info *info = container_of(...);
| info->lower_inode = lower_inode;

There are other people which want to read the code.

> +static int dazukofs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
> +{
> + struct vfsmount *lower_mnt;
> + struct dentry *lower_dentry;
> + struct vfsmount *vfsmount_save;
> + struct dentry *dentry_save;
> + int valid;
> +
> + valid = 1;
> +
> + lower_dentry = GET_LOWER_DENTRY(dentry);
> +
> + if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate)
> + goto out;

Why do you use goto instead of return?

> +static int dazukofs_d_hash(struct dentry *dentry, struct qstr *name)
> +{
> + struct dentry *lower_dentry = GET_LOWER_DENTRY(dentry);
> +
> + if (!lower_dentry || !lower_dentry->d_op ||
> + !lower_dentry->d_op->d_hash) {
> + return 0;
> + }

You mix rather different coding styles through the whole code. Also why
do you say that lower_dentry can be 0 in _hash, but not in _revalidate?

> +static void dazukofs_d_release(struct dentry *dentry)
> +{
> + if (GET_DENTRY_INFO(dentry)) {
> + dput(GET_LOWER_DENTRY(dentry));
> + mntput(GET_LOWER_MNT(dentry));

Why do you push anything out in other functions while making it explicit
would make it much easier readable?

Bastian

--
Killing is stupid; useless!
-- McCoy, "A Private Little War", stardate 4211.8

2008-12-21 17:57:30

by John Ogness

[permalink] [raw]
Subject: Re: [PATCH 1/5] VFS: DazukoFS, stackable-fs, file access control

On 2008-12-21, Bastian Blank <[email protected]> wrote:
> There are several (okay, all) includes missing.

Indeed. I will fix this.

>> +static inline
>> +struct dazukofs_sb_info *GET_SB_INFO(struct super_block *upper_sb)
>
> Coding-style.

The functions were originally macros. The capitalization was inspired
by macros such as IS_ERR, PTR_ERR, MKDEV, etc. That is the
explanation, but it doesn't make it ok. I will change it.

>> +static inline void SET_LOWER_INODE(struct inode *upper_inode,
>> + struct inode *lower_inode)
>> +{
>> + ((struct dazukofs_inode_info *)
>> + container_of(upper_inode, struct dazukofs_inode_info,
>> + vfs_inode))->lower_inode = lower_inode;
>> +}
>
> Please make such cast cascades explicit:
>
> | struct dazukofs_inode_info *info = container_of(...);
> | info->lower_inode = lower_inode;
>
> There are other people which want to read the code.

Agreed.

>> +static int dazukofs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
>> +{
>> + struct vfsmount *lower_mnt;
>> + struct dentry *lower_dentry;
>> + struct vfsmount *vfsmount_save;
>> + struct dentry *dentry_save;
>> + int valid;
>> +
>> + valid = 1;
>> +
>> + lower_dentry = GET_LOWER_DENTRY(dentry);
>> +
>> + if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate)
>> + goto out;
>
> Why do you use goto instead of return?

I use a goto here so that only 1 exit point exists within the
function. However, that probably doesn't make much sense when such
basic sanity checks are performed at the beginning of the function. I
will change this.

>> +static int dazukofs_d_hash(struct dentry *dentry, struct qstr *name)
>> +{
>> + struct dentry *lower_dentry = GET_LOWER_DENTRY(dentry);
>> +
>> + if (!lower_dentry || !lower_dentry->d_op ||
>> + !lower_dentry->d_op->d_hash) {
>> + return 0;
>> + }
>
> You mix rather different coding styles through the whole code.

When I wrote the stacking code, it was the first time I started using
the Linux coding style. I will go back through and see what I can
clean up.

> Also why do you say that lower_dentry can be 0 in _hash, but not in
> _revalidate?

_hash doesn't need this check. I will remove it.

>> +static void dazukofs_d_release(struct dentry *dentry)
>> +{
>> + if (GET_DENTRY_INFO(dentry)) {
>> + dput(GET_LOWER_DENTRY(dentry));
>> + mntput(GET_LOWER_MNT(dentry));
>
> Why do you push anything out in other functions while making it
> explicit would make it much easier readable?

For me, writing a stackable filesystem was quite complex. Using the
all-caps functions for upper/lower layer translations helped me to
easily see what I was doing. I agree that the all-caps thing is quite
ugly. I will change that, but I would prefer to keep the translation
functions as separate inline functions. (The same technique can be
seen in ecryptfs as well.)

John Ogness

2008-12-23 18:00:37

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH 0/5] VFS: DazukoFS, stackable-fs, file access control

Hi!

> 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.

Somewhere here you should mention that unlike other solutions that
work 100% of time, dazuko is be design racy and may still allow
viruses to be spread from linux system when mmap is used.
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

2008-12-23 18:56:19

by John Ogness

[permalink] [raw]
Subject: Re: [PATCH 0/5] VFS: DazukoFS, stackable-fs, file access control

On 2008-12-23, Pavel Machek <[email protected]> wrote:
>> 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.
>
> Somewhere here you should mention that unlike other solutions that
> work 100% of time, dazuko is be design racy and may still allow
> viruses to be spread from linux system when mmap is used.

This thread is about DazukoFS. I feel there is no need to discuss
previous incarnations of Dazuko, all of which share _no_ code with
DazukoFS.

If you are aware of a race condition in DazukoFS, please report
it. Thank you.

John Ogness

2008-12-24 08:53:19

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH 0/5] VFS: DazukoFS, stackable-fs, file access control

On Tue 2008-12-23 19:55:27, John Ogness wrote:
> On 2008-12-23, Pavel Machek <[email protected]> wrote:
> >> 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.
> >
> > Somewhere here you should mention that unlike other solutions that
> > work 100% of time, dazuko is be design racy and may still allow
> > viruses to be spread from linux system when mmap is used.
>
> This thread is about DazukoFS. I feel there is no need to discuss
> previous incarnations of Dazuko, all of which share _no_ code with
> DazukoFS.
>
> If you are aware of a race condition in DazukoFS, please report
> it. Thank you.

I'm surprised, do you claim there is none?

So how do you handle mmap(...MAP_SHARED) case?
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

2008-12-24 12:12:19

by John Ogness

[permalink] [raw]
Subject: Re: [PATCH 0/5] VFS: DazukoFS, stackable-fs, file access control

On 2008-12-24, Pavel Machek <[email protected]> wrote:
>> If you are aware of a race condition in DazukoFS, please report
>> it. Thank you.
>
> I'm surprised, do you claim there is none?

It depends on how you want to define "race condition". DazukoFS (in
its current form) closes as many holes as default UNIX permissions or
SElinux. However, changing permissions (i.e. scanning files) _after_
two parties already have an open handle to the file is currently not
covered. But this issue is not unique to DazukoFS (although as a
stackable filesystem, DazukoFS could support this case as well).

> So how do you handle mmap(...MAP_SHARED) case?

DazukoFS currently does not support writing to memory mapped files for
this very reason. It needs to be determined how writing to memory
mapped files should be handled. I am choosing to tackle this issue
together with the Linux kernel community, rather than trying to solve
it within the much smaller Dazuko community.

Although memory mapped writing is currently not supported, I believe
DazukoFS still offers value to the Linux kernel. I am not aware of any
network services that require memory mapped writing for their sharing.

John Ogness

2008-12-26 18:30:38

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH 0/5] VFS: DazukoFS, stackable-fs, file access control

On Wed 2008-12-24 13:11:29, John Ogness wrote:
> On 2008-12-24, Pavel Machek <[email protected]> wrote:
> >> If you are aware of a race condition in DazukoFS, please report
> >> it. Thank you.
> >
> > I'm surprised, do you claim there is none?
>
> It depends on how you want to define "race condition". DazukoFS (in
> its current form) closes as many holes as default UNIX permissions or
> SElinux. However, changing permissions (i.e. scanning files) _after_
> two parties already have an open handle to the file is currently not
> covered. But this issue is not unique to DazukoFS (although as a
> stackable filesystem, DazukoFS could support this case as well).

Not unique to dazukofs, but regression to scanning at nfsd level,
right? So I guess it should be mentioned in changelog if you compare
yourself to solutions that do not have this problem.
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html