/*
 *  linux/fs/read_write.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 *  Minor pieces Copyright (C) 2002 Red Hat Inc, All Rights Reserved
 *
 *  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/slab.h> 
#include <linux/stat.h>
#include <linux/fcntl.h>
#include <linux/file.h>
#include <linux/uio.h>
#include <linux/smp_lock.h>
#include <linux/dnotify.h>

#include <asm/uaccess.h>

/* RSBAC */
#ifdef CONFIG_RSBAC_RW
#include <rsbac/adf.h>
#endif

struct file_operations generic_ro_fops = {
	llseek:		generic_file_llseek,
	read:		generic_file_read,
	mmap:		generic_file_mmap,
};

ssize_t generic_read_dir(struct file *filp, char *buf, size_t siz, loff_t *ppos)
{
	return -EISDIR;
}

loff_t generic_file_llseek(struct file *file, loff_t offset, int origin)
{
	long long retval;

	switch (origin) {
		case 2:
			offset += i_size_read(file->f_dentry->d_inode);
			break;
		case 1:
			offset += file->f_pos;
	}
	retval = -EINVAL;
	if (offset>=0 && offset<=file->f_dentry->d_inode->i_sb->s_maxbytes) {
		if (offset != file->f_pos) {
			file->f_pos = offset;
			file->f_reada = 0;
			file->f_version = ++event;
		}
		retval = offset;
	}
	return retval;
}

loff_t no_llseek(struct file *file, loff_t offset, int origin)
{
	return -ESPIPE;
}

loff_t default_llseek(struct file *file, loff_t offset, int origin)
{
	long long retval;

	switch (origin) {
		case 2:
			offset += i_size_read(file->f_dentry->d_inode);
			break;
		case 1:
			offset += file->f_pos;
	}
	retval = -EINVAL;
	if (offset >= 0) {
		if (offset != file->f_pos) {
			file->f_pos = offset;
			file->f_reada = 0;
			file->f_version = ++event;
		}
		retval = offset;
	}
	return retval;
}

static inline loff_t llseek(struct file *file, loff_t offset, int origin)
{
	loff_t (*fn)(struct file *, loff_t, int);
	loff_t retval;

	fn = default_llseek;
	if (file->f_op && file->f_op->llseek)
		fn = file->f_op->llseek;
	lock_kernel();
	retval = fn(file, offset, origin);
	unlock_kernel();
	return retval;
}

asmlinkage off_t sys_lseek(unsigned int fd, off_t offset, unsigned int origin)
{
	off_t retval;
	struct file * file;

	retval = -EBADF;
	file = fget(fd);
	if (!file)
		goto bad;
	retval = -EINVAL;
	if (origin <= 2) {
		loff_t res = llseek(file, offset, origin);
		retval = res;
		if (res != (loff_t)retval)
			retval = -EOVERFLOW;	/* LFS: should only happen on 32 bit platforms */
	}
	fput(file);
bad:
	return retval;
}

#if !defined(__alpha__)
asmlinkage long sys_llseek(unsigned int fd, unsigned long offset_high,
			   unsigned long offset_low, loff_t * result,
			   unsigned int origin)
{
	int retval;
	struct file * file;
	loff_t offset;

	retval = -EBADF;
	file = fget(fd);
	if (!file)
		goto bad;
	retval = -EINVAL;
	if (origin > 2)
		goto out_putf;

	offset = llseek(file, ((loff_t) offset_high << 32) | offset_low,
			origin);

	retval = (int)offset;
	if (offset >= 0) {
		retval = -EFAULT;
		if (!copy_to_user(result, &offset, sizeof(offset)))
			retval = 0;
	}
out_putf:
	fput(file);
bad:
	return retval;
}
#endif

asmlinkage ssize_t sys_read(unsigned int fd, char * buf, size_t count)
{
	ssize_t ret;
	struct file * file;

        /* RSBAC */
        #ifdef CONFIG_RSBAC_RW
        enum  rsbac_target_t          rsbac_target = T_NONE;
        union rsbac_target_id_t       rsbac_target_id;
        union rsbac_target_id_t       rsbac_new_target_id;
        union rsbac_attribute_value_t rsbac_attribute_value;
        #endif

	ret = -EBADF;
	file = fget(fd);
	if (file) {
		if (file->f_mode & FMODE_READ) {
			ret = locks_verify_area(FLOCK_VERIFY_READ, file->f_dentry->d_inode,
						file, file->f_pos, count);
			if (!ret) {
				ssize_t (*read)(struct file *, char *, size_t, loff_t *);

				ret = -EINVAL;

                                /* RSBAC */
                                #ifdef CONFIG_RSBAC_RW
#ifdef CONFIG_RSBAC_DEBUG
                                if (rsbac_debug_aef)
                                  printk(KERN_DEBUG "sys_read(): calling ADF\n");
#endif
                                /* get target type and id clear */
                                #ifdef CONFIG_RSBAC_NET_OBJ_RW
                                if(S_ISSOCK(file->f_dentry->d_inode->i_mode))
                                  {
                                    #if !defined(CONFIG_RSBAC_NET_OBJ_UNIX)
                                    if(   file->f_dentry->d_inode->u.socket_i.ops
                                       && (file->f_dentry->d_inode->u.socket_i.ops->family != AF_UNIX)
                                      )
                                    #endif
                                      {
                                        rsbac_target = T_NETOBJ;
                                        rsbac_target_id.netobj.sock_p
                                         = &file->f_dentry->d_inode->u.socket_i;
                                        rsbac_target_id.netobj.local_addr = NULL;
                                        rsbac_target_id.netobj.local_len = 0;
                                        rsbac_target_id.netobj.remote_addr = NULL;
                                        rsbac_target_id.netobj.remote_len = 0;
                                      }
                                  }
                                else
                                #endif
                                if(   S_ISFIFO(file->f_dentry->d_inode->i_mode)
                                      /* named FIFO only */
                                   && (file->f_dentry->d_sb->s_magic != PIPEFS_MAGIC)
                                  )
                                  {
                                    rsbac_target = T_FIFO;
                                    rsbac_target_id.fifo.device = file->f_dentry->d_inode->i_dev;
                                    rsbac_target_id.fifo.inode  = file->f_dentry->d_inode->i_ino;
                                    rsbac_target_id.fifo.dentry_p = file->f_dentry;
                                  }
                                else
                                if(S_ISREG(file->f_dentry->d_inode->i_mode))
                                  {
                                    rsbac_target = T_FILE;
                                    rsbac_target_id.file.device = file->f_dentry->d_inode->i_dev;
                                    rsbac_target_id.file.inode  = file->f_dentry->d_inode->i_ino;
                                    rsbac_target_id.file.dentry_p = file->f_dentry;
                                  }
                                else
                                if(S_ISBLK(file->f_dentry->d_inode->i_mode))
                                  {
                                    rsbac_target = T_DEV;
                                    rsbac_target_id.dev.type = D_block;
                                    rsbac_target_id.dev.id = file->f_dentry->d_inode->i_rdev;
                                  }
                                else
                                if(S_ISCHR(file->f_dentry->d_inode->i_mode))
                                  {
                                    rsbac_target = T_DEV;
                                    rsbac_target_id.dev.type = D_char;
                                    rsbac_target_id.dev.id = file->f_dentry->d_inode->i_rdev;
                                  }
                                rsbac_attribute_value.dummy = 0;
                                if (   (rsbac_target != T_NONE)
                                    && !rsbac_adf_request(R_READ,
                                                          current->pid,
                                                          rsbac_target,
                                                          rsbac_target_id,
                                                          A_none,
                                                          rsbac_attribute_value)
                                   )
                                  {
                                    ret = -EPERM;
                                  }
                                else
                                #endif /* CONFIG_RSBAC_RW */

				if (file->f_op && (read = file->f_op->read) != NULL)
					ret = read(file, buf, count, &file->f_pos);
			}
		}
		if (ret > 0)
			dnotify_parent(file->f_dentry, DN_ACCESS);

                /* RSBAC: notify adf of read file etc. */
                #ifdef CONFIG_RSBAC_RW
                if (   (ret > 0)
                    && (rsbac_target != T_NONE)
                   )
                  {
                    rsbac_new_target_id.dummy = 0;
                    if (rsbac_adf_set_attr(R_READ,
                                           current->pid,
                                           rsbac_target,
                                           rsbac_target_id,
                                           T_NONE,
                                           rsbac_new_target_id,
                                           A_none,
                                           rsbac_attribute_value))
                      {
                        printk(KERN_WARNING
                               "sys_read: rsbac_adf_set_attr() returned error\n");
                      }
                  }
                #endif

		fput(file);
	}
	return ret;
}

asmlinkage ssize_t sys_write(unsigned int fd, const char * buf, size_t count)
{
	ssize_t ret;
	struct file * file;

        /* RSBAC */
        #ifdef CONFIG_RSBAC_RW
        enum  rsbac_target_t          rsbac_target = T_NONE;
        union rsbac_target_id_t       rsbac_target_id;
        union rsbac_target_id_t       rsbac_new_target_id;
        union rsbac_attribute_value_t rsbac_attribute_value;
        #endif

	ret = -EBADF;
	file = fget(fd);
	if (file) {
		if (file->f_mode & FMODE_WRITE) {
			struct inode *inode = file->f_dentry->d_inode;
			ret = locks_verify_area(FLOCK_VERIFY_WRITE, inode, file,
				file->f_pos, count);
			if (!ret) {
				ssize_t (*write)(struct file *, const char *, size_t, loff_t *);
				ret = -EINVAL;

                                /* RSBAC */
                                #ifdef CONFIG_RSBAC_RW
#ifdef CONFIG_RSBAC_DEBUG
                                if (rsbac_debug_aef)
                                  printk(KERN_DEBUG "sys_write(): calling ADF\n");
#endif
                                /* get target type and id clear */
                                #ifdef CONFIG_RSBAC_NET_OBJ_RW
                                if(S_ISSOCK(file->f_dentry->d_inode->i_mode))
                                  {
                                    #if !defined(CONFIG_RSBAC_NET_OBJ_UNIX)
                                    if(   file->f_dentry->d_inode->u.socket_i.ops
                                       && (file->f_dentry->d_inode->u.socket_i.ops->family != AF_UNIX)
                                      )
                                    #endif
                                      {
                                        rsbac_target = T_NETOBJ;
                                        rsbac_target_id.netobj.sock_p
                                         = &file->f_dentry->d_inode->u.socket_i;
                                        rsbac_target_id.netobj.local_addr = NULL;
                                        rsbac_target_id.netobj.local_len = 0;
                                        rsbac_target_id.netobj.remote_addr = NULL;
                                        rsbac_target_id.netobj.remote_len = 0;
                                      }
                                  }
                                else
                                #endif
                                if(   S_ISFIFO(file->f_dentry->d_inode->i_mode)
                                      /* named FIFO only */
                                   && (file->f_dentry->d_sb->s_magic != PIPEFS_MAGIC)
                                  )
                                  {
                                    rsbac_target = T_FIFO;
                                    rsbac_target_id.fifo.device = file->f_dentry->d_inode->i_dev;
                                    rsbac_target_id.fifo.inode  = file->f_dentry->d_inode->i_ino;
                                    rsbac_target_id.fifo.dentry_p = file->f_dentry;
                                  }
                                else
                                if(S_ISREG(file->f_dentry->d_inode->i_mode))
                                  {
                                    rsbac_target = T_FILE;
                                    rsbac_target_id.file.device = file->f_dentry->d_inode->i_dev;
                                    rsbac_target_id.file.inode  = file->f_dentry->d_inode->i_ino;
                                    rsbac_target_id.file.dentry_p = file->f_dentry;
                                  }
                                else
                                if(S_ISBLK(file->f_dentry->d_inode->i_mode))
                                  {
                                    rsbac_target = T_DEV;
                                    rsbac_target_id.dev.type = D_block;
                                    rsbac_target_id.dev.id = file->f_dentry->d_inode->i_rdev;
                                  }
                                else
                                if(S_ISCHR(file->f_dentry->d_inode->i_mode))
                                  {
                                    rsbac_target = T_DEV;
                                    rsbac_target_id.dev.type = D_char;
                                    rsbac_target_id.dev.id = file->f_dentry->d_inode->i_rdev;
                                  }
                                rsbac_attribute_value.dummy = 0;
                                if (   (rsbac_target != T_NONE)
                                    && !rsbac_adf_request(R_WRITE,
                                                          current->pid,
                                                          rsbac_target,
                                                          rsbac_target_id,
                                                          A_none,
                                                          rsbac_attribute_value)
                                   )
                                  {
                                    ret = -EPERM;
                                  }
                                else
                                #endif /* CONFIG_RSBAC_RW */

				if (file->f_op && (write = file->f_op->write) != NULL)
					ret = write(file, buf, count, &file->f_pos);
			}
		}
		if (ret > 0)
			dnotify_parent(file->f_dentry, DN_MODIFY);

                /* RSBAC: notify adf of written file etc. */
                #ifdef CONFIG_RSBAC_RW
                if (   (ret > 0)
                    && (rsbac_target != T_NONE)
                   )
                  {
                    rsbac_new_target_id.dummy = 0;
                    if (rsbac_adf_set_attr(R_WRITE,
                                           current->pid,
                                           rsbac_target,
                                           rsbac_target_id,
                                           T_NONE,
                                           rsbac_new_target_id,
                                           A_none,
                                           rsbac_attribute_value))
                      {
                        printk(KERN_WARNING
                               "sys_write: rsbac_adf_set_attr() returned error\n");
                      }
                  }
                #endif

		fput(file);
	}
	return ret;
}

static ssize_t do_readv_writev(int type, struct file *file,
			       const struct iovec * vector,
			       unsigned long count)
{
	typedef ssize_t (*io_fn_t)(struct file *, char *, size_t, loff_t *);
	typedef ssize_t (*iov_fn_t)(struct file *, const struct iovec *, unsigned long, loff_t *);

	ssize_t tot_len;
	struct iovec iovstack[UIO_FASTIOV];
	struct iovec *iov=iovstack;
	ssize_t ret, i;
	io_fn_t fn;
	iov_fn_t fnv;
	struct inode *inode;

        /* RSBAC */
        #ifdef CONFIG_RSBAC_RW
        enum  rsbac_adf_request_t     rsbac_request;
        enum  rsbac_target_t          rsbac_target = T_NONE;
        union rsbac_target_id_t       rsbac_target_id;
        union rsbac_target_id_t       rsbac_new_target_id;
        union rsbac_attribute_value_t rsbac_attribute_value;
        #endif

	/*
	 * First get the "struct iovec" from user memory and
	 * verify all the pointers
	 */
	ret = 0;
	if (!count)
		goto out_nofree;
	ret = -EINVAL;
	if (count > UIO_MAXIOV)
		goto out_nofree;
	if (!file->f_op)
		goto out_nofree;
	if (count > UIO_FASTIOV) {
		ret = -ENOMEM;
		iov = kmalloc(count*sizeof(struct iovec), GFP_KERNEL);
		if (!iov)
			goto out_nofree;
	}
	ret = -EFAULT;
	if (copy_from_user(iov, vector, count*sizeof(*vector)))
		goto out;

	/*
	 * Single unix specification:
	 * We should -EINVAL if an element length is not >= 0 and fitting an ssize_t
	 * The total length is fitting an ssize_t
	 *
	 * Be careful here because iov_len is a size_t not an ssize_t
	 */
	 
	tot_len = 0;
	ret = -EINVAL;
	for (i = 0 ; i < count ; i++) {
		ssize_t tmp = tot_len;
		ssize_t len = (ssize_t) iov[i].iov_len;
		if (len < 0)	/* size_t not fitting an ssize_t .. */
			goto out;
		tot_len += len;
		if (tot_len < tmp) /* maths overflow on the ssize_t */
			goto out;
	}

	inode = file->f_dentry->d_inode;
	/* VERIFY_WRITE actually means a read, as we write to user space */
	ret = locks_verify_area((type == VERIFY_WRITE
				 ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE),
				inode, file, file->f_pos, tot_len);
	if (ret) goto out;

	fnv = (type == VERIFY_WRITE ? file->f_op->readv : file->f_op->writev);
	if (fnv) {
                /* RSBAC */
                #ifdef CONFIG_RSBAC_RW
#ifdef CONFIG_RSBAC_DEBUG
                if (rsbac_debug_aef)
                  printk(KERN_DEBUG "do_readv_writev() [sys_vread(), sys_vwrite()]: calling ADF\n");
#endif
                /* get target type and id clear */
                #ifdef CONFIG_RSBAC_NET_OBJ_RW
                if(S_ISSOCK(inode->i_mode))
                  {
                    #if !defined(CONFIG_RSBAC_NET_OBJ_UNIX)
                    if(   inode->u.socket_i.ops
                       && (inode->u.socket_i.ops->family != AF_UNIX)
                      )
                    #endif
                      {
                        rsbac_target = T_NETOBJ;
                        rsbac_target_id.netobj.sock_p = &inode->u.socket_i;
                        rsbac_target_id.netobj.local_addr = NULL;
                        rsbac_target_id.netobj.local_len = 0;
                        rsbac_target_id.netobj.remote_addr = NULL;
                        rsbac_target_id.netobj.remote_len = 0;
                      }
                  }
                else
                #endif
                if(   S_ISFIFO(inode->i_mode)
                   /* named FIFO only */
                   && (inode->i_sb->s_magic != PIPEFS_MAGIC)
                  )
                  {
                    rsbac_target = T_FIFO;
                    rsbac_target_id.fifo.device = inode->i_dev;
                    rsbac_target_id.fifo.inode  = inode->i_ino;
                    rsbac_target_id.fifo.dentry_p = file->f_dentry;
                  }
                else
                if(S_ISREG(inode->i_mode))
                  {
                    rsbac_target = T_FILE;
                    rsbac_target_id.file.device = inode->i_dev;
                    rsbac_target_id.file.inode  = inode->i_ino;
                    rsbac_target_id.file.dentry_p = file->f_dentry;
                  }
                else
                if(S_ISBLK(inode->i_mode))
                  {
                    rsbac_target = T_DEV;
                    rsbac_target_id.dev.type = D_block;
                    rsbac_target_id.dev.id = inode->i_rdev;
                  }
                else
                if(S_ISCHR(inode->i_mode))
                  {
                    rsbac_target = T_DEV;
                    rsbac_target_id.dev.type = D_char;
                    rsbac_target_id.dev.id = inode->i_rdev;
                  }
                if(type == VERIFY_WRITE)
                  rsbac_request = R_READ;
                else
                  rsbac_request = R_WRITE;
                rsbac_attribute_value.dummy = 0;
                if (   (rsbac_target != T_NONE)
                    && !rsbac_adf_request(rsbac_request,
                                          current->pid,
                                          rsbac_target,
                                          rsbac_target_id,
                                          A_none,
                                          rsbac_attribute_value)
                   )
                  {
                    ret = -EPERM;
                  }
                else
                #endif /* CONFIG_RSBAC_RW */

  		ret = fnv(file, iov, count, &file->f_pos);

                /* RSBAC: notify adf of read or written file etc. */
                #ifdef CONFIG_RSBAC_RW
                if (   (ret > 0)
                    && (rsbac_target != T_NONE)
                   )
                  {
                    rsbac_new_target_id.dummy = 0;
                    if (rsbac_adf_set_attr(rsbac_request,
                                           current->pid,
                                           rsbac_target,
                                           rsbac_target_id,
                                           T_NONE,
                                           rsbac_new_target_id,
                                           A_none,
                                           rsbac_attribute_value))
                      {
                        printk(KERN_WARNING
                               "do_readv_writev() [sys_vread(), sys_vwrite()]: rsbac_adf_set_attr() returned error\n");
                      }
                  }
                #endif

		if (ret != -ENOSYS)
			goto out;
	}

	/* VERIFY_WRITE actually means a read, as we write to user space */
	fn = (type == VERIFY_WRITE ? file->f_op->read :
	      (io_fn_t) file->f_op->write);

	ret = 0;
	vector = iov;
	while (count > 0) {
		void * base;
		size_t len;
		ssize_t nr;

		base = vector->iov_base;
		len = vector->iov_len;
		vector++;
		count--;

		nr = fn(file, base, len, &file->f_pos);

		if (nr < 0) {
			if (!ret) ret = nr;
			break;
		}
		ret += nr;
		if (nr != len)
			break;
	}

out:
	if (iov != iovstack)
		kfree(iov);
out_nofree:
	/* VERIFY_WRITE actually means a read, as we write to user space */
	if ((ret + (type == VERIFY_WRITE)) > 0)
		dnotify_parent(file->f_dentry,
			(type == VERIFY_WRITE) ? DN_ACCESS : DN_MODIFY);
	return ret;
}

asmlinkage ssize_t sys_readv(unsigned long fd, const struct iovec * vector,
			     unsigned long count)
{
	struct file * file;
	ssize_t ret;


	ret = -EBADF;
	file = fget(fd);
	if (!file)
		goto bad_file;
	if (file->f_op && (file->f_mode & FMODE_READ) &&
	    (file->f_op->readv || file->f_op->read))
		ret = do_readv_writev(VERIFY_WRITE, file, vector, count);
	fput(file);

bad_file:
	return ret;
}

asmlinkage ssize_t sys_writev(unsigned long fd, const struct iovec * vector,
			      unsigned long count)
{
	struct file * file;
	ssize_t ret;


	ret = -EBADF;
	file = fget(fd);
	if (!file)
		goto bad_file;
	if (file->f_op && (file->f_mode & FMODE_WRITE) &&
	    (file->f_op->writev || file->f_op->write))
		ret = do_readv_writev(VERIFY_READ, file, vector, count);
	fput(file);

bad_file:
	return ret;
}

/* From the Single Unix Spec: pread & pwrite act like lseek to pos + op +
   lseek back to original location.  They fail just like lseek does on
   non-seekable files.  */

asmlinkage ssize_t sys_pread(unsigned int fd, char * buf,
			     size_t count, loff_t pos)
{
	ssize_t ret;
	struct file * file;
	ssize_t (*read)(struct file *, char *, size_t, loff_t *);

        /* RSBAC */
        #ifdef CONFIG_RSBAC_RW
        enum  rsbac_target_t          rsbac_target = T_NONE;
        union rsbac_target_id_t       rsbac_target_id;
        union rsbac_target_id_t       rsbac_new_target_id;
        union rsbac_attribute_value_t rsbac_attribute_value;
        #endif

	ret = -EBADF;
	file = fget(fd);
	if (!file)
		goto bad_file;
	if (!(file->f_mode & FMODE_READ))
		goto out;
	ret = locks_verify_area(FLOCK_VERIFY_READ, file->f_dentry->d_inode,
				file, pos, count);
	if (ret)
		goto out;
	ret = -EINVAL;
	if (!file->f_op || !(read = file->f_op->read))
		goto out;
	if (pos < 0)
		goto out;

        /* RSBAC */
        #ifdef CONFIG_RSBAC_RW
#ifdef CONFIG_RSBAC_DEBUG
        if (rsbac_debug_aef)
          printk(KERN_DEBUG "sys_pread(): calling ADF\n");
#endif
        /* get target type and id clear */
        if(S_ISREG(file->f_dentry->d_inode->i_mode))
          {
            rsbac_target = T_FILE;
            rsbac_target_id.file.device = file->f_dentry->d_inode->i_dev;
            rsbac_target_id.file.inode  = file->f_dentry->d_inode->i_ino;
            rsbac_target_id.file.dentry_p = file->f_dentry;
          }
        else
        if(S_ISBLK(file->f_dentry->d_inode->i_mode))
          {
            rsbac_target = T_DEV;
            rsbac_target_id.dev.type = D_block;
            rsbac_target_id.dev.id = file->f_dentry->d_inode->i_rdev;
          }
        else
        if(S_ISCHR(file->f_dentry->d_inode->i_mode))
          {
            rsbac_target = T_DEV;
            rsbac_target_id.dev.type = D_char;
            rsbac_target_id.dev.id = file->f_dentry->d_inode->i_rdev;
          }
        rsbac_attribute_value.dummy = 0;
        if (   (rsbac_target != T_NONE)
            && !rsbac_adf_request(R_READ,
                                  current->pid,
                                  rsbac_target,
                                  rsbac_target_id,
                                  A_none,
                                  rsbac_attribute_value)
           )
          {
            ret = -EPERM;
            goto out;
          }
        #endif /* CONFIG_RSBAC_RW */

	ret = read(file, buf, count, &pos);
	if (ret > 0)
		dnotify_parent(file->f_dentry, DN_ACCESS);

        /* RSBAC: notify adf of read file etc. */
        #ifdef CONFIG_RSBAC_RW
        if (   (ret > 0)
            && (rsbac_target != T_NONE)
           )
          {
            rsbac_new_target_id.dummy = 0;
            if (rsbac_adf_set_attr(R_READ,
                                   current->pid,
                                   rsbac_target,
                                   rsbac_target_id,
                                   T_NONE,
                                   rsbac_new_target_id,
                                   A_none,
                                   rsbac_attribute_value))
              {
                printk(KERN_WARNING
                        "sys_pread(): rsbac_adf_set_attr() returned error\n");
              }
          }
        #endif

out:
	fput(file);
bad_file:
	return ret;
}

asmlinkage ssize_t sys_pwrite(unsigned int fd, const char * buf,
			      size_t count, loff_t pos)
{
	ssize_t ret;
	struct file * file;
	ssize_t (*write)(struct file *, const char *, size_t, loff_t *);

        /* RSBAC */
        #ifdef CONFIG_RSBAC_RW
        enum  rsbac_target_t          rsbac_target = T_NONE;
        union rsbac_target_id_t       rsbac_target_id;
        union rsbac_target_id_t       rsbac_new_target_id;
        union rsbac_attribute_value_t rsbac_attribute_value;
        #endif

	ret = -EBADF;
	file = fget(fd);
	if (!file)
		goto bad_file;
	if (!(file->f_mode & FMODE_WRITE))
		goto out;
	ret = locks_verify_area(FLOCK_VERIFY_WRITE, file->f_dentry->d_inode,
				file, pos, count);
	if (ret)
		goto out;
	ret = -EINVAL;
	if (!file->f_op || !(write = file->f_op->write))
		goto out;
	if (pos < 0)
		goto out;

        /* RSBAC */
        #ifdef CONFIG_RSBAC_RW
#ifdef CONFIG_RSBAC_DEBUG
        if (rsbac_debug_aef)
          printk(KERN_DEBUG "sys_pwrite(): calling ADF\n");
#endif
        /* get target type and id clear */
        if(S_ISREG(file->f_dentry->d_inode->i_mode))
          {
            rsbac_target = T_FILE;
            rsbac_target_id.file.device = file->f_dentry->d_inode->i_dev;
            rsbac_target_id.file.inode  = file->f_dentry->d_inode->i_ino;
            rsbac_target_id.file.dentry_p = file->f_dentry;
          }
        else
        if(S_ISBLK(file->f_dentry->d_inode->i_mode))
          {
            rsbac_target = T_DEV;
            rsbac_target_id.dev.type = D_block;
            rsbac_target_id.dev.id = file->f_dentry->d_inode->i_rdev;
          }
        else
        if(S_ISCHR(file->f_dentry->d_inode->i_mode))
          {
            rsbac_target = T_DEV;
            rsbac_target_id.dev.type = D_char;
            rsbac_target_id.dev.id = file->f_dentry->d_inode->i_rdev;
          }
        rsbac_attribute_value.dummy = 0;
        if (   (rsbac_target != T_NONE)
            && !rsbac_adf_request(R_WRITE,
                                  current->pid,
                                  rsbac_target,
                                  rsbac_target_id,
                                  A_none,
                                  rsbac_attribute_value)
           )
          {
            ret = -EPERM;
            goto out;
          }
        #endif /* CONFIG_RSBAC_RW */

	ret = write(file, buf, count, &pos);
	if (ret > 0)
		dnotify_parent(file->f_dentry, DN_MODIFY);

        /* RSBAC: notify adf of written file etc. */
        #ifdef CONFIG_RSBAC_RW
        if (   (ret > 0)
            && (rsbac_target != T_NONE)
           )
          {
            rsbac_new_target_id.dummy = 0;
            if (rsbac_adf_set_attr(R_WRITE,
                                   current->pid,
                                   rsbac_target,
                                   rsbac_target_id,
                                   T_NONE,
                                   rsbac_new_target_id,
                                   A_none,
                                   rsbac_attribute_value))
              {
                printk(KERN_WARNING
                       "sys_pwrite(): rsbac_adf_set_attr() returned error\n");
              }
          }
        #endif

out:
	fput(file);
bad_file:
	return ret;
}
