Re: [PATCH][RFC] Simple tamper-proof device filesystem.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Quoting Tetsuo Handa ([email protected]):
> Hello.
> 
> Thank you for attending discussion for previous posting
> (starting from http://lkml.org/lkml/2007/12/16/23 ).
> 
> The previous posting was for feasibility test to know
> whether this kind of trivial filesystem is acceptable for mainline.
> 
> Now, it seems that there is a little chance for accepting.
> Therefore I rebased the patch using the -mm tree.
> 
> Regards.
> ----------
> Subject: Simple tamper-proof device filesystem.
> 
> The goal of this filesystem is to guarantee that
> "applications using well-known device locations under /dev
> get the device they want" (e.g. an application that accesses /dev/null can
> always get a character special device with major=1 and minor=3).
> 
> This idea sounds silly? Indeed, if you think the root can do whatever
> he/she wants do do. But this filesystem makes sense when used with
> access control mechanisms like MAC (mandatory access control).
> I want to use this filesystem in case where a process with root privilege was
> hijacked but the behavior of the hijacked process is still restricted by MAC.
> 
> Why not use FUSE?
> 
>   Because /dev has to be available through the lifetime of the kernel.
>   It is not acceptable if /dev stops working due to SIGKILL or OOM-killer.
> 
> Why not use SELinux?
> 
>   Because SELinux doesn't guarantee filename and its attribute.
>   As far as I know, no MAC implementation can handle filename and its attribute.
>   I guess this is because
> 
>     Filename and its attributes pairs are conventionally considered as
>     constant and reliable.
> 
>     It makes the MAC's policy syntax complicated to describe this attribute
>     enforcement information in MAC's policy.
> 
>   I want to add functionality that the MACs are missing.
>   Instead of adding this functionality per MAC,
>   I propose to add it as ground work, to be combined with any MAC.
> 
> Why not drop CAP_MKNOD?
> 
>   Dropping CAP_MKNOD is not enough for emulating this filesystem because
>   a process can still rename()/unlink() to break filename and its attributes
>   handling (e.g. mv /dev/sda1 /dev/sda1.tmp; mv /dev/sda2 /dev/sda1;
>   mv /dev/sda1.tmp /dev/sda2 or unlink /dev/null; touch /dev/null ).
> 
> This time, I'm implementing this filesystem as an extension to tmpfs
> because what this filesystem does are nothing but check filename and
> its attributes in addition to what tmpfs does.
> 
> Signed-off-by: Tetsuo Handa <[email protected]>
> ---
>  fs/ramfs/inode.c   |  101 ++++-
>  fs/ramfs/syaoran.h | 1066 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 1160 insertions(+), 7 deletions(-)
> 
> --- linux-2.6-mm.orig/fs/ramfs/inode.c
> +++ linux-2.6-mm/fs/ramfs/inode.c
> @@ -35,6 +35,7 @@
>  #include <linux/sched.h>
>  #include <asm/uaccess.h>
>  #include "internal.h"
> +#include "syaoran.h"
> 
>  /* some random number */
>  #define RAMFS_MAGIC	0x858458f6
> @@ -49,7 +50,8 @@ static struct backing_dev_info ramfs_bac
>  			  BDI_CAP_READ_MAP | BDI_CAP_WRITE_MAP | BDI_CAP_EXEC_MAP,
>  };
> 
> -struct inode *ramfs_get_inode(struct super_block *sb, int mode, dev_t dev)
> +struct inode *__ramfs_get_inode(struct super_block *sb, int mode, dev_t dev,
> +				const int mac)
>  {
>  	struct inode * inode = new_inode(sb);
> 
> @@ -65,10 +67,19 @@ struct inode *ramfs_get_inode(struct sup
>  		switch (mode & S_IFMT) {
>  		default:
>  			init_special_inode(inode, mode, dev);
> +			if (mac) {
> +				if (S_ISBLK(mode))
> +					inode->i_fop = &wrapped_def_blk_fops;
> +				else if (S_ISCHR(mode))
> +					inode->i_fop = &wrapped_def_chr_fops;
> +				inode->i_op = &syaoran_file_inode_operations;
> +			}
>  			break;
>  		case S_IFREG:
>  			inode->i_op = &ramfs_file_inode_operations;
>  			inode->i_fop = &ramfs_file_operations;
> +			if (mac)
> +				inode->i_op = &syaoran_file_inode_operations;
>  			break;
>  		case S_IFDIR:
>  			inode->i_op = &ramfs_dir_inode_operations;
> @@ -79,12 +90,19 @@ struct inode *ramfs_get_inode(struct sup
>  			break;
>  		case S_IFLNK:
>  			inode->i_op = &page_symlink_inode_operations;
> +			if (mac)
> +				inode->i_op = &syaoran_symlink_inode_operations;
>  			break;
>  		}
>  	}
>  	return inode;
>  }
> 
> +struct inode *ramfs_get_inode(struct super_block *sb, int mode, dev_t dev)
> +{
> +	return __ramfs_get_inode(sb, mode, dev, 0);
> +}
> +
>  /*
>   * File creation. Allocate an inode, and we're done..
>   */
> @@ -92,9 +110,17 @@ struct inode *ramfs_get_inode(struct sup
>  static int
>  ramfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
>  {
> -	struct inode * inode = ramfs_get_inode(dir->i_sb, mode, dev);
> +	struct inode *inode;
>  	int error = -ENOSPC;
> 
> +	/*** SYAORAN start. ***/
> +	if (dir->i_sb->s_op == &syaoran_ops) {
> +		if (syaoran_may_create_node(dentry, mode, dev) < 0)
> +			return -EPERM;
> +		inode = syaoran_get_inode(dir->i_sb, mode, dev);
> +	/*** SYAORAN end. ***/
> +	} else
> +		inode = ramfs_get_inode(dir->i_sb, mode, dev);
>  	if (inode) {
>  		if (dir->i_mode & S_ISGID) {
>  			inode->i_gid = dir->i_gid;
> @@ -127,7 +153,14 @@ static int ramfs_symlink(struct inode * 
>  	struct inode *inode;
>  	int error = -ENOSPC;
> 
> -	inode = ramfs_get_inode(dir->i_sb, S_IFLNK|S_IRWXUGO, 0);
> +	/*** SYAORAN start. ***/
> +	if (dir->i_sb->s_op == &syaoran_ops) {
> +		if (syaoran_may_create_node(dentry, S_IFLNK, 0) < 0)
> +			return -EPERM;
> +		inode = syaoran_get_inode(dir->i_sb, S_IFLNK|S_IRWXUGO, 0);
> +	/*** SYAORAN end. ***/
> +	} else
> +		inode = ramfs_get_inode(dir->i_sb, S_IFLNK|S_IRWXUGO, 0);
>  	if (inode) {
>  		int l = strlen(symname)+1;
>  		error = page_symlink(inode, symname, l);
> @@ -146,13 +179,14 @@ static int ramfs_symlink(struct inode * 
>  static const struct inode_operations ramfs_dir_inode_operations = {
>  	.create		= ramfs_create,
>  	.lookup		= simple_lookup,
> -	.link		= simple_link,
> -	.unlink		= simple_unlink,
> +	.link		= ramfs_link,
> +	.unlink		= ramfs_unlink,
>  	.symlink	= ramfs_symlink,
>  	.mkdir		= ramfs_mkdir,
> -	.rmdir		= simple_rmdir,
> +	.rmdir		= ramfs_rmdir,
>  	.mknod		= ramfs_mknod,
> -	.rename		= simple_rename,
> +	.rename		= ramfs_rename,
> +	.setattr        = ramfs_setattr,
>  };
> 
>  static const struct super_operations ramfs_ops = {
> @@ -184,6 +218,35 @@ static int ramfs_fill_super(struct super
>  	return 0;
>  }
> 
> +static int syaoran_fill_super(struct super_block *sb, void *data, int silent)
> +{
> +	struct inode *inode;
> +	struct dentry *root;
> +	int error;
> +
> +	sb->s_maxbytes = MAX_LFS_FILESIZE;
> +	sb->s_blocksize = PAGE_CACHE_SIZE;
> +	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
> +	sb->s_magic = SYAORAN_MAGIC;
> +	sb->s_op = &syaoran_ops;
> +	sb->s_time_gran = 1;
> +	error = syaoran_initialize(sb, data);
> +	if (error < 0)
> +		return error;
> +	inode = syaoran_get_inode(sb, S_IFDIR | 0755, 0);
> +	if (!inode)
> +		return -ENOMEM;
> +
> +	root = d_alloc_root(inode);
> +	if (!root) {
> +		iput(inode);
> +		return -ENOMEM;
> +	}
> +	sb->s_root = root;
> +	syaoran_make_initial_nodes(sb);
> +	return 0;
> +}
> +
>  int ramfs_get_sb(struct file_system_type *fs_type,
>  	int flags, const char *dev_name, void *data, struct vfsmount *mnt)
>  {
> @@ -197,6 +260,13 @@ static int rootfs_get_sb(struct file_sys
>  			    mnt);
>  }
> 
> +static int syaoran_get_sb(struct file_system_type *fs_type, int flags,
> +			  const char *dev_name, void *data,
> +			  struct vfsmount *mnt)
> +{
> +	return get_sb_nodev(fs_type, flags, data, syaoran_fill_super, mnt);
> +}
> +
>  static struct file_system_type ramfs_fs_type = {
>  	.name		= "ramfs",
>  	.get_sb		= ramfs_get_sb,
> @@ -207,6 +277,11 @@ static struct file_system_type rootfs_fs
>  	.get_sb		= rootfs_get_sb,
>  	.kill_sb	= kill_litter_super,
>  };
> +static struct file_system_type syaoran_fs_type = {
> +	.name           = "syaoran",
> +	.get_sb         = syaoran_get_sb,
> +	.kill_sb        = kill_litter_super,
> +};
> 
>  static int __init init_ramfs_fs(void)
>  {
> @@ -237,3 +312,15 @@ int __init init_rootfs(void)
>  }
> 
>  MODULE_LICENSE("GPL");
> +
> +static int __init init_syaoran_fs(void)
> +{
> +	return register_filesystem(&syaoran_fs_type);
> +}
> +
> +static void __exit exit_syaoran_fs(void)
> +{
> +	unregister_filesystem(&syaoran_fs_type);
> +}
> +module_init(init_syaoran_fs);
> +module_exit(exit_syaoran_fs);
> --- /dev/null
> +++ linux-2.6-mm/fs/ramfs/syaoran.h
> @@ -0,0 +1,1066 @@
> +/*
> + * fs/ramfs/syaoran.h
> + *
> + * Implementation of the Tamper-Proof Device Filesystem.
> + *
> + * Copyright (C) 2005-2007  NTT DATA CORPORATION
> + *
> + * Version: 1.5.3-pre   2007/12/23
> + */
> +
> +#include <linux/namei.h>
> +#include <linux/mm.h>
> +#include <linux/quotaops.h>
> +
> +#define list_for_each_cookie(pos, cookie, head) \
> +	for ((cookie) || ((cookie) = (head)), pos = (cookie)->next; \
> +		prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
> +		(cookie) = pos, pos = pos->next)
> +
> +/* The following constants are used to restrict operations.*/
> +
> +#define MAY_CREATE          1 /* This file is allowed to be mknod()ed.        */
> +#define MAY_DELETE          2 /* This file is allowed to be unlink()ed.       */
> +#define MAY_CHMOD           4 /* This file is allowed to be chmod()ed.        */
> +#define MAY_CHOWN           8 /* This file is allowed to be chown()ed.        */
> +#define DEVICE_USED        16 /* This block or character device file is used. */
> +#define NO_CREATE_AT_MOUNT 32 /* Don't create this file at mount().           */
> +
> +/* some random number */
> +#define SYAORAN_MAGIC    0x2F646576 /* = '/dev' */
> +
> +static struct inode_operations syaoran_file_inode_operations;
> +static struct inode_operations syaoran_symlink_inode_operations;
> +
> +static void syaoran_put_super(struct super_block *sb);
> +static int syaoran_initialize(struct super_block *sb, void *data);
> +static void syaoran_make_initial_nodes(struct super_block *sb);
> +static int syaoran_may_create_node(struct dentry *dentry, int mode, int dev);
> +static int syaoran_may_modify_node(struct dentry *dentry, unsigned int flags);
> +static int syaoran_create_tracelog(struct super_block *sb,
> +				   const char *filename);
> +static struct inode *__ramfs_get_inode(struct super_block *sb, int mode,
> +				       dev_t dev, int mac);
> +
> +static struct inode *syaoran_get_inode(struct super_block *sb, int mode,
> +				       dev_t dev)
> +{
> +	return __ramfs_get_inode(sb, mode, dev, 1);

To integrate this nicer into tmpfs, at least define TMPFS_IS_MAC as 1
and TMPFS_NOT_MAC as 0 and pass those values instead of just 1 and 0.

> +}
> +
> +static struct super_operations syaoran_ops = {
> +	.statfs     = simple_statfs,
> +	.drop_inode = generic_delete_inode,
> +	.put_super  = syaoran_put_super,
> +};
> +
> +/* Wraps blkdev_open() to trace open operation for block devices. */
> +static int (*org_blkdev_open) (struct inode *inode, struct file *filp);
> +static struct file_operations wrapped_def_blk_fops;

Again, I should think you'd actually want to take blkdev_open() from
fs/block_dev.c and chrdev_open() from fs/char_dev.c.  Surely your
method of grabbing it here is not acceptable for upstream code.

That's all I've got for now - though if you'd just break up some of
these functions - especially syaoran_initialize() with it's set of {}
blocks, it would help.

thanks,
-serge


> +
> +static int wrapped_blkdev_open(struct inode *inode, struct file *filp)
> +{
> +	int error = org_blkdev_open(inode, filp);
> +	if (error != -ENXIO)
> +		syaoran_may_modify_node(filp->f_dentry, DEVICE_USED);
> +	return error;
> +}
> +
> +/* Wraps chrdev_open() to trace open operation for character devices. */
> +static int (*org_chrdev_open) (struct inode *inode, struct file *filp);
> +static struct file_operations wrapped_def_chr_fops;
> +
> +static int wrapped_chrdev_open(struct inode *inode, struct file *filp)
> +{
> +	int error = org_chrdev_open(inode, filp);
> +	if (error != -ENXIO)
> +		syaoran_may_modify_node(filp->f_dentry, DEVICE_USED);
> +	return error;
> +}
> +
> +struct dev_entry {
> +	struct list_head list;
> +	/* Binary form of pathname under mount point. Never NULL. */
> +	char *name;
> +	/*
> +	 * Mode and permissions. setuid/setgid/sticky bits are not supported.
> +	 */
> +	mode_t mode;
> +	uid_t uid;
> +	gid_t gid;
> +	dev_t kdev;
> +	/*
> +	 * Binary form of initial contents for the symlink.
> +	 *  NULL if not symlink.
> +	 */
> +	char *symlink_data;
> +	/* File access control flags. */
> +	unsigned int flags;
> +	/* Text form of pathname under mount point. Never NULL. */
> +	const char *printable_name;
> +	/*
> +	 * Text form of initial contents for the symlink.
> +	 * NULL if not symlink.
> +	 */
> +	const char *printable_symlink_data;
> +};
> +
> +struct syaoran_sb_info {
> +	struct list_head list;
> +	bool initialize_done;  /* False if initialization is in progress. */
> +	bool is_permissive_mode; /* True if permissive mode. */
> +};
> +
> +static void syaoran_put_super(struct super_block *sb)
> +{
> +	struct syaoran_sb_info *info;
> +	struct dev_entry *entry;
> +	struct dev_entry *tmp;
> +	if (!sb)
> +		return;
> +	info = (struct syaoran_sb_info *) sb->s_fs_info;
> +	if (!info)
> +		return;
> +	list_for_each_entry_safe(entry, tmp, &info->list, list) {
> +		kfree(entry->name);
> +		kfree(entry->symlink_data);
> +		kfree(entry->printable_name);
> +		kfree(entry->printable_symlink_data);
> +		list_del(&entry->list);
> +		/* printk("Entry removed.\n"); */
> +		kfree(entry);
> +	}
> +	kfree(info);
> +	sb->s_fs_info = NULL;
> +	printk(KERN_DEBUG "%s: Unused memory freed.\n", __FUNCTION__);
> +}
> +
> +/* Get absolute pathname from mount point. */
> +static int get_local_absolute_path(struct dentry *dentry, char *buffer,
> +				   int buflen)
> +{
> +	char *start = buffer;
> +	char *end = buffer + buflen;
> +	int namelen;
> +
> +	if (buflen < 256)
> +		goto out;
> +
> +	*--end = '\0';
> +	buflen--;
> +	for (;;) {
> +		struct dentry *parent;
> +		if (IS_ROOT(dentry))
> +			break;
> +		parent = dentry->d_parent;
> +		namelen = dentry->d_name.len;
> +		buflen -= namelen + 1;
> +		if (buflen < 0)
> +			goto out;
> +		end -= namelen;
> +		memcpy(end, dentry->d_name.name, namelen);
> +		*--end = '/';
> +		dentry = parent;
> +	}
> +	if (*end == '/') {
> +		buflen++;
> +		end++;
> +	}
> +	namelen = dentry->d_name.len;
> +	buflen -= namelen;
> +	if (buflen < 0)
> +		goto out;
> +	end -= namelen;
> +	memcpy(end, dentry->d_name.name, namelen);
> +	memmove(start, end, strlen(end) + 1);
> +	return 0;
> +out:
> +	return -ENOMEM;
> +}
> +
> +/* Get absolute pathname of the given dentry from mount point. */
> +static int local_realpath_from_dentry(struct dentry *dentry, char *newname,
> +				      int newname_len)
> +{
> +	int error;
> +	struct dentry *d_dentry;
> +	if (!dentry || !newname || newname_len <= 0)
> +		return -EINVAL;
> +	d_dentry = dget(dentry);
> +	/***** CRITICAL SECTION START *****/
> +	spin_lock(&dcache_lock);
> +	error = get_local_absolute_path(d_dentry, newname, newname_len);
> +	spin_unlock(&dcache_lock);
> +	/***** CRITICAL SECTION END *****/
> +	dput(d_dentry);
> +	return error;
> +}
> +
> +static int syaoran_check_flags(struct syaoran_sb_info *info,
> +			       struct dentry *dentry, int mode, int dev,
> +			       unsigned int flags)
> +{
> +	int error = -EPERM;
> +	struct dev_entry *entry;
> +	/*
> +	 * Since local_realpath_from_dentry() holds dcache_lock,
> +	 * allocating buffer using kmalloc() won't help improving concurrency.
> +	 * Therefore, I use static buffer here.
> +	 */
> +	static char filename[PAGE_SIZE];
> +	static DEFINE_SPINLOCK(lock);
> +	spin_lock(&lock);
> +	memset(filename, 0, sizeof(filename));
> +	if (local_realpath_from_dentry(dentry, filename, sizeof(filename) - 1))
> +		goto out;
> +	list_for_each_entry(entry, &info->list, list) {
> +		if ((mode & S_IFMT) != (entry->mode & S_IFMT))
> +			continue;
> +		if ((S_ISBLK(mode) || S_ISCHR(mode)) && dev != entry->kdev)
> +			continue;
> +		if (strcmp(entry->name, filename + 1))
> +			continue;
> +		if (info->is_permissive_mode) {
> +			entry->flags |= flags;
> +			error = 0;
> +		} else {
> +			if ((entry->flags & flags) == flags)
> +				error = 0;
> +		}
> +		break;
> +	}
> +out:
> +	if (error && strlen(filename) < (sizeof(filename) / 4) - 16) {
> +		const char *name;
> +		const uid_t uid = current->fsuid;
> +		const gid_t gid = current->fsgid;
> +		const mode_t perm = mode & 0777;
> +		flags &= ~DEVICE_USED;
> +		{
> +			char *end = filename + sizeof(filename) - 1;
> +			const char *cp = strchr(filename, '\0') - 1;
> +			while (cp > filename) {
> +				const unsigned char c = *cp--;
> +				if (c == '\\') {
> +					*--end = '\\';
> +					*--end = '\\';
> +				} else if (c > ' ' && c < 127) {
> +					*--end = c;
> +				} else {
> +					*--end = (c & 7) + '0';
> +					*--end = ((c >> 3) & 7) + '0';
> +					*--end = (c >> 6) + '0';
> +					*--end = '\\';
> +				}
> +			}
> +			name = end;
> +		}
> +		switch (mode & S_IFMT) {
> +		case S_IFCHR:
> +			printk(KERN_DEBUG
> +			       "SYAORAN-ERROR: %s %3o %3u %3u %2u %c %3u %3u\n",
> +			       name, perm, uid, gid, flags, 'c',
> +			       MAJOR(dev), MINOR(dev));
> +			break;
> +		case S_IFBLK:
> +			printk(KERN_DEBUG
> +			       "SYAORAN-ERROR: %s %3o %3u %3u %2u %c %3u %3u\n",
> +			       name, perm, uid, gid, flags, 'b',
> +			       MAJOR(dev), MINOR(dev));
> +			break;
> +		case S_IFIFO:
> +			printk(KERN_DEBUG
> +			       "SYAORAN-ERROR: %s %3o %3u %3u %2u %c\n",
> +			       name, perm, uid, gid, flags, 'p');
> +			break;
> +		case S_IFSOCK:
> +			printk(KERN_DEBUG
> +			       "SYAORAN-ERROR: %s %3o %3u %3u %2u %c\n",
> +			       name, perm, uid, gid, flags, 's');
> +			break;
> +		case S_IFDIR:
> +			printk(KERN_DEBUG
> +			       "SYAORAN-ERROR: %s %3o %3u %3u %2u %c\n",
> +			       name, perm, uid, gid, flags, 'd');
> +			break;
> +		case S_IFLNK:
> +			printk(KERN_DEBUG
> +			       "SYAORAN-ERROR: %s %3o %3u %3u %2u %c %s\n",
> +			       name, perm, uid, gid, flags, 'l', "unknown");
> +			break;
> +		case S_IFREG:
> +			printk(KERN_DEBUG
> +			       "SYAORAN-ERROR: %s %3o %3u %3u %2u %c\n",
> +			       name, perm, uid, gid, flags, 'f');
> +			break;
> +		}
> +	}
> +	spin_unlock(&lock);
> +	return error;
> +}
> +
> +/* Check whether the given dentry is allowed to mknod. */
> +static int syaoran_may_create_node(struct dentry *dentry, int mode, int dev)
> +{
> +	struct syaoran_sb_info *info =
> +		(struct syaoran_sb_info *) dentry->d_sb->s_fs_info;
> +	if (!info) {
> +		printk(KERN_DEBUG "%s: dentry->d_sb->s_fs_info == NULL\n",
> +		       __FUNCTION__);
> +		return -EPERM;
> +	}
> +	if (!info->initialize_done)
> +		return 0;
> +	return syaoran_check_flags(info, dentry, mode, dev, MAY_CREATE);
> +}
> +
> +/* Check whether the given dentry is allowed to chmod/chown/unlink. */
> +static int syaoran_may_modify_node(struct dentry *dentry, unsigned int flags)
> +{
> +	struct syaoran_sb_info *info =
> +		(struct syaoran_sb_info *) dentry->d_sb->s_fs_info;
> +	if (!info) {
> +		printk(KERN_DEBUG "%s: dentry->d_sb->s_fs_info == NULL\n",
> +		       __FUNCTION__);
> +		return -EPERM;
> +	}
> +	if (flags == DEVICE_USED && !info->is_permissive_mode)
> +		return 0;
> +	if (!dentry->d_inode)
> +		return -ENOENT;
> +	return syaoran_check_flags(info, dentry, dentry->d_inode->i_mode,
> +				   dentry->d_inode->i_rdev, flags);
> +}
> +
> +static int ramfs_link(struct dentry *old_dentry, struct inode *dir,
> +			struct dentry *dentry)
> +{
> +	struct inode *inode;
> +	if (dir->i_sb->s_op != &syaoran_ops)
> +		goto ok;
> +	/*** SYAORAN start. ***/
> +	inode = old_dentry->d_inode;
> +	if (!inode ||
> +	    syaoran_may_create_node(dentry, inode->i_mode, inode->i_rdev))
> +		return -EPERM;
> +	/*** SYAORAN end. ***/
> +ok:
> +	return simple_link(old_dentry, dir, dentry);
> +}
> +
> +static int ramfs_unlink(struct inode *dir, struct dentry *dentry)
> +{
> +	if (dir->i_sb->s_op != &syaoran_ops)
> +		goto ok;
> +	/*** SYAORAN start. ***/
> +	if (syaoran_may_modify_node(dentry, MAY_DELETE))
> +		return -EPERM;
> +	/*** SYAORAN end. ***/
> +ok:
> +	return simple_unlink(dir, dentry);
> +}
> +
> +static int ramfs_rename(struct inode *old_dir, struct dentry *old_dentry,
> +			  struct inode *new_dir, struct dentry *new_dentry)
> +{
> +	struct inode *inode;
> +	if (old_dir->i_sb->s_op != &syaoran_ops)
> +		goto ok;
> +	/*** SYAORAN start. ***/
> +	inode = old_dentry->d_inode;
> +	if (!inode || syaoran_may_modify_node(old_dentry, MAY_DELETE) ||
> +	    syaoran_may_create_node(new_dentry, inode->i_mode, inode->i_rdev))
> +		return -EPERM;
> +	/*** SYAORAN end. ***/
> +ok:
> +	return simple_rename(old_dir, old_dentry, new_dir, new_dentry);
> +}
> +
> +static int ramfs_rmdir(struct inode *dir, struct dentry *dentry)
> +{
> +	if (dir->i_sb->s_op != &syaoran_ops)
> +		goto ok;
> +	/*** SYAORAN start. ***/
> +	if (syaoran_may_modify_node(dentry, MAY_DELETE))
> +		return -EPERM;
> +	/*** SYAORAN end. ***/
> +ok:
> +	return simple_rmdir(dir, dentry);
> +}
> +
> +/*
> + * Original tmpfs doesn't set ramfs_dir_inode_operations.setattr field.
> + * Now I'm setting the field to share tmpfs/rootfs/SYAORAN code.
> + * Side effect is that the checking order of notify_change() has changed from
> + *  inode_change_ok() -> security_inode_setattr() ->
> + *                                     DQUOT_TRANSFER() -> inode_setattr()
> + * to
> + *  security_inode_setattr() -> inode_change_ok() ->
> + *                                     DQUOT_TRANSFER() -> inode_setattr()
> + *
> + * Is this change problematic? If problematic, I'll stop sharing the field.
> + */
> +static int ramfs_setattr(struct dentry *dentry, struct iattr *attr)
> +{
> +	unsigned int ia_valid;
> +	unsigned int flags = 0;
> +	struct inode *inode = dentry->d_inode;
> +	int error = inode_change_ok(inode, attr);
> +	if (inode->i_sb->s_op != &syaoran_ops)
> +		goto ok;
> +	/*** SYAORAN start. ***/
> +	ia_valid = attr->ia_valid;
> +	if (ia_valid & (ATTR_UID | ATTR_GID))
> +		flags |= MAY_CHOWN;
> +	if (ia_valid & ATTR_MODE)
> +		flags |= MAY_CHMOD;
> +	if (syaoran_may_modify_node(dentry, flags))
> +		return -EPERM;
> +	/*** SYAORAN end. ***/
> +ok:
> +	if (!error) {
> +		if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) ||
> +		    (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid))
> +			error = DQUOT_TRANSFER(inode, attr) ? -EDQUOT : 0;
> +		if (!error)
> +			error = inode_setattr(inode, attr);
> +	}
> +	return error;
> +}
> +
> +static struct inode_operations syaoran_file_inode_operations = {
> +	.getattr        = simple_getattr,
> +	.setattr        = ramfs_setattr,
> +};
> +
> +static struct inode_operations syaoran_symlink_inode_operations = {
> +	.readlink       = generic_readlink,
> +	.follow_link    = page_follow_link_light,
> +	.put_link       = page_put_link,
> +	.setattr        = ramfs_setattr,
> +};
> +
> +/*
> + * The following codes are used for processing the policy file and
> + * creating initial nodes.
> + */
> +
> +/* lookup_create() without nameidata. Called only while initialization. */
> +static struct dentry *lookup_create2(const char *name, struct dentry *base,
> +				     const bool is_dir)
> +{
> +	struct dentry *dentry;
> +	const int len = name ? strlen(name) : 0;
> +	mutex_lock(&base->d_inode->i_mutex);
> +	dentry = lookup_one_len(name, base, len);
> +	if (IS_ERR(dentry))
> +		goto fail;
> +	if (!is_dir && name[len] && !dentry->d_inode)
> +		goto enoent;
> +	return dentry;
> +enoent:
> +	dput(dentry);
> +	dentry = ERR_PTR(-ENOENT);
> +fail:
> +	return dentry;
> +}
> +
> +/* mkdir(). Called only while initialization. */
> +static int fs_mkdir(const char *pathname, struct dentry *base, int mode,
> +		    uid_t user, gid_t group)
> +{
> +	struct dentry *dentry = lookup_create2(pathname, base, 1);
> +	int error = PTR_ERR(dentry);
> +	if (!IS_ERR(dentry)) {
> +		error = vfs_mkdir(base->d_inode, dentry, mode);
> +		if (!error) {
> +			lock_kernel();
> +			dentry->d_inode->i_uid = user;
> +			dentry->d_inode->i_gid = group;
> +			unlock_kernel();
> +		}
> +		dput(dentry);
> +	}
> +	mutex_unlock(&base->d_inode->i_mutex);
> +	return error;
> +}
> +
> +/* mknod(). Called only while initialization. */
> +static int fs_mknod(const char *filename, struct dentry *base, int mode,
> +		    dev_t dev, uid_t user, gid_t group)
> +{
> +	struct dentry *dentry;
> +	int error;
> +	switch (mode & S_IFMT) {
> +	case S_IFCHR:
> +	case S_IFBLK:
> +	case S_IFIFO:
> +	case S_IFSOCK:
> +	case S_IFREG:
> +		break;
> +	default:
> +		return -EPERM;
> +	}
> +	dentry = lookup_create2(filename, base, 0);
> +	error = PTR_ERR(dentry);
> +	if (!IS_ERR(dentry)) {
> +		error = vfs_mknod(base->d_inode, dentry, mode, dev);
> +		if (!error) {
> +			lock_kernel();
> +			dentry->d_inode->i_uid = user;
> +			dentry->d_inode->i_gid = group;
> +			unlock_kernel();
> +		}
> +		dput(dentry);
> +	}
> +	mutex_unlock(&base->d_inode->i_mutex);
> +	return error;
> +}
> +
> +/* symlink(). Called only while initialization. */
> +static int fs_symlink(const char *pathname, struct dentry *base,
> +		      char *oldname, int mode, uid_t user, gid_t group)
> +{
> +	struct dentry *dentry = lookup_create2(pathname, base, 0);
> +	int error = PTR_ERR(dentry);
> +	if (!IS_ERR(dentry)) {
> +		error = vfs_symlink(base->d_inode, dentry, oldname, S_IALLUGO);
> +		if (!error) {
> +			lock_kernel();
> +			dentry->d_inode->i_mode = mode;
> +			dentry->d_inode->i_uid = user;
> +			dentry->d_inode->i_gid = group;
> +			unlock_kernel();
> +		}
> +		dput(dentry);
> +	}
> +	mutex_unlock(&base->d_inode->i_mutex);
> +	return error;
> +}
> +
> +/*
> + * Format string.
> + * Leading and trailing whitespaces are removed.
> + * Multiple whitespaces are packed into single space.
> + */
> +static void syaoran_normalize_line(unsigned char *buffer)
> +{
> +	unsigned char *sp = buffer;
> +	unsigned char *dp = buffer;
> +	bool first = 1;
> +	while (*sp && (*sp <= ' ' || *sp >= 127))
> +		sp++;
> +	while (*sp) {
> +		if (!first)
> +			*dp++ = ' ';
> +		first = 0;
> +		while (*sp > ' ' && *sp < 127)
> +			*dp++ = *sp++;
> +		while (*sp && (*sp <= ' ' || *sp >= 127))
> +			sp++;
> +	}
> +	*dp = '\0';
> +}
> +
> +/* Convert text form of filename into binary form. */
> +static void syaoran_unescape(char *filename)
> +{
> +	char *cp = filename;
> +	char c, d, e;
> +	if (!cp)
> +		return;
> +	while ((c = *filename++) != '\0') {
> +		if (c != '\\') {
> +			*cp++ = c;
> +			continue;
> +		}
> +		if ((c = *filename++) == '\\') {
> +			*cp++ = c;
> +			continue;
> +		}
> +		if (c < '0' || c > '3')
> +			break;
> +		d = *filename++;
> +		if (d < '0' || d > '7')
> +			break;
> +		e = *filename++;
> +		if (e < '0' || e > '7')
> +			break;
> +		*(unsigned char *) cp++ = (unsigned char)
> +			(((unsigned char) (c - '0') << 6) +
> +			 ((unsigned char) (d - '0') << 3) +
> +			 (unsigned char) (e - '0'));
> +	}
> +	*cp = '\0';
> +}
> +
> +static inline char *strdup(const char *data)
> +{
> +	return kstrdup(data, GFP_KERNEL);
> +}
> +
> +static int register_node_info(char *buffer, struct super_block *sb)
> +{
> +	enum {
> +		ARG_FILENAME     = 0,
> +		ARG_PERMISSION   = 1,
> +		ARG_UID          = 2,
> +		ARG_GID          = 3,
> +		ARG_FLAGS        = 4,
> +		ARG_DEV_TYPE     = 5,
> +		ARG_SYMLINK_DATA = 6,
> +		ARG_DEV_MAJOR    = 6,
> +		ARG_DEV_MINOR    = 7,
> +		MAX_ARG          = 8
> +	};
> +	char *args[MAX_ARG];
> +	int i;
> +	int error = -EINVAL;
> +	unsigned int perm, uid, gid, flags, major = 0, minor = 0;
> +	struct syaoran_sb_info *info = (struct syaoran_sb_info *) sb->s_fs_info;
> +	struct dev_entry *entry;
> +	memset(args, 0, sizeof(args));
> +	args[0] = buffer;
> +	for (i = 1; i < MAX_ARG; i++) {
> +		args[i] = strchr(args[i - 1] + 1, ' ');
> +		if (!args[i])
> +			break;
> +		*args[i]++ = '\0';
> +	}
> +	/*
> +	  printk("<%s> <%s> <%s> <%s> <%s> <%s> <%s> <%s>\n",
> +	  args[0], args[1], args[2], args[3], args[4], args[5],
> +	  args[6], args[7]);
> +	*/
> +	if (!args[ARG_FILENAME] || !args[ARG_PERMISSION] || !args[ARG_UID] ||
> +	    !args[ARG_GID] || !args[ARG_DEV_TYPE] || !args[ARG_FLAGS])
> +		goto out;
> +	if (sscanf(args[ARG_PERMISSION], "%o", &perm) != 1 || !(perm <= 0777)
> +	    || sscanf(args[ARG_UID], "%u", &uid) != 1
> +	    || sscanf(args[ARG_GID], "%u", &gid) != 1
> +	    || sscanf(args[ARG_FLAGS], "%u", &flags) != 1
> +	    || *(args[ARG_DEV_TYPE] + 1))
> +		goto out;
> +	switch (*args[ARG_DEV_TYPE]) {
> +	case 'c':
> +		perm |= S_IFCHR;
> +		if (!args[ARG_DEV_MAJOR]
> +		    || sscanf(args[ARG_DEV_MAJOR], "%u", &major) != 1
> +		    || !args[ARG_DEV_MINOR]
> +		    || sscanf(args[ARG_DEV_MINOR], "%u", &minor) != 1)
> +			goto out;
> +		break;
> +	case 'b':
> +		perm |= S_IFBLK;
> +		if (!args[ARG_DEV_MAJOR]
> +		    || sscanf(args[ARG_DEV_MAJOR], "%u", &major) != 1
> +		    || !args[ARG_DEV_MINOR]
> +		    || sscanf(args[ARG_DEV_MINOR], "%u", &minor) != 1)
> +			goto out;
> +		break;
> +	case 'l':
> +		perm |= S_IFLNK;
> +		if (!args[ARG_SYMLINK_DATA])
> +			goto out;
> +		break;
> +	case 'd':
> +		perm |= S_IFDIR;
> +		break;
> +	case 's':
> +		perm |= S_IFSOCK;
> +		break;
> +	case 'p':
> +		perm |= S_IFIFO;
> +		break;
> +	case 'f':
> +		perm |= S_IFREG;
> +		break;
> +	default:
> +		goto out;
> +	}
> +	error = -ENOMEM;
> +	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
> +	if (!entry)
> +		goto out;
> +	if (S_ISLNK(perm)) {
> +		entry->printable_symlink_data = strdup(args[ARG_SYMLINK_DATA]);
> +		if (!entry->printable_symlink_data)
> +			goto out_freemem;
> +	}
> +	entry->printable_name = strdup(args[ARG_FILENAME]);
> +	if (!entry->printable_name)
> +		goto out_freemem;
> +	if (S_ISLNK(perm)) {
> +		entry->symlink_data = strdup(entry->printable_symlink_data);
> +		if (!entry->symlink_data)
> +			goto out_freemem;
> +		syaoran_unescape(entry->symlink_data);
> +	}
> +	entry->name = strdup(entry->printable_name);
> +	if (!entry->name)
> +		goto out_freemem;
> +	syaoran_unescape(entry->name);
> +	/*
> +	 * Drop trailing '/', for GetLocalAbsolutePath() doesn't append
> +	 *  trailing '/'.
> +	 */
> +	i = strlen(entry->name);
> +	if (i && entry->name[i - 1] == '/')
> +		entry->name[i - 1] = '\0';
> +	entry->mode = perm;
> +	entry->uid = uid;
> +	entry->gid = gid;
> +	entry->kdev = S_ISCHR(perm) || S_ISBLK(perm) ? MKDEV(major, minor) : 0;
> +	entry->flags = flags;
> +	list_add_tail(&entry->list, &info->list);
> +	/* printk("Entry added.\n"); */
> +	error = 0;
> +out:
> +	return error;
> +out_freemem:
> +	kfree(entry->printable_symlink_data);
> +	kfree(entry->printable_name);
> +	kfree(entry->symlink_data);
> +	kfree(entry);
> +	goto out;
> +}
> +
> +static int read_config_file(struct file *file, struct super_block *sb)
> +{
> +	char *buffer;
> +	int error = -ENOMEM;
> +	if (!file)
> +		return -EINVAL;
> +	buffer = kzalloc(PAGE_SIZE, GFP_KERNEL);
> +	if (buffer) {
> +		int len;
> +		char *cp;
> +		unsigned long offset = 0;
> +		while ((len = kernel_read(file, offset, buffer, PAGE_SIZE)) > 0
> +		       && (cp = memchr(buffer, '\n', len)) != NULL) {
> +			*cp = '\0';
> +			offset += cp - buffer + 1;
> +			syaoran_normalize_line(buffer);
> +			if (register_node_info(buffer, sb) == -ENOMEM)
> +				goto out;
> +		}
> +		error = 0;
> +	}
> +out:
> +	kfree(buffer);
> +	return error;
> +}
> +
> +static void make_node(struct dev_entry *entry, struct dentry *root)
> +{
> +	struct dentry *base = dget(root);
> +	char *filename = entry->name;
> +	char *name = filename;
> +	unsigned int c;
> +	const mode_t perm = entry->mode;
> +	const uid_t uid = entry->uid;
> +	const gid_t gid = entry->gid;
> +	goto start;
> +	while ((c = *(unsigned char *) filename) != '\0') {
> +		if (c == '/') {
> +			struct dentry *new_base;
> +			const int len = filename - name;
> +			*filename = '\0';
> +			mutex_lock(&base->d_inode->i_mutex);
> +			new_base = lookup_one_len(name, base, len);
> +			mutex_unlock(&base->d_inode->i_mutex);
> +			dput(base);
> +			*filename = '/';
> +			filename++;
> +			if (IS_ERR(new_base))
> +				return;
> +			if (!new_base->d_inode ||
> +			    !S_ISDIR(new_base->d_inode->i_mode)) {
> +				dput(new_base);
> +				return;
> +			}
> +			base = new_base;
> +start:
> +			name = filename;
> +		} else {
> +			filename++;
> +		}
> +	}
> +	filename = (char *) name;
> +	if (S_ISLNK(perm)) {
> +		fs_symlink(filename, base, entry->symlink_data, perm, uid, gid);
> +	} else if (S_ISDIR(perm)) {
> +		fs_mkdir(filename, base, perm ^ S_IFDIR, uid, gid);
> +	} else if (S_ISSOCK(perm) || S_ISFIFO(perm) || S_ISREG(perm)) {
> +		fs_mknod(filename, base, perm, 0, uid, gid);
> +	} else if (S_ISCHR(perm) || S_ISBLK(perm)) {
> +		fs_mknod(filename, base, perm, entry->kdev, uid, gid);
> +	}
> +	dput(base);
> +}
> +
> +/* Create files according to the policy file. */
> +static void syaoran_make_initial_nodes(struct super_block *sb)
> +{
> +	struct syaoran_sb_info *info;
> +	struct dev_entry *entry;
> +	if (!sb)
> +		return;
> +	info = (struct syaoran_sb_info *) sb->s_fs_info;
> +	if (!info)
> +		return;
> +	if (info->is_permissive_mode) {
> +		syaoran_create_tracelog(sb, ".syaoran");
> +		syaoran_create_tracelog(sb, ".syaoran_all");
> +	}
> +	list_for_each_entry(entry, &info->list, list) {
> +		if ((entry->flags & NO_CREATE_AT_MOUNT) == 0)
> +			make_node(entry, sb->s_root);
> +	}
> +	info->initialize_done = 1;
> +}
> +
> +/* Read policy file. */
> +static int syaoran_initialize(struct super_block *sb, void *data)
> +{
> +	int error = -EINVAL;
> +	static bool first = 1;
> +	if (first) {
> +		first = 0;
> +		printk(KERN_INFO "SYAORAN: 1.5.3-pre   2007/12/23\n");
> +	}
> +	{
> +		struct inode *inode = new_inode(sb);
> +		if (!inode)
> +			return -EINVAL;
> +		/* Create /dev/ram0 to get the value of blkdev_open(). */
> +		init_special_inode(inode, S_IFBLK | 0666, MKDEV(1, 0));
> +		wrapped_def_blk_fops = *inode->i_fop;
> +		iput(inode);
> +		org_blkdev_open = wrapped_def_blk_fops.open;
> +		wrapped_def_blk_fops.open = wrapped_blkdev_open;
> +	}
> +	{
> +		struct inode *inode = new_inode(sb);
> +		if (!inode)
> +			return -EINVAL;
> +		/* Create /dev/null to get the value of chrdev_open(). */
> +		init_special_inode(inode, S_IFCHR | 0666, MKDEV(1, 3));
> +		wrapped_def_chr_fops = *inode->i_fop;
> +		iput(inode);
> +		org_chrdev_open = wrapped_def_chr_fops.open;
> +		wrapped_def_chr_fops.open = wrapped_chrdev_open;
> +	}
> +	if (data) {
> +		struct file *f;
> +		char *filename = (char *) data;
> +		bool is_permissive_mode = 0;
> +		if (strncmp(filename, "accept=", 7) == 0) {
> +			filename += 7;
> +			is_permissive_mode = 1;
> +		} else if (strncmp(filename, "enforce=", 8) == 0) {
> +			filename += 8;
> +			is_permissive_mode = 0;
> +		} else {
> +			printk(KERN_INFO
> +			       "SYAORAN: Missing 'accept=' or 'enforce='.\n");
> +			return -EINVAL;
> +		}
> +		f = open_pathname(AT_FDCWD, filename, O_RDONLY, 0600);
> +		if (!IS_ERR(f)) {
> +			struct syaoran_sb_info *p;
> +			if (!S_ISREG(f->f_dentry->d_inode->i_mode))
> +				goto out;
> +			p = kzalloc(sizeof(*p), GFP_KERNEL);
> +			if (!p)
> +				goto out;
> +			p->is_permissive_mode = is_permissive_mode;
> +			sb->s_fs_info = p;
> +			INIT_LIST_HEAD(&((struct syaoran_sb_info *)
> +					 sb->s_fs_info)->list);
> +			printk(KERN_INFO "SYAORAN: Reading '%s'\n", filename);
> +			error = read_config_file(f, sb);
> +out:
> +			if (error)
> +				printk(KERN_INFO "SYAORAN: Can't read '%s'\n",
> +				       filename);
> +			filp_close(f, NULL);
> +		} else {
> +			printk(KERN_INFO "SYAORAN: Can't open '%s'\n",
> +			       filename);
> +		}
> +	} else {
> +		printk(KERN_INFO "SYAORAN: Missing config-file path.\n");
> +	}
> +	return error;
> +}
> +
> +/*
> + * The following structure and codes are used for transferring data
> + * to interfaces files.
> + */
> +
> +struct syaoran_read_struct {
> +	char *buf;               /* Buffer for reading.                */
> +	int avail;               /* Bytes available for reading.       */
> +	struct super_block *sb;  /* The super_block of this partition. */
> +	struct dev_entry *entry; /* The entry currently reading from.  */
> +	_Bool read_all;          /* Dump all entries?                  */
> +	struct list_head *pos;   /* Current position.                  */
> +};
> +
> +static void syaoran_read_table(struct syaoran_read_struct *head, char *buf,
> +			       int count)
> +{
> +	struct super_block *sb = head->sb;
> +	struct syaoran_sb_info *info =
> +		(struct syaoran_sb_info *) sb->s_fs_info;
> +	struct list_head *pos;
> +	const _Bool read_all = head->read_all;
> +	if (!info)
> +		return;
> +	if (!head->pos)
> +		return;
> +	list_for_each_cookie(pos, head->pos, &info->list) {
> +		struct dev_entry *entry =
> +			list_entry(pos, struct dev_entry, list);
> +		const unsigned int flags =
> +			read_all ? entry->flags : entry->flags & ~DEVICE_USED;
> +		const char *name = entry->printable_name;
> +		const uid_t uid = entry->uid;
> +		const gid_t gid = entry->gid;
> +		const mode_t perm = entry->mode & 0777;
> +		int len = 0;
> +		switch (entry->mode & S_IFMT) {
> +		case S_IFCHR:
> +			if (!head->read_all && !(entry->flags & DEVICE_USED))
> +				break;
> +			len = snprintf(buf, count,
> +				       "%-20s %3o %3u %3u %2u %c %3u %3u\n",
> +				       name, perm, uid, gid, flags, 'c',
> +				       MAJOR(entry->kdev), MINOR(entry->kdev));
> +			break;
> +		case S_IFBLK:
> +			if (!head->read_all && !(entry->flags & DEVICE_USED))
> +				break;
> +			len = snprintf(buf, count,
> +				       "%-20s %3o %3u %3u %2u %c %3u %3u\n",
> +				       name, perm, uid, gid, flags, 'b',
> +				       MAJOR(entry->kdev), MINOR(entry->kdev));
> +			break;
> +		case S_IFIFO:
> +			len = snprintf(buf, count,
> +				       "%-20s %3o %3u %3u %2u %c\n",
> +				       name, perm, uid, gid, flags, 'p');
> +			break;
> +		case S_IFSOCK:
> +			len = snprintf(buf, count,
> +				       "%-20s %3o %3u %3u %2u %c\n",
> +				       name, perm, uid, gid, flags, 's');
> +			break;
> +		case S_IFDIR:
> +			len = snprintf(buf, count,
> +				       "%-20s %3o %3u %3u %2u %c\n",
> +				       name, perm, uid, gid, flags, 'd');
> +			break;
> +		case S_IFLNK:
> +			len = snprintf(buf, count,
> +				       "%-20s %3o %3u %3u %2u %c %s\n",
> +				       name, perm, uid, gid, flags, 'l',
> +				       entry->printable_symlink_data);
> +			break;
> +		case S_IFREG:
> +			len = snprintf(buf, count,
> +				       "%-20s %3o %3u %3u %2u %c\n",
> +				       name, perm, uid, gid, flags, 'f');
> +			break;
> +		}
> +		if (len < 0 || count <= len)
> +			break;
> +		count -= len;
> +		buf += len;
> +		head->avail += len;
> +	}
> +}
> +
> +static int syaoran_trace_open(struct inode *inode, struct file *file)
> +{
> +	struct syaoran_read_struct *head =
> +		kzalloc(sizeof(*head), GFP_KERNEL);
> +	if (!head)
> +		return -ENOMEM;
> +	head->sb = inode->i_sb;
> +	head->read_all =
> +		(strcmp(file->f_dentry->d_name.name, ".syaoran_all") == 0);
> +	head->pos = &((struct syaoran_sb_info *) head->sb->s_fs_info)->list;
> +	head->buf = kzalloc(PAGE_SIZE * 2, GFP_KERNEL);
> +	if (!head->buf) {
> +		kfree(head);
> +		return -ENOMEM;
> +	}
> +	file->private_data = head;
> +	return 0;
> +}
> +
> +static int syaoran_trace_release(struct inode *inode, struct file *file)
> +{
> +	struct syaoran_read_struct *head = file->private_data;
> +	kfree(head->buf);
> +	kfree(head);
> +	file->private_data = NULL;
> +	return 0;
> +}
> +
> +static ssize_t syaoran_trace_read(struct file *file, char __user *buf,
> +				  size_t count, loff_t *ppos)
> +{
> +	struct syaoran_read_struct *head =
> +		(struct syaoran_read_struct *) file->private_data;
> +	int len = head->avail;
> +	char *cp = head->buf;
> +	if (!access_ok(VERIFY_WRITE, buf, count))
> +		return -EFAULT;
> +	syaoran_read_table(head, cp + len, PAGE_SIZE * 2 - len);
> +	len = head->avail;
> +	if (len > count)
> +		len = count;
> +	if (len > 0) {
> +		if (copy_to_user(buf, cp, len))
> +			return -EFAULT;
> +		head->avail -= len;
> +		memmove(cp, cp + len, head->avail);
> +	}
> +	return len;
> +}
> +
> +static struct file_operations syaoran_trace_operations = {
> +	.open    = syaoran_trace_open,
> +	.release = syaoran_trace_release,
> +	.read    = syaoran_trace_read,
> +};
> +
> +/* Create interface files for reading status. */
> +static int syaoran_create_tracelog(struct super_block *sb, const char *filename)
> +{
> +	struct inode *inode;
> +	struct dentry *base = dget(sb->s_root);
> +	struct dentry *dentry = lookup_create2(filename, base, 0);
> +	int error = PTR_ERR(dentry);
> +	if (IS_ERR(dentry))
> +		goto out;
> +	inode = syaoran_get_inode(sb, S_IFREG | 0400, 0);
> +	if (!inode)
> +		error = -ENOSPC;
> +	else {
> +		/* Override file operation. */
> +		inode->i_fop = &syaoran_trace_operations;
> +		d_instantiate(dentry, inode);
> +		dget(dentry); /* Extra count - pin the dentry in core */
> +		error = 0;
> +	}
> +	dput(dentry);
> +out:
> +	mutex_unlock(&base->d_inode->i_mutex);
> +	dput(base);
> +	return error;
> +}
> -
> To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
> the body of a message to [email protected]
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux