/************************************************* */
/* Rule Set Based Access Control                   */
/* Implementation of ACI data structures           */
/* Author and (c) 1999, 2000: Amon Ott             */
/* (some smaller parts copied from fs/namei.c      */
/*  and others)                                    */
/*                                                 */
/* Last modified: 09/Nov/2000                      */
/************************************************* */

#include <linux/types.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/quotaops.h>
#include <linux/proc_fs.h>
#include <linux/msdos_fs.h>
#include <linux/iso_fs.h>
#include <linux/nfs_fs.h>
#include <linux/ext2_fs.h>
#include <linux/coda_psdev.h>
#include <linux/ncp_fs.h>
#include <linux/smb.h>
#include <linux/mm.h>
#include <linux/malloc.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/config.h>
#include <linux/module.h>
#include <asm/uaccess.h>
#include <rsbac/types.h>
#include <rsbac/aci_data_structures.h>
#include <rsbac/error.h>
#include <rsbac/helpers.h>
#include <rsbac/fs.h>
#include <rsbac/getname.h>
#include <rsbac/adf.h>
#include <rsbac/adf_main.h>
#include <rsbac/reg.h>
#include <linux/string.h>
#include <linux/kdev_t.h>
#include <linux/smp_lock.h>

#ifdef CONFIG_RSBAC_PM
#include <rsbac/pm.h>
#endif

#if defined(CONFIG_RSBAC_RC) || defined(CONFIG_RSBAC_RC_MAINT)
#include <rsbac/rc.h>
#endif

#if defined(CONFIG_RSBAC_AUTH) || defined(CONFIG_RSBAC_AUTH_MAINT)
#include <rsbac/auth.h>
#endif

#if defined(CONFIG_RSBAC_ACL) || defined(CONFIG_RSBAC_ACL_MAINT)
#include <rsbac/acl.h>
#endif

#if defined(CONFIG_RSBAC_AUTO_WRITE) && (CONFIG_RSBAC_AUTO_WRITE > 0)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
#define __KERNEL_SYSCALLS__
#endif
#include <linux/unistd.h>
#include <linux/timer.h>
static u_int auto_interval = CONFIG_RSBAC_AUTO_WRITE * HZ;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
static struct wait_queue * rsbacd_wait = NULL;
#else
static DECLARE_WAIT_QUEUE_HEAD(rsbacd_wait);
#endif
static struct timer_list rsbac_timer;
#endif /* CONFIG_RSBAC_AUTO_WRITE */

/************************************************************************** */
/*                          Global Variables                                */
/************************************************************************** */

/* The following global variables are needed for access to ACI data.        */
/* For efficiency reasons there is an array of lists for files and dirs,    */
/* using (inode number) modulo (array size) as a hash function.             */

static boolean rsbac_initialized = FALSE;

static char    compiled_modules[80];

#ifdef CONFIG_RSBAC_REG
kdev_t rsbac_root_dev;
EXPORT_SYMBOL(rsbac_root_dev);
EXPORT_SYMBOL(rsbac_write_sem);
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
struct semaphore rsbac_write_sem = MUTEX;
#else
DECLARE_MUTEX(rsbac_write_sem);
#endif

static struct rsbac_device_list_head_t   device_list_head;
static struct rsbac_dev_list_head_t      dev_list_head;
static struct rsbac_ipc_list_head_t      ipc_list_head;
static struct rsbac_user_list_head_t     user_list_head;
static struct rsbac_process_list_head_t  process_list_head;

/* Default ACIs: implemented as variables, might be changeable some time */
/* When adding an fd item, we need a default aci                            */
static struct rsbac_file_dir_aci_t default_fd_aci = DEFAULT_FD_ACI;

/* And then there are rsbac-internal dir items    */
static struct rsbac_file_dir_aci_t default_rsbac_dir_aci = DEFAULT_RSBAC_DIR_ACI;

/* And the rsbac root dir items for each device (end of recursive inherit) */
static struct rsbac_file_dir_aci_t default_root_dir_aci = DEFAULT_ROOT_DIR_ACI;

/* default for new dev items */
static struct rsbac_dev_aci_t default_dev_aci =  DEFAULT_DEV_ACI;

/* default for new IPC items */
static struct rsbac_ipc_aci_t default_ipc_aci =  DEFAULT_IPC_ACI;

/* default for new user items */
static struct rsbac_user_aci_t default_u_aci =  DEFAULT_U_ACI;

/* default for new process items */
static struct rsbac_process_aci_t default_p_aci =  DEFAULT_P_ACI;

/* default for init process item */
static struct rsbac_process_aci_t default_init_p_aci =  DEFAULT_INIT_P_ACI;

#if defined(CONFIG_RSBAC_PROC) && defined(CONFIG_PROC_FS)
#include <rsbac/proc_fs.h>
#ifndef PROC_BLOCK_SIZE
#define PROC_BLOCK_SIZE	(3*1024)  /* 4K page size but our output routines use some slack for overruns */
#endif

#ifdef CONFIG_RSBAC_XSTATS
static u_long get_attr_count[T_NONE] = {0,0,0,0,0,0,0};
static u_long set_attr_count[T_NONE] = {0,0,0,0,0,0,0};
static u_long remove_count[T_NONE] = {0,0,0,0,0,0,0};
#endif

#ifdef CONFIG_RSBAC_REG
EXPORT_SYMBOL(proc_rsbac_root_p);
#endif
struct proc_dir_entry * proc_rsbac_root_p = NULL;

#ifdef CONFIG_RSBAC_REG
EXPORT_SYMBOL(proc_rsbac_backup_p);
#endif
struct proc_dir_entry * proc_rsbac_backup_p = NULL;

#endif /* PROC */


/**************************************************/
/*       Declarations of internal functions       */
/**************************************************/

  static struct rsbac_file_dir_list_item_t 
          * do_add_file_dir_item(struct rsbac_file_dir_aci_t,
                                 struct rsbac_file_dir_list_head_t *);

  static struct rsbac_file_dir_list_item_t 
          * add_file_dir_item(struct rsbac_file_dir_aci_t,
                              struct rsbac_device_list_item_t *);

  void convert_old_file_dir_aci(struct rsbac_file_dir_old_aci_t *,
                                struct rsbac_file_dir_aci_t *);

  void convert_old_old_file_dir_aci(struct rsbac_file_dir_old_old_aci_t *,
                                    struct rsbac_file_dir_aci_t *);

  void convert_old_dev_aci(struct rsbac_dev_old_aci_t *,
                           struct rsbac_dev_aci_t *);

  void convert_old_old_dev_aci(struct rsbac_dev_old_old_aci_t *,
                               struct rsbac_dev_aci_t *);

  void convert_old_user_aci(struct rsbac_user_old_aci_t *,
                            struct rsbac_user_aci_t *);

  void convert_old_old_user_aci(struct rsbac_user_old_old_aci_t *,
                                struct rsbac_user_aci_t *);

  static struct rsbac_device_list_item_t * lookup_device(kdev_t);

  static struct rsbac_file_dir_list_item_t *
         do_lookup_file_dir(u_long,
                            struct rsbac_file_dir_list_head_t *);

  static struct rsbac_file_dir_list_item_t *
         lookup_file_dir(u_long,
                         struct rsbac_device_list_item_t *);

/************************************************* */
/*               Internal Help functions           */
/************************************************* */

static inline int fd_hash(u_long inode)
  {
    return(inode % RSBAC_NR_FD_LISTS);
  }

/* These help functions do NOT handle data consistency protection by        */
/* rw-spinlocks! This is done exclusively by non-internal functions!        */

/************************************************************************** */
/* Read/Write functions                                                     */

/* The read functions are only called from rsbac_init(), the write functions*/
/* are called on every change from all functions that modify data.          */

/* This help function protects some filesystems from being written to */
/* and disables writing under some conditions, e.g. in an interrupt */ 

boolean writable(struct super_block * sb_p)
 {
#ifdef CONFIG_RSBAC_NO_WRITE
   return(FALSE);
#else
   if (!sb_p || !sb_p->s_dev)
     return(FALSE);
   if (   rsbac_debug_no_write
       || (sb_p->s_flags & MS_RDONLY)
       || in_interrupt())
     return(FALSE);
   if (   !MAJOR(sb_p->s_dev)
#ifndef CONFIG_RSBAC_MSDOS_WRITE
       || (sb_p->s_magic == MSDOS_SUPER_MAGIC)
#endif
       || (sb_p->s_magic == NFS_SUPER_MAGIC)
       || (sb_p->s_magic == CODA_SUPER_MAGIC)
       || (sb_p->s_magic == NCP_SUPER_MAGIC)
       || (sb_p->s_magic == SMB_SUPER_MAGIC)
       || (sb_p->s_magic == ISOFS_SUPER_MAGIC))
     return(FALSE);
   else
     return(TRUE);
#endif
 }

/* This lookup function ensures correct access to the file system.          */
/* It returns a pointer to the dentry of the rsbac directory on the mounted */
/* device specified by kdev. If the directory    */
/* does not exist, it is created, if create_dir == TRUE and writable. */

static int lookup_aci_path_dentry(kdev_t     kdev,
                            struct dentry ** dir_dentry_pp,
                                 boolean     create_dir,
                                 struct rsbac_device_list_item_t * device_p)
  {
    struct dentry * dir_dentry_p = NULL;
    struct dentry * root_dentry_p = NULL;
    int             err=0;
    struct super_block * sb_p;
    struct rsbac_file_dir_aci_t  fd_aci;

    if(!dir_dentry_pp)
      return(-RSBAC_EINVALIDPOINTER);

    if(!device_p)
      {
        printk(KERN_WARNING
               "lookup_aci_path_dentry(): No entry for device %02u:%02u\n",
               MAJOR(kdev), MINOR(kdev));
        return(-RSBAC_EINVALIDDEV);
      }
    /* use for DEBUG only! */
/*    if(create_dir)
      return(-RSBAC_ENOTFOUND); */

    /* already looked up earlier? */
    if(device_p->rsbac_dir_dentry_p)
      {
        *dir_dentry_pp = dget(device_p->rsbac_dir_dentry_p);
        if(rsbac_debug_ds)
          printk(KERN_DEBUG
                 "lookup_aci_path_dentry(): device_p->rsbac_dir_dentry_p->d_count is %i!\n",
             #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
                 device_p->rsbac_dir_dentry_p->d_count);
             #else
                 device_p->rsbac_dir_dentry_p->d_count.counter);
             #endif
        return(0);
      }

    /* get super_block-pointer */
    sb_p = get_super(kdev);
    if (!sb_p)
      {
        printk(KERN_WARNING
               "lookup_aci_path_dentry(): No super_block for device %02u:%02u\n",
               MAJOR(kdev), MINOR(kdev));
        return (-RSBAC_EINVALIDDEV);
      }

    if (!sb_p->s_root)
      {
        printk(KERN_WARNING
               "lookup_aci_path_dentry(): Super_block for device %02u:%02u has no dentry!\n",
               MAJOR(kdev), MINOR(kdev));
        return (-RSBAC_EINVALIDDEV);
      }

    if (!sb_p->s_root->d_inode)
      {
        printk(KERN_WARNING
               "lookup_aci_path_dentry(): Super_block for device %02u:%02u has no dentry->d_inode!\n",
               MAJOR(kdev), MINOR(kdev));
        return (-RSBAC_EINVALIDDEV);
      }

    /* lookup dentry of ACI_PATH on this device */
    if(rsbac_debug_ds)
      printk(KERN_DEBUG
             "lookup_aci_path_dentry(): lookup rsbac path %s for device %02u:%02u, sb_p->s_root->d_count is %i!\n",
             RSBAC_ACI_PATH, MAJOR(kdev), MINOR(kdev),
             #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
             sb_p->s_root->d_count);
             #else
             sb_p->s_root->d_count.counter);
             #endif
    #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
    dir_dentry_p = rsbac_lookup_dentry(RSBAC_ACI_PATH, dget(sb_p->s_root), 0);
    #else
    dir_dentry_p = rsbac_lookup_one(RSBAC_ACI_PATH, sb_p->s_root);
    #endif
    if(IS_ERR(dir_dentry_p))
      switch(PTR_ERR(dir_dentry_p))
        {
          case -ENOENT       :
          case -ENOTDIR      : printk(KERN_WARNING
                                      "lookup_aci_path_dentry(): device %02u:%02u root dir is invalid!\n",
                                      MAJOR(kdev), MINOR(kdev));
                               return(-RSBAC_ENOROOTDIR);
          case -ENOMEM       : printk(KERN_WARNING
                                      "lookup_aci_path_dentry(): memory allocation error!\n");
                               return(-RSBAC_ENOROOTDIR);
          case -ENAMETOOLONG : printk(KERN_WARNING
                                      "lookup_aci_path_dentry(): ACI_PATH too long on fs!\n");
                               return(-RSBAC_EPATHTOOLONG);
          case -EACCES       : printk(KERN_WARNING
                                      "lookup_aci_path_dentry(): No access to ACI_PATH!\n");
                               return(-RSBAC_EACCESS);
          default            : printk(KERN_WARNING
                                      "lookup_aci_path_dentry(): Error on root dir: %li!\n",
                                      PTR_ERR(dir_dentry_p));
                               return(-RSBAC_ENOROOTDIR);
        }

    if (!dir_dentry_p)
      {
        printk(KERN_WARNING
               "lookup_aci_path_dentry(): rsbac_lookup_(dentry|one) returned null pointer!\n");
               return(-RSBAC_EINVALIDPOINTER);
      }
    if (!dir_dentry_p->d_inode)
      { /* dir could not be found -> try to create it */
        /* but only, if allowed... */
        if (!create_dir)
          {
            dput(dir_dentry_p);
            return (-RSBAC_ENOTFOUND);
          }
        if(rsbac_debug_ds)
          printk(KERN_DEBUG
                 "lookup_aci_path_dentry(): try to create dir, first test writable!\n");
        /* ... and writable. */
        if (!writable(sb_p))
          { /* mounted read only or special case */
            dput(dir_dentry_p);
            return(-RSBAC_ENOTWRITABLE);
          }
        root_dentry_p = lock_parent(dir_dentry_p);
        err = PTR_ERR(root_dentry_p);
        if (   IS_ERR(root_dentry_p))
          {
            dput(dir_dentry_p);
            return(-RSBAC_ECOULDNOTCREATEPATH);
          }
        if (   !root_dentry_p->d_inode
            || !root_dentry_p->d_inode->i_op
            || !root_dentry_p->d_inode->i_op->mkdir)
          {
            unlock_dir(root_dentry_p);
            dput(dir_dentry_p);
            return(-RSBAC_ECOULDNOTCREATEPATH);
          }
        DQUOT_INIT(root_dentry_p->d_inode);
        err = root_dentry_p->d_inode->i_op->mkdir(root_dentry_p->d_inode,
                                                  dir_dentry_p,
                                                  RSBAC_ACI_DIR_MODE);
        unlock_dir(root_dentry_p);
        if (err)
          {
            dput(dir_dentry_p);
            return(-RSBAC_ECOULDNOTCREATEPATH);
          }

        /* creation was successful, so set attributes for dir to */
        /* rsbac_defaults (rsbac_internal etc.) */
        if(rsbac_debug_ds)
          printk(KERN_DEBUG
                 "lookup_aci_path_dentry(): set attributes for new dir!\n");
        fd_aci = default_rsbac_dir_aci;
        fd_aci.id = dir_dentry_p->d_inode->i_ino;
        add_file_dir_item(fd_aci, lookup_device(kdev));
      }
    else /* was found */
      { /* check, whether this is a dir */
        if(!S_ISDIR(dir_dentry_p->d_inode->i_mode))
          { /* no dir! We have a real prob here! */
            dput(dir_dentry_p);
            printk(KERN_WARNING
                   "lookup_aci_path_dentry(): supposed /%s dir on dev %02u:%02u is no dir!\n",
                   RSBAC_ACI_PATH,
                   MAJOR(kdev),
                   MINOR(kdev));
                   return(-RSBAC_EACCESS);
          }
      }
    if(rsbac_debug_ds)
      printk(KERN_DEBUG
             "lookup_aci_path_dentry(): dir_dentry_p->d_count is %i!\n",
             #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
             dir_dentry_p->d_count);
             #else
             dir_dentry_p->d_count.counter);
             #endif
    if(rsbac_debug_ds)
      printk(KERN_DEBUG
             "lookup_aci_path_dentry(): sb_p->s_root->d_count is now %i!\n",
             #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
             sb_p->s_root->d_count);
             #else
             sb_p->s_root->d_count.counter);
             #endif
    /* we want to keep dir_dentry_p in device_item -> make another dget */
    /* this must be undone in remove_device_item! */
    *dir_dentry_pp = dget(dir_dentry_p);
    device_p->rsbac_dir_dentry_p = dir_dentry_p;
    return (err);
  }

/* read_fd_lists() */
/* Reading all file and dir aci from specified file system.                 */
/* This function returns an error, if any of the files for this device      */
/* could not be read successfully. Nevertheless, parts of them might have   */
/* been read correctly.                                                     */

static int read_fd_lists(struct rsbac_device_list_item_t * device_p,
                         kdev_t kdev)
  {
    struct dentry * dir_dentry_p;
    struct file     file;
    char          * name;
    char            number[7];
    int             err = 0;
    int             tmperr,i;
    u_int           aci_version;
    u_long          read_count = 0, read_sum = 0;
    struct rsbac_file_dir_list_head_t * head_p;
    struct rsbac_file_dir_aci_t         fd_aci;
    struct rsbac_file_dir_old_aci_t     old_fd_aci;
    struct rsbac_file_dir_old_old_aci_t old_old_fd_aci;
    mm_segment_t    oldfs;
    boolean         wrong_list;
    u_long flags;

    if(!device_p)
      return(-RSBAC_EINVALIDPOINTER);

    /* lookup dentry of ACI_PATH on this device without create, */
    /* returns errorcode, if failed */
    if ((err = lookup_aci_path_dentry(kdev, &dir_dentry_p, FALSE, device_p)))
      return(err);

    name = kmalloc(RSBAC_MAXNAMELEN, GFP_KERNEL);
    if(!name)
      return -RSBAC_ENOMEM;

    /* read all the list files */
    for (i=0; i<RSBAC_NR_FD_LISTS; i++)
      {
        /* shorten access to list head */
        head_p = &device_p->head_table[i];
        /* no item for wrong list found so far */
        wrong_list = FALSE;

        /* construct name from base name + number */
        strcpy(name, RSBAC_ACI_FD_NAME);
        strcat(name, inttostr(number,i) );

        /* open file for reading */
        if ((tmperr = rsbac_read_open(name,
                                      &file,
                                      kdev) ))
          {
            err = tmperr;
            continue;
	  }
        /* OK, now we can start reading */
        /* There is a read function for this file, so read as many ACIs */
        /* as possible. A positive return val means a read success,     */
        /* 0 end of file and a negative value an error.                 */

        if (rsbac_debug_ds)
          printk(KERN_DEBUG "read_fd_lists(): reading from file %s\n",
                 name);

        /* get access */
        rsbac_write_lock(&head_p->lock, &flags);

        /* Set current user space to kernel space, because read() writes */
        /* to user space */
        oldfs = get_fs();
        set_fs(KERNEL_DS);
        read_count = 0;

        tmperr = file.f_op->read(&file,
                                 (char *) &aci_version,
                                 sizeof(aci_version),
                                 &file.f_pos);
        /* error? */
	if (tmperr < sizeof(aci_version))
	  {
            printk(KERN_WARNING
                   "read_fd_lists(): read error from file %u on device %02u:%02u!\n",
                   i, MAJOR(kdev), MINOR(kdev));
            err |= -RSBAC_EREADFAILED;
            goto end_read;
          }
        /* if wrong aci version, set no_write and skip */
        switch(aci_version)
          {
            case RSBAC_FD_ACI_VERSION:
                break;
            case RSBAC_FD_OLD_ACI_VERSION:
                printk(KERN_WARNING
                       "read_fd_lists(): upconverting old aci for list %i on device %02u:%02u!\n",
                       i, MAJOR(kdev), MINOR(kdev));
                if(rsbac_debug_ds)
                  printk(KERN_DEBUG
                         "read_fd_lists(): old aci version %u, current %u!\n",
                         aci_version, RSBAC_FD_ACI_VERSION);
                /* set dirty to force saving (if non-sync) */
                head_p->dirty = TRUE;
                break;
            case RSBAC_FD_OLD_OLD_ACI_VERSION:
                printk(KERN_WARNING
                       "read_fd_lists(): upconverting old old aci for list %i on device %02u:%02u!\n",
                       i, MAJOR(kdev), MINOR(kdev));
                if(rsbac_debug_ds)
                  printk(KERN_DEBUG
                         "read_fd_lists(): old old aci version %u, current %u!\n",
                         aci_version, RSBAC_FD_ACI_VERSION);
                /* set dirty to force saving (if non-sync) */
                head_p->dirty = TRUE;
                break;
            default:
                printk(KERN_WARNING
                       "read_fd_lists(): wrong aci version %u, current %u - skipping file %i and setting no_write for device %02u:%02u !\n",
                       aci_version, RSBAC_FD_ACI_VERSION, i, MAJOR(kdev), MINOR(kdev));
                device_p->no_write = TRUE;
                goto end_read;
          }

	do
	  {
	    switch(aci_version)
	      {
                case RSBAC_FD_ACI_VERSION:
                    tmperr = file.f_op->read(&file,
                                             (char *) &fd_aci,
                                             sizeof(fd_aci),
                                             &file.f_pos);
                    break;
                case RSBAC_FD_OLD_ACI_VERSION:
                    tmperr = file.f_op->read(&file,
                                             (char *) &old_fd_aci,
                                             sizeof(old_fd_aci),
                                             &file.f_pos);
                    break;
                case RSBAC_FD_OLD_OLD_ACI_VERSION:
                    tmperr = file.f_op->read(&file,
                                             (char *) &old_old_fd_aci,
                                             sizeof(old_old_fd_aci),
                                             &file.f_pos);
                    break;
                default:
                    err |= -RSBAC_EREADFAILED;
                    goto end_read;
              }

            /* if successful, add item without writing to disk */
	    if (tmperr > 0)
	      {
                switch(aci_version)
	          {
                    case RSBAC_FD_OLD_ACI_VERSION:
                        convert_old_file_dir_aci(&old_fd_aci, &fd_aci);
                        break;
                    case RSBAC_FD_OLD_OLD_ACI_VERSION:
                        convert_old_old_file_dir_aci(&old_old_fd_aci, &fd_aci);
                        break;
                    default:
                  }
                if(fd_hash(fd_aci.id) == i)
                  { /* item is for this list */
                    if(do_lookup_file_dir(fd_aci.id, head_p))
                      { /* item id exists -> complain and skip */
                        printk(KERN_WARNING
                               "read_fd_lists(): aci for id %lu on list %i, device %02u:%02u exists -> ignored - run rsbac_check!\n",
                               fd_aci.id, i, MAJOR(kdev), MINOR(kdev));
                        /* set dirty for this lists */
                        head_p->dirty = TRUE;
                      }
                    else
                      {
                        do_add_file_dir_item(fd_aci, head_p);
                      }
                  }
                else
                  { /* item is for other list, probably error or NR_LISTS changed */
                    if(lookup_file_dir(fd_aci.id, device_p))
                      { /* item id exists in other list -> complain and skip */
                        printk(KERN_WARNING
                               "read_fd_lists(): aci for id %lu on list %i, device %02u:%02u exists on correct list %i -> ignored - run rsbac_check!\n",
                               fd_aci.id, i, MAJOR(kdev), MINOR(kdev), fd_hash(fd_aci.id));
                      }
                    else
                      { /* wrong list -> move (only, if enabled) and tell */
                        if(rsbac_change_nr)
                          {
                            add_file_dir_item(fd_aci, device_p);
                            /* set dirty for other list */
                            device_p->head_table[fd_hash(fd_aci.id)].dirty = TRUE;
                          }
                        else
                          {
                            printk(KERN_WARNING
                                   "read_fd_lists(): aci for id %lu on list %i, device %02u:%02u is on wrong list, should be %i -> ignored, because rsbac_change_nr is not set - error, run rsbac_check!\n",
                                   fd_aci.id, i, MAJOR(kdev), MINOR(kdev), fd_hash(fd_aci.id));
                          }
                        /*  -> mark for complaining */
                        wrong_list = TRUE;
                        /* set dirty for this lists */
                        head_p->dirty = TRUE;
                      }
	          }
                read_count++;
/*                if (rsbac_debug_ds) printk(KERN_DEBUG "read_fd_lists(): read item %lu\n",
                     fd_aci.id); */
	      }
	  }
	while (tmperr > 0); /* end of do */

	if (tmperr < 0)
	  {
            printk(KERN_WARNING "read_fd_lists(): read error %i from file %i on device %02u:%02u!\n",
                    tmperr, i, MAJOR(kdev), MINOR(kdev));
            err |= -RSBAC_EREADFAILED;
          }

end_read:
        /* free access */
        rsbac_write_unlock(&head_p->lock, &flags);

        /* Set current user space back to user space, because read() writes */
        /* to user space */
        set_fs(oldfs);

        read_sum += read_count;

        /* We do not need this file any more */
        rsbac_read_close(&file);

        /* at least one item was moved to other list -> complain now */
        if(wrong_list)
          {
            if(rsbac_change_nr)
              {
                printk(KERN_WARNING
                       "read_fd_lists(): aci was moved from list %i, device %02u:%02u, to other list - rsbac_change_nr was set!\n",
                       i, MAJOR(kdev), MINOR(kdev));
              }
            else
              {
                printk(KERN_WARNING
                       "read_fd_lists(): ignored other list's aci on list %i, device %02u:%02u - rsbac_change_nr was not set!\n",
                       i, MAJOR(kdev), MINOR(kdev));
              }
          }
      } /* end of 'for all files' */

    if (rsbac_debug_ds)
      printk(KERN_DEBUG
             "read_fd_lists(): total of %lu entries read.\n",
             read_sum);

    /* check for rsbac dir aci */
    if (!lookup_file_dir(dir_dentry_p->d_inode->i_ino, device_p) )
      { /* rsbac dir is unprotected -> */
        /* set attributes for dir to default_rsbac_dir_aci (rsbac_internal etc.) */
        fd_aci = default_rsbac_dir_aci;
        fd_aci.id = dir_dentry_p->d_inode->i_ino;
        add_file_dir_item(fd_aci, device_p);
        printk(KERN_WARNING
               "read_fd_lists(): aci entry for rsbac dir on device %02u:%02u was missing, now added.\n",
               MAJOR(kdev), MINOR(kdev));
        /* mark list as dirty */
        device_p->head_table[fd_hash(fd_aci.id)].dirty = TRUE;
      }
    /*Now that all the files are read, we do not need the dir dentry any more*/
    dput(dir_dentry_p);

    kfree(name);
    return(err);
  };

#ifndef CONFIG_RSBAC_NO_WRITE
/* partial_write_fd_list() */
/* write file and dir aci for one sublist (specified by head_p) */

static int partial_write_fd_list(       int                          head_nr,
                                 struct rsbac_file_dir_list_head_t * head_p,
                                 struct rsbac_device_list_item_t   * device_p)
  {
    struct file     file;
    char          * name;
    char            number[10];
    char          * buffer = NULL;
    u_long          write_len, written, bytes;
    u_long          buflen;
    u_int           tmp_count,part_count = 0,part_div;
    int             err = 0;
    int             tmperr;
    u_int           aci_version = RSBAC_FD_ACI_VERSION;
    struct rsbac_file_dir_list_item_t * current_p;
    mm_segment_t    oldfs;
    u_long          flags;
    struct rsbac_file_dir_list_head_t   i_head;
    struct rsbac_file_dir_list_item_t * fd_item_p;
    struct rsbac_file_dir_list_item_t * fd_item_p2;

/* for DEBUG only: */
/*    return(0); */
    if (rsbac_debug_write)
      printk(KERN_DEBUG "partial_write_fd_list(): start for list %i!\n", head_nr);
    if(!head_p || !device_p || (head_nr < 0) || (head_nr >= RSBAC_NR_FD_LISTS))
      return(-RSBAC_EINVALIDPOINTER);

    name = kmalloc(RSBAC_MAXNAMELEN, GFP_KERNEL);
    if(!name)
      return -RSBAC_ENOMEM;

    /* prepare internal fd-list */
    i_head.head = NULL;
    i_head.tail = NULL;
    i_head.curr = NULL;
    i_head.count = 0;
    /* protect external list */
    rsbac_read_lock(&head_p->lock, &flags);
    current_p = head_p->head;
    while (current_p) 
      {
        if(!do_add_file_dir_item(current_p->aci,&i_head))
          {
            /* unprotect this list */
            rsbac_read_unlock(&head_p->lock, &flags);
            err = -RSBAC_ENOMEM;
            goto out_free;
          }
        current_p = current_p->next;
      }
    /* unprotect this list */
    rsbac_read_unlock(&head_p->lock, &flags);

    /* allocate mem */
    part_div = 1;
    while(!buffer)
      {
        part_div++;
        if(part_div > head_p->count)
          {
            err = -RSBAC_ENOMEM;
            goto out_free;
          }
        part_count = (head_p->count + part_div -1) / part_div;
        buflen = part_count * sizeof(*head_p->head);
        if(buflen < RSBAC_MIN_WRITE_FD_BUF_LEN)
          {
            printk(KERN_WARNING
                   "partial_write_fd_list(): available mem chunks too small, deferring write\n");
            err = -RSBAC_ENOMEM;
            goto out_free;
          }
        if(rsbac_debug_write)
          printk(KERN_DEBUG "partial_write_fd_list(): trying to allocate %lu bytes\n",
                 buflen);
        /* try to kmalloc, but only, if < max. kmalloc-cache_size */
        if(buflen < (1 << 17))
          {
            buffer = (char *) kmalloc(buflen, GFP_KERNEL);
            if(!buffer)
              printk(KERN_INFO
                     "partial_write_fd_list(): allocation of %lu bytes failed!\n",
                     buflen);
          }
      }

    /* construct name from base name + head number */
    strcpy(name, RSBAC_ACI_FD_NAME);
    strcat(name, inttostr(number,head_nr) );

    /* get rsbac write-to-disk semaphore */
    down(&rsbac_write_sem);

    /* open file */
    if ((err = rsbac_write_open(name,
                                &file,
                                device_p->id) ))
      {
        goto out_sem_free;
      }

    /* OK, now we can start filling and writing the buffer */
/*    if (rsbac_debug_write)
        printk(KERN_DEBUG "write_fd_list(): start writing!\n"); */

    /* Set current user space to kernel space, because write() reads    */
    /* from user space */
    oldfs = get_fs();
    set_fs(KERNEL_DS);

    tmperr = file.f_op->write(&file,
                              (char *) &aci_version,
                              sizeof(aci_version),
                              &file.f_pos);
/*        if (rsbac_debug_write)
            printk(KERN_DEBUG "partial_write_fd_list(): wrote version\n"); */
    if (tmperr < sizeof(aci_version))
      {
        printk(KERN_WARNING
               "partial_write_fd_list(): write error %i on file!\n",
               tmperr);
        err = -RSBAC_EWRITEFAILED;
        goto end_write;
      }

    current_p = i_head.head;
    while(current_p)
      {
        /* fill buffer */
        tmp_count=0;
        write_len=0;
        while (current_p && (tmp_count < part_count)) 
          {
             memcpy(buffer+write_len,
                    (char *) &(current_p->aci),
	            sizeof(current_p->aci));
             current_p = current_p->next;
             write_len += sizeof(current_p->aci);
             tmp_count++;
          }
        /* write buffer */
        written = 0;
        tmperr = 0;
        while ((written < write_len) && (tmperr >= 0)) 
          {
            bytes = min(write_len - written, RSBAC_MAX_WRITE_CHUNK);
            tmperr = file.f_op->write(&file,
                                      buffer+written,
	                              bytes,
	                              &file.f_pos);
/*          if (rsbac_debug_write)
              printk(KERN_DEBUG "write_fd_list(): wrote item %lu\n",
                     current_p->aci.id); */
            if(tmperr > 0)
              {
                written += tmperr;
              }
          }
        if (tmperr < 0)
          {
            printk(KERN_WARNING
                   "write_fd_lists(): write error %i on file!\n",
                   tmperr);
            err = -RSBAC_EWRITEFAILED;
            goto end_write;
          }
      }

end_write:
    /* Set current user space back to user space, because write() reads */
    /* from user space */
    set_fs(oldfs);

    if (rsbac_debug_write)
      printk(KERN_DEBUG "write_fd_list(): File %i on device %02u:%02u: %lu entries written.\n",
             head_nr, MAJOR(device_p->id), MINOR(device_p->id), i_head.count);

/* out_close: */
    /* End of write access                     */
    rsbac_write_close(&file);

out_sem_free:
    /* free general write access sem */
    up(&rsbac_write_sem);

/* out_buf_free: */
    /* free buffer */
    kfree(buffer);

out_free:
    /* free internal list */
    fd_item_p = i_head.head;
    while (fd_item_p)
      {
        fd_item_p2 = fd_item_p->next;
        kfree(fd_item_p);
        fd_item_p = fd_item_p2;
      }
    kfree(name);
    return(err);
  };


/* write_fd_list() */
/* write file and dir aci for one sublist (specified by head_p) */
/* This function now handles locking, remove from caller function!!! */

static int write_fd_list(       int                          head_nr,
                         struct rsbac_file_dir_list_head_t * head_p,
                         struct rsbac_device_list_item_t   * device_p)
  {
    struct file     file;
    char          * name;
    char            number[10];
    u_long          write_count, written = 0, bytes;
    int             err = 0;
    int             tmperr = 0;
    struct rsbac_file_dir_list_item_t * current_p;
    mm_segment_t    oldfs;
    u_long          flags;
    u_long          buflen;
    char          * buffer = NULL;
    boolean         vmalloc_used = FALSE;
    u_int           aci_version = RSBAC_FD_ACI_VERSION;

/* for DEBUG only:
 *    return(0);
 */
    if(!head_p || !device_p || (head_nr < 0) || (head_nr >= RSBAC_NR_FD_LISTS))
      return(-RSBAC_EINVALIDPOINTER);
    if(device_p->no_write || !writable(get_super(device_p->id)))
      return(-RSBAC_ENOTWRITABLE);
    name = kmalloc(RSBAC_MAXNAMELEN, GFP_KERNEL);
    if(!name)
      return -RSBAC_ENOMEM;

    if (rsbac_debug_write)
      printk(KERN_DEBUG "write_fd_list(): start for list %i!\n", head_nr);

    /* protect this list */
    rsbac_read_lock(&head_p->lock, &flags);
    /* allocate mem */
    buflen = head_p->count * sizeof(*head_p->head) + sizeof(aci_version);
    /* try to kmalloc, but only, if < max. kmalloc-cache_size */
    if(buflen < RSBAC_MAX_KMALLOC)
      buffer = (char *) kmalloc(buflen, GFP_KERNEL);
    /* If kmalloc failed, use vmalloc instead */
    if(!buffer)
      {
        buffer = (char *) vmalloc(buflen);
        vmalloc_used = TRUE;
        if(rsbac_debug_write)
          printk(KERN_DEBUG
                 "write_fd_list(): vmalloc used for list %i on device %02u:%02u!\n",
                 head_nr, MAJOR(device_p->id), MINOR(device_p->id));
      }
    if(!buffer)
      {
        /* unprotect this list */
        rsbac_read_unlock(&head_p->lock, &flags);
        /* got no mem: fall back to partial_write_fd_list */
        printk(KERN_WARNING
               "write_fd_list(): list %i on device %02u:%02u too large (%lu bytes), calling partial_write_fd_list()!\n",
               head_nr, MAJOR(device_p->id), MINOR(device_p->id), buflen);
        err = partial_write_fd_list(head_nr,head_p,device_p);
        kfree(name);
        return(err);
      }
    /* copy version */
    memcpy(buffer,
           (char *) &aci_version,
           sizeof(aci_version));
    write_count = sizeof(aci_version);
    /* copy list */
    current_p = head_p->head;
    while (current_p) 
      {
        memcpy(buffer+write_count,
               (char *) &(current_p->aci),
	       sizeof(current_p->aci));
        current_p = current_p->next;
        write_count += sizeof(current_p->aci);
      }
    /* unprotect this list */
    rsbac_read_unlock(&head_p->lock, &flags);

    /* construct name from base name + head number */
    strcpy(name, RSBAC_ACI_FD_NAME);
    strcat(name, inttostr(number,head_nr) );

    /* get rsbac write-to-disk semaphore */
    down(&rsbac_write_sem);

    /* open file */
    if ((err = rsbac_write_open(name,
                                &file,
                                device_p->id) ))
      {
        up(&rsbac_write_sem);
        /* free buffer */
        if(vmalloc_used)
          vfree(buffer);
        else
          kfree(buffer);
        kfree(name);
        return(err);
      }

    /* OK, now we can start writing the buffer */
/*    if (rsbac_debug_write)
        printk(KERN_DEBUG "write_fd_list(): start writing!\n"); */

    /* Set current user space to kernel space, because write() reads    */
    /* from user space */
    oldfs = get_fs();
    set_fs(KERNEL_DS);

    while ((written < write_count) && (tmperr >= 0)) 
      {
        bytes = min(write_count - written, RSBAC_MAX_WRITE_CHUNK);
        tmperr = file.f_op->write(&file,
                                  buffer+written,
	                          bytes,
	                          &file.f_pos);
/*        if (rsbac_debug_write)
            printk(KERN_DEBUG "write_fd_list(): wrote item %lu\n",
                   current_p->aci.id); */
        if(tmperr > 0)
          {
            written += tmperr;
          }
      }
    if (tmperr < 0)
      {
        printk(KERN_WARNING
               "write_fd_list(): write error %i on file!\n",
               tmperr);
        err |= -RSBAC_EWRITEFAILED;
      }

    /* Set current user space back to user space, because write() reads */
    /* from user space */
    set_fs(oldfs);

    /* End of write access */
    rsbac_write_close(&file);
    /* free overall sem */
    up(&rsbac_write_sem);
    if (rsbac_debug_write)
      printk(KERN_DEBUG "write_fd_list(): File %i on device %02u:%02u: %lu entries written.\n",
             head_nr, MAJOR(device_p->id), MINOR(device_p->id), (write_count - sizeof(aci_version)) / sizeof(current_p->aci));
    /* free buffer */
    if(vmalloc_used)
      vfree(buffer);
    else
      kfree(buffer);
    kfree(name);
    return(err);
  };
#endif /* ifndef CONFIG_RSBAC_NO_WRITE */

/************************************************************************ */

  /* add_dev_item() is called in read_dev_list(), but defined later,    */
  /* so we declare it here.                                               */
  
  static struct rsbac_dev_list_item_t* 
         add_dev_item(struct rsbac_dev_aci_t);

/* read_dev_list() */
/* reading the system wide dev list from root filesystem */

static int read_dev_list(void)
  {
    struct file                     file;
    int                             err = 0;
    int                             tmperr;
    u_int                           aci_version;
    u_long                          read_count = 0;
    struct rsbac_dev_aci_t          dev_aci;
    struct rsbac_dev_old_aci_t      old_dev_aci;
    struct rsbac_dev_old_old_aci_t  old_old_dev_aci;
    mm_segment_t    oldfs;

        /* open file */
        if ((err = rsbac_read_open(RSBAC_ACI_DEV_NAME,
                                   &file,
                                   ROOT_DEV) ))
          return(err);

        /* OK, now we can start reading */
        /* There is a read function for this file, so read as many ACIs */
        /* as possible. A positive return value means a read success,   */
        /* 0 end of file and a negative value an error.                 */

        /* Set current user space to kernel space, because read() writes */
        /* to user space */
        oldfs = get_fs();
        set_fs(KERNEL_DS);

        tmperr = file.f_op->read(&file,
                                 (char *) &aci_version,
                                 sizeof(aci_version),
                                 &file.f_pos);
        /* error? */
	if (tmperr < sizeof(aci_version))
	  {
            printk(KERN_WARNING
                   "read_dev_lists(): read error from file!\n");
            err = -RSBAC_EREADFAILED;
            goto end_read;
          }
        /* if wrong aci version, set no_write and skip */
        switch(aci_version)
          {
            case RSBAC_DEV_ACI_VERSION:
                break;
            case RSBAC_DEV_OLD_ACI_VERSION:
                printk(KERN_WARNING
                       "read_dev_list(): upconverting old aci list!\n");
                if(rsbac_debug_ds)
                  printk(KERN_DEBUG
                         "read_dev_list(): old aci version %u, current %u!\n",
                         aci_version, RSBAC_DEV_ACI_VERSION);
                /* set dirty to force saving (if non-sync) */
                dev_list_head.dirty = TRUE;
                break;
            case RSBAC_DEV_OLD_OLD_ACI_VERSION:
                printk(KERN_WARNING
                       "read_dev_list(): upconverting old old aci list!\n");
                if(rsbac_debug_ds)
                  printk(KERN_DEBUG
                         "read_dev_list(): old aci version %u, current %u!\n",
                         aci_version, RSBAC_DEV_ACI_VERSION);
                /* set dirty to force saving (if non-sync) */
                dev_list_head.dirty = TRUE;
                break;
            default:
                printk(KERN_WARNING
                       "read_dev_list(): wrong aci version %u, expected %u - skipping file and setting no_write for DEV list!\n",
                       aci_version, RSBAC_DEV_ACI_VERSION);
                dev_list_head.no_write = TRUE;
                err = -RSBAC_EREADFAILED;
                goto end_read;
          }

	do
	  {
	    switch(aci_version)
	      {
                case RSBAC_DEV_ACI_VERSION:
                    tmperr = file.f_op->read(&file,
	                                     (char *) &dev_aci,
	                                     sizeof(dev_aci),
	                                     &file.f_pos);
                    break;
                case RSBAC_DEV_OLD_ACI_VERSION:
                    tmperr = file.f_op->read(&file,
	                                     (char *) &old_dev_aci,
	                                     sizeof(old_dev_aci),
	                                     &file.f_pos);
                    break;
                case RSBAC_DEV_OLD_OLD_ACI_VERSION:
                    tmperr = file.f_op->read(&file,
	                                     (char *) &old_old_dev_aci,
	                                     sizeof(old_old_dev_aci),
	                                     &file.f_pos);
                    break;
                default:
                    err |= -RSBAC_EREADFAILED;
                    goto end_read;
              }
            /* if successful, add item without writing to disk */
	    if (tmperr > 0)
	      {
                switch(aci_version)
	          {
                    case RSBAC_DEV_OLD_ACI_VERSION:
                        convert_old_dev_aci(&old_dev_aci, &dev_aci);
                        break;
                    case RSBAC_DEV_OLD_OLD_ACI_VERSION:
                        convert_old_old_dev_aci(&old_old_dev_aci, &dev_aci);
                        break;
                    default:
                  }
	        add_dev_item(dev_aci);
	        read_count++;
/*                if (rsbac_debug_ds) printk(KERN_DEBUG "read_dev_list(): read item %i\n",
                     dev_aci.id);
*/
	      }
	  }
	while (tmperr > 0); /* end of do */

	if (tmperr < 0)
	  {
            printk(KERN_WARNING "read_dev_list(): read error from file!\n");
            err = -RSBAC_EREADFAILED;
          }

end_read:
        /* Set current user space back to user space, because read() writes */
        /* to user space */
        set_fs(oldfs);

        if (rsbac_debug_ds)
          printk(KERN_DEBUG "read_dev_list(): %lu entries read.\n",
                 read_count);
        /* We do not need this file any more */
        rsbac_read_close(&file);
        return(err);
  }; /* end of read_dev_list() */

#ifndef CONFIG_RSBAC_NO_WRITE
static int write_dev_list(void)
  {
    struct file                    file;
    int                            err = 0;
    struct rsbac_dev_list_item_t * current_p;
    mm_segment_t    oldfs;
    u_long          write_count, written = 0, bytes;
    int             tmperr = 0;
    u_long          flags;
    char          * buffer = NULL;
    u_long          buflen;
    boolean         vmalloc_used = FALSE;
    u_int           aci_version = RSBAC_DEV_ACI_VERSION;

    /* test no_write */
    if(dev_list_head.no_write)
      return(-RSBAC_ENOTWRITABLE);

    /* protect this list */
    rsbac_read_lock(&dev_list_head.lock, &flags);
    /* allocate mem */
    buflen = dev_list_head.count * sizeof(*dev_list_head.head) + sizeof(aci_version);
    /* try to kmalloc, but only, if < max. kmalloc-cache_size */
    if(buflen < RSBAC_MAX_KMALLOC)
      buffer = (char *) kmalloc(buflen, GFP_KERNEL);
    /* If kmalloc failed, use vmalloc instead */
    if(!buffer)
      {
        buffer = (char *) vmalloc(buflen);
        vmalloc_used = TRUE;
        if(rsbac_debug_write)
          printk(KERN_DEBUG
                 "write_dev_list(): vmalloc used!\n");
      }
    if(!buffer)
      {
        /* unprotect this list */
        rsbac_read_unlock(&dev_list_head.lock, &flags);
        return(-RSBAC_ENOMEM);
      }
    /* copy version */
    memcpy(buffer,
           (char *) &aci_version,
           sizeof(aci_version));
    write_count = sizeof(aci_version);
    /* copy list */
    current_p = dev_list_head.head;
    while (current_p) 
      {
        memcpy(buffer+write_count,
               (char *) &(current_p->aci),
	       sizeof(current_p->aci));
        current_p = current_p->next;
        write_count += sizeof(current_p->aci);
      }
    /* unprotect this list */
    rsbac_read_unlock(&dev_list_head.lock, &flags);
    
    /* get rsbac write-to-disk semaphore */
    down(&rsbac_write_sem);

    /* open file */
    if ((err = rsbac_write_open(RSBAC_ACI_DEV_NAME,
                                &file,
                                ROOT_DEV) ))
      {
        up(&rsbac_write_sem);
        /* free buffer */
        if(vmalloc_used)
          vfree(buffer);
        else
          kfree(buffer);
        return(err);
      }

    /* OK, now we can start writing all dev items. */
    /* Set current user space to kernel space, because write() reads    */
    /* from user space */
    oldfs = get_fs();
    set_fs(KERNEL_DS);

    while ((written < write_count) && (tmperr >= 0)) 
      {
        bytes = min(write_count - written, RSBAC_MAX_WRITE_CHUNK);
        tmperr = file.f_op->write(&file,
                                  buffer+written,
	                          bytes,
	                          &file.f_pos);
/*        if (rsbac_debug_write)
            printk(KERN_DEBUG "write_dev_list(): wrote item %lu\n",
                   current_p->aci.id); */
        if(tmperr > 0)
          {
            written += tmperr;
          }
      }
    if (tmperr < 0)
      {
        printk(KERN_WARNING
               "write_dev_list(): write error %i on file!\n",
               tmperr);
        err |= -RSBAC_EWRITEFAILED;
      }

    /* Set current user space back to user space, because write() reads */
    /* from user space */
    set_fs(oldfs);

    if (rsbac_debug_write)
      printk(KERN_DEBUG "write_dev_list(): %lu entries written.\n",
             (write_count - sizeof(aci_version)) / sizeof(current_p->aci));
    /* End of write access */
    rsbac_write_close(&file);
    /* free overall sem */
    up(&rsbac_write_sem);
    /* free buffer */
    if(vmalloc_used)
      vfree(buffer);
    else
      kfree(buffer);
    return(err);
  }; /* end of write_dev_list() */
#endif /* ifndef CONFIG_RSBAC_NO_WRITE */

/************************************************************************ */

  /* add_user_item() is called in read_user_list(), but defined later,    */
  /* so we declare it here.                                               */
  
  static struct rsbac_user_list_item_t* 
         add_user_item(struct rsbac_user_aci_t);

/* read_user_list() */
/* reading the system wide user list from root filesystem */

static int read_user_list(void)
  {
    struct file                     file;
    int                             err = 0;
    int                             tmperr;
    u_int                           aci_version;
    u_long                          read_count = 0;
    struct rsbac_user_aci_t         user_aci;
    struct rsbac_user_old_aci_t     old_user_aci;
    struct rsbac_user_old_old_aci_t old_old_user_aci;
    mm_segment_t    oldfs;

        /* open file */
        if ((err = rsbac_read_open(RSBAC_ACI_USER_NAME,
                                   &file,
                                   ROOT_DEV) ))
          return(err);

        /* OK, now we can start reading */
        /* There is a read function for this file, so read as many ACIs */
        /* as possible. A positive return value means a read success,   */
        /* 0 end of file and a negative value an error.                 */

        /* Set current user space to kernel space, because read() writes */
        /* to user space */
        oldfs = get_fs();
        set_fs(KERNEL_DS);

        tmperr = file.f_op->read(&file,
                                 (char *) &aci_version,
                                 sizeof(aci_version),
                                 &file.f_pos);
        /* error? */
	if (tmperr < sizeof(aci_version))
	  {
            printk(KERN_WARNING
                   "read_user_lists(): read error from file!\n");
            err = -RSBAC_EREADFAILED;
            goto end_read;
          }
        /* if wrong aci version, set no_write and skip */
        switch(aci_version)
          {
            case RSBAC_USER_ACI_VERSION:
                break;
            case RSBAC_USER_OLD_ACI_VERSION:
                printk(KERN_WARNING
                       "read_user_list(): upconverting old aci list!\n");
                if(rsbac_debug_ds)
                  printk(KERN_DEBUG
                         "read_user_list(): old aci version %u, current %u!\n",
                         aci_version, RSBAC_USER_ACI_VERSION);
                /* set dirty to force saving (if non-sync) */
                user_list_head.dirty = TRUE;
                break;
            case RSBAC_USER_OLD_OLD_ACI_VERSION:
                printk(KERN_WARNING
                       "read_user_list(): upconverting old aci list!\n");
                if(rsbac_debug_ds)
                  printk(KERN_DEBUG
                         "read_user_list(): old aci version %u, current %u!\n",
                         aci_version, RSBAC_USER_ACI_VERSION);
                /* set dirty to force saving (if non-sync) */
                user_list_head.dirty = TRUE;
                break;
            default:
                printk(KERN_WARNING
                       "read_user_list(): wrong aci version %u, expected %u - skipping file and setting no_write for USER list!\n",
                       aci_version, RSBAC_USER_ACI_VERSION);
                user_list_head.no_write = TRUE;
                err = -RSBAC_EREADFAILED;
                goto end_read;
          }

	do
	  {
	    switch(aci_version)
	      {
                case RSBAC_USER_ACI_VERSION:
                    tmperr = file.f_op->read(&file,
	                                     (char *) &user_aci,
	                                     sizeof(user_aci),
	                                     &file.f_pos);
                    break;
                case RSBAC_USER_OLD_ACI_VERSION:
                    tmperr = file.f_op->read(&file,
	                                     (char *) &old_user_aci,
	                                     sizeof(old_user_aci),
	                                     &file.f_pos);
                    break;
                case RSBAC_USER_OLD_OLD_ACI_VERSION:
                    tmperr = file.f_op->read(&file,
	                                     (char *) &old_old_user_aci,
	                                     sizeof(old_old_user_aci),
	                                     &file.f_pos);
                    break;
                default:
                    err |= -RSBAC_EREADFAILED;
                    goto end_read;
              }
            /* if successful, add item without writing to disk */
	    if (tmperr > 0)
	      {
                switch(aci_version)
	          {
                    case RSBAC_USER_OLD_ACI_VERSION:
                        convert_old_user_aci(&old_user_aci, &user_aci);
                        break;
                    case RSBAC_USER_OLD_OLD_ACI_VERSION:
                        convert_old_old_user_aci(&old_old_user_aci, &user_aci);
                        break;
                    default:
                  }
	        add_user_item(user_aci);
	        read_count++;
/*                if (rsbac_debug_ds) printk(KERN_DEBUG "read_user_list(): read item %i\n",
                     user_aci.id);
*/
	      }
	  }
	while (tmperr > 0); /* end of do */

	if (tmperr < 0)
	  {
            printk(KERN_WARNING "read_user_list(): read error from file!\n");
            err = -RSBAC_EREADFAILED;
          }

end_read:
        /* Set current user space back to user space, because read() writes */
        /* to user space */
        set_fs(oldfs);

        if (rsbac_debug_ds)
          printk(KERN_DEBUG "read_user_list(): %lu entries read.\n",
                 read_count);
        /* We do not need this file any more */
        rsbac_read_close(&file);
        return(err);
  }; /* end of read_user_list() */

#ifndef CONFIG_RSBAC_NO_WRITE
static int write_user_list(void)
  {
    struct file                     file;
    int                             err = 0;
    struct rsbac_user_list_item_t * current_p;
    mm_segment_t    oldfs;
    u_long          write_count, written = 0, bytes;
    int             tmperr = 0;
    u_long          flags;
    char          * buffer = NULL;
    u_long          buflen;
    boolean         vmalloc_used = FALSE;
    u_int           aci_version = RSBAC_USER_ACI_VERSION;

    /* test no_write */
    if(user_list_head.no_write)
      return(-RSBAC_ENOTWRITABLE);

    /* protect this list */
    rsbac_read_lock(&user_list_head.lock, &flags);
    /* allocate mem */
    buflen = user_list_head.count * sizeof(*user_list_head.head) + sizeof(aci_version);
    /* try to kmalloc, but only, if < max. kmalloc-cache_size */
    if(buflen < RSBAC_MAX_KMALLOC)
      buffer = (char *) kmalloc(buflen, GFP_KERNEL);
    /* If kmalloc failed, use vmalloc instead */
    if(!buffer)
      {
        buffer = (char *) vmalloc(buflen);
        vmalloc_used = TRUE;
        if(rsbac_debug_write)
          printk(KERN_DEBUG
                 "write_user_list(): vmalloc used!\n");
      }
    if(!buffer)
      {
        /* unprotect this list */
        rsbac_read_unlock(&user_list_head.lock, &flags);
        return(-RSBAC_ENOMEM);
      }
    /* copy version */
    memcpy(buffer,
           (char *) &aci_version,
           sizeof(aci_version));
    write_count = sizeof(aci_version);
    /* copy list */
    current_p = user_list_head.head;
    while (current_p) 
      {
        memcpy(buffer+write_count,
               (char *) &(current_p->aci),
	       sizeof(current_p->aci));
        current_p = current_p->next;
        write_count += sizeof(current_p->aci);
      }
    /* unprotect this list */
    rsbac_read_unlock(&user_list_head.lock, &flags);

    /* get rsbac write-to-disk semaphore */
    down(&rsbac_write_sem);

    /* open file */
    if ((err = rsbac_write_open(RSBAC_ACI_USER_NAME,
                                &file,
                                ROOT_DEV) ))
      {
        up(&rsbac_write_sem);
        /* free buffer */
        if(vmalloc_used)
          vfree(buffer);
        else
          kfree(buffer);
        return(err);
      }

    /* OK, now we can start writing the buffer. */
    /* Set current user space to kernel space, because write() reads    */
    /* from user space */
    oldfs = get_fs();
    set_fs(KERNEL_DS);

    while ((written < write_count) && (tmperr >= 0)) 
      {
        bytes = min(write_count - written, RSBAC_MAX_WRITE_CHUNK);
        tmperr = file.f_op->write(&file,
                                  buffer+written,
	                          bytes,
	                          &file.f_pos);
/*        if (rsbac_debug_write)
            printk(KERN_DEBUG "write_user_list(): wrote item %lu\n",
                   current_p->aci.id); */
        if(tmperr > 0)
          {
            written += tmperr;
          }
      }
    if (tmperr < 0)
      {
        printk(KERN_WARNING
               "write_user_list(): write error %i on file!\n",
               tmperr);
        err |= -RSBAC_EWRITEFAILED;
      }

    /* Set current user space back to user space, because write() reads */
    /* from user space */
    set_fs(oldfs);

    if (rsbac_debug_write)
      printk(KERN_DEBUG "write_user_list(): %lu entries written.\n",
             write_count / sizeof(current_p->aci));
    /* End of write access                     */
    rsbac_write_close(&file);
    /* free overall sem */
    up(&rsbac_write_sem);
    /* free buffer */
    if(vmalloc_used)
      vfree(buffer);
    else
      kfree(buffer);
    return(err);
  }; /* end of write_user_list() */
#endif /* ifndef CONFIG_RSBAC_NO_WRITE */

/************************************************************************** */
/* The lookup functions return NULL, if the item is not found, and a        */
/* pointer to the item otherwise.                                           */

/* The do_lookup() function is used as an abstraction from the hash         */
/* management.*/

/* First, a lookup for the device list item                                 */

static struct rsbac_device_list_item_t * lookup_device(kdev_t kdev)
    {
      struct rsbac_device_list_item_t  * curr = device_list_head.curr;
      
      /* if there is no current item or it is not the right one, search... */
      if(! (curr && (curr->id == kdev) ) )
        {
          curr = device_list_head.head;
          while (curr && (curr->id != kdev))
            {
              curr = curr->next;
            }
          if (curr)
            device_list_head.curr=curr;
          else
            printk(KERN_WARNING "lookup_device(): Invalid device %02u:%02u\n",
                   MAJOR(kdev),MINOR(kdev));
        }
      /* it is the current item -> return it */
        return (curr);
    };

/* do_lookup_file_dir() */
/* lookup file/dir in single sublist */

static struct rsbac_file_dir_list_item_t 
          * do_lookup_file_dir(u_long id,
                               struct rsbac_file_dir_list_head_t * head)
    {
      struct rsbac_file_dir_list_item_t  * curr;
      
      if(!head)
        return(NULL);
      curr = head->curr;
      /* is the current item the one we look for? yes -> return, else search */
      if (curr && (curr->aci.id == id))
        return (curr);
      else
        curr = head->head;
        while (curr && (curr->aci.id != id))
          {
            curr = curr->next;
          }
        if (curr)
          head->curr=curr;
        return (curr);
    };

/* lookup_file_dir() */
/* lookup file/dir in single device */
static struct rsbac_file_dir_list_item_t *
         lookup_file_dir(u_long id,
                         struct rsbac_device_list_item_t * device_p)
    {
      if (!device_p)
        return(NULL);
      return(do_lookup_file_dir(id,
                &(device_p->head_table[fd_hash(id)]) ) );
    };

/* lookup_dev() */
/* lookup dev item */
static struct rsbac_dev_list_item_t  * lookup_dev(struct rsbac_dev_t dev)
    {
      struct rsbac_dev_list_item_t  * curr = dev_list_head.curr;
      
      /* is the current item the one we look for? yes -> return, else search */
      if (curr && (curr->aci.id.type == dev.type)
          && (curr->aci.id.id == dev.id) )
        return (curr);
      else
        curr = dev_list_head.head;
        while (curr && ((curr->aci.id.type != dev.type)
                        || (curr->aci.id.id != dev.id) ) )
          curr = curr->next;

        if (curr)
          dev_list_head.curr=curr;
        return (curr);
    };

/* lookup_ipc() */
/* lookup ipc item */
static struct rsbac_ipc_list_item_t  * lookup_ipc(struct rsbac_ipc_t ipc)
    {
      struct rsbac_ipc_list_item_t  * curr = ipc_list_head.curr;
      
      /* is the current item the one we look for? yes -> return, else search */
      if (   curr
          && (curr->aci.id.type == ipc.type)
          && (   (   (ipc.type != I_sock)
                  && (curr->aci.id.id.id_nr == ipc.id.id_nr)
                 )
              || (   (ipc.type == I_sock)
                  && (curr->aci.id.id.sock == ipc.id.sock)
                 )
             )
         )
        return (curr);
      else
        curr = ipc_list_head.head;
        while (   curr
               && (   (curr->aci.id.type != ipc.type)
                   || (   (ipc.type != I_sock)
                       && (curr->aci.id.id.id_nr != ipc.id.id_nr)
                      )
                   || (   (ipc.type == I_sock)
                       && (curr->aci.id.id.sock != ipc.id.sock)
                      )
                  )
              )
          curr = curr->next;

        if (curr)
          ipc_list_head.curr=curr;
        return (curr);
    };

static struct rsbac_user_list_item_t     * lookup_user(rsbac_uid_t uid)
    {
      struct rsbac_user_list_item_t  * curr = user_list_head.curr;
      
      /* is the current item the one we look for? yes -> return, else search */
      if (curr && (curr->aci.id == uid))
        return (curr);
      else
        curr = user_list_head.head;
        while (curr && (curr->aci.id != uid))
          curr = curr->next;
        if (curr)
          user_list_head.curr=curr;
        return (curr);
    };

static struct rsbac_process_list_item_t  * lookup_process(rsbac_pid_t pid)
    {
      struct rsbac_process_list_item_t  * curr = process_list_head.curr;
      
      /* is the current item the one we look for? yes -> return, else search */
      if (curr && (curr->aci.id == pid))
        return (curr);
      else
        curr = process_list_head.head;
        while (curr && (curr->aci.id != pid))
          curr = curr->next;
        if (curr)
          process_list_head.curr=curr;
        return (curr);
    };

/************************************************************************** */
/* The add_item() functions add an item to the list, set head.curr to it,   */
/* and return a pointer to the item.                                        */
/* These functions will NOT check, if there is already an item under the    */
/* same ID! If this happens, the lookup functions will return the old item! */
/* All list manipulation must be protected by rw-spinlocks to prevent       */
/* inconsistency and undefined behaviour in other concurrent functions.     */

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
static struct rsbac_device_list_item_t 
          * add_device_item(kdev_t kdev)
#else
static struct rsbac_device_list_item_t 
          * add_device_item(kdev_t kdev, struct dentry * d_covers)
#endif
    {
      struct rsbac_device_list_item_t * new_item_p;
      int i;

      /* allocate memory for new device, return NULL, if failed */
      if ( !(new_item_p = (struct rsbac_device_list_item_t *)
                    kmalloc(sizeof(*new_item_p), GFP_KERNEL) ) )
         return(NULL);
         
      new_item_p->id = kdev;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
      new_item_p->d_covers = d_covers;
#endif
      new_item_p->no_write = FALSE;
      new_item_p->rsbac_dir_dentry_p = NULL;

      /* init file/dir sublists */
      for (i = 0; i < RSBAC_NR_FD_LISTS; i++)
        {
          new_item_p->head_table[i].lock = RW_LOCK_UNLOCKED;
          new_item_p->head_table[i].head = NULL;
          new_item_p->head_table[i].tail = NULL;
          new_item_p->head_table[i].curr = NULL;
          new_item_p->head_table[i].dirty = FALSE;
          new_item_p->head_table[i].count = 0;
        };
        
      /* add new device to device list */
      if (!device_list_head.head)
        { /* first device */
          device_list_head.head=new_item_p;
          device_list_head.tail=new_item_p;
          device_list_head.curr=new_item_p;
          device_list_head.count=1;
          new_item_p->prev=NULL;
          new_item_p->next=NULL;
        }  
      else
        { /* there is another device -> hang to tail */
          new_item_p->prev=device_list_head.tail;
          new_item_p->next=NULL;
          device_list_head.tail->next=new_item_p;
          device_list_head.tail=new_item_p;
          device_list_head.curr=new_item_p;
          device_list_head.count++;
        };
      return(new_item_p);
    };

/* The do_add() function is used as an abstraction from the hash management.*/

/* do_add_file_dir_item() */
/* add item to single sublist */
static struct rsbac_file_dir_list_item_t 
          * do_add_file_dir_item(struct rsbac_file_dir_aci_t aci,
                                 struct rsbac_file_dir_list_head_t * head_p)
    {
      struct rsbac_file_dir_list_item_t * new_item_p = NULL;      

      if (!head_p)
        return(NULL);
/*      if (rsbac_debug_ds) printk(KERN_DEBUG "do_add_file_dir_item(): allocating memory!\n"); */
      /* Try to allocate memory for the new item */
      if ( !(new_item_p = (struct rsbac_file_dir_list_item_t *)
                 kmalloc(sizeof(*new_item_p),GFP_KERNEL)) )
        {
          if (rsbac_debug_ds)
            printk(KERN_DEBUG "do_add_file_dir_item(): could not allocate memory!\n");
          return(NULL);
        }
      (*new_item_p).aci = aci;
      
      if (!head_p->head)
        { /* first item in this sublist */
          head_p->head=new_item_p;
          head_p->tail=new_item_p;
          head_p->curr=new_item_p;
          head_p->count = 1;
          new_item_p->prev=NULL;
          new_item_p->next=NULL;          
        }  
      else
        { /* not first item -> hang to tail */
          new_item_p->prev=head_p->tail;
          new_item_p->next=NULL;
          head_p->tail->next=new_item_p;
          head_p->tail=new_item_p;
          head_p->curr=new_item_p;
          head_p->count++;
        };
      return(new_item_p);
    };

/* add_file_dir_item() */
/* add item to device */
static struct rsbac_file_dir_list_item_t 
          * add_file_dir_item(struct rsbac_file_dir_aci_t aci,
                              struct rsbac_device_list_item_t * device_p)
    {
      struct rsbac_file_dir_list_item_t * item_p = NULL;

      if (!device_p)
        return(NULL);
      item_p = do_add_file_dir_item(aci,
                                    &(device_p->head_table[fd_hash(aci.id)]) );
      /* if add successful, write to disk */
      return (item_p);
    };


/* WARNING: Writing to disk is NOT done in this function, because it would */
/* not work with read_dev_list(). So write_dev_list() must be called by    */
/* the caller of this function afterwards!                                 */

static struct rsbac_dev_list_item_t* 
         add_dev_item(struct rsbac_dev_aci_t aci)
    {
      struct rsbac_dev_list_item_t * new_item_p = NULL;

      if ( !(new_item_p = (struct rsbac_dev_list_item_t *)
                 kmalloc(sizeof(*new_item_p),GFP_KERNEL)) )
        return(NULL);
      (*new_item_p).aci = aci;
      
      if (!dev_list_head.head)
        {
          dev_list_head.head=new_item_p;
          dev_list_head.tail=new_item_p;
          dev_list_head.curr=new_item_p;
          dev_list_head.count = 1;
          new_item_p->prev=NULL;
          new_item_p->next=NULL;          
        }  
      else
        {
          new_item_p->prev=dev_list_head.tail;
          new_item_p->next=NULL;
          dev_list_head.tail->next=new_item_p;
          dev_list_head.tail=new_item_p;
          dev_list_head.curr=new_item_p;
          dev_list_head.count++;
        };
      return(new_item_p);
    };

static struct rsbac_ipc_list_item_t* 
         add_ipc_item(struct rsbac_ipc_aci_t aci)
    {
      struct rsbac_ipc_list_item_t * new_item_p = NULL;

      if ( !(new_item_p = (struct rsbac_ipc_list_item_t *)
                 kmalloc(sizeof(*new_item_p),GFP_KERNEL)) )
        return(NULL);
      new_item_p->aci = aci;
      
      if (!ipc_list_head.head)
        {
          ipc_list_head.head=new_item_p;
          ipc_list_head.tail=new_item_p;
          ipc_list_head.curr=new_item_p;
          ipc_list_head.count = 1;
          new_item_p->prev=NULL;
          new_item_p->next=NULL;          
        }  
      else
        {
          new_item_p->prev=ipc_list_head.tail;
          new_item_p->next=NULL;
          ipc_list_head.tail->next=new_item_p;
          ipc_list_head.tail=new_item_p;
          ipc_list_head.curr=new_item_p;
          ipc_list_head.count++;
        };
      return(new_item_p);
    };

/* WARNING: Writing to disk is NOT done in this function, because it would */
/* not work with read_user_list(). So write_user_list() must be called by  */
/* the caller of this function afterwards!                                 */

static struct rsbac_user_list_item_t* 
         add_user_item(struct rsbac_user_aci_t aci)
    {
      struct rsbac_user_list_item_t * new_item_p = NULL;

      if ( !(new_item_p = (struct rsbac_user_list_item_t *)
                 kmalloc(sizeof(*new_item_p),GFP_KERNEL)) )
        return(NULL);
      new_item_p->aci = aci;
      
      if (!user_list_head.head)
        {
          user_list_head.head=new_item_p;
          user_list_head.tail=new_item_p;
          user_list_head.curr=new_item_p;
          user_list_head.count = 1;
          new_item_p->prev=NULL;
          new_item_p->next=NULL;          
        }  
      else
        {
          new_item_p->prev=user_list_head.tail;
          new_item_p->next=NULL;
          user_list_head.tail->next=new_item_p;
          user_list_head.tail=new_item_p;
          user_list_head.curr=new_item_p;
          user_list_head.count++;
        };
      return(new_item_p);
    };

static struct rsbac_process_list_item_t* 
         add_process_item(struct rsbac_process_aci_t aci)
    {
      struct rsbac_process_list_item_t * new_item_p = NULL;

      if ( !(new_item_p = (struct rsbac_process_list_item_t *)
                 kmalloc(sizeof(*new_item_p),GFP_KERNEL)) )
        return(NULL);
      new_item_p->aci = aci;
      
      if (!process_list_head.head)
        {
          process_list_head.head=new_item_p;
          process_list_head.tail=new_item_p;
          process_list_head.curr=new_item_p;
          process_list_head.count = 1;
          new_item_p->prev=NULL;
          new_item_p->next=NULL;          
        }  
      else
        {
          new_item_p->prev=process_list_head.tail;
          new_item_p->next=NULL;
          process_list_head.tail->next=new_item_p;
          process_list_head.tail=new_item_p;
          process_list_head.curr=new_item_p;
          process_list_head.count++;
        };
      return(new_item_p);
    };

/************************************************************************** */
/* The remove_item() functions remove an item from the list. If this item   */
/* is head, tail or curr, these pointers are set accordingly.               */
/* To speed up removing several subsequent items, curr is set to the next   */
/* item, if possible.                                                       */
/* If the item is not found, nothing is done.                               */

static void remove_device_item(kdev_t kdev)
    {
      struct rsbac_device_list_item_t   * item_p;
      struct rsbac_file_dir_list_item_t * fd_item_p;
      struct rsbac_file_dir_list_item_t * fd_item_p2;
      int i;
    
      /* first we must locate the item. */
      if ( (item_p = lookup_device(kdev)) )
        { /* ok, item was found */
          if (device_list_head.head == item_p)  
             { /* item is head */
               if (device_list_head.tail == item_p)
                 { /* item is head and tail = only item -> list will be empty*/
                   device_list_head.head = NULL;
                   device_list_head.tail = NULL;
                 }
               else
                 { /* item is head, but not tail -> next item becomes head */
                   item_p->next->prev = NULL;
                   device_list_head.head = item_p->next;
                 };
             }
          else
             { /* item is not head */
               if (device_list_head.tail == item_p)
                 { /*item is not head, but tail -> previous item becomes tail*/
                   item_p->prev->next = NULL;
                   device_list_head.tail = item_p->prev;
                 }
               else
                 { /* item is neither head nor tail -> item is cut out */
                   item_p->prev->next = item_p->next;
                   item_p->next->prev = item_p->prev;
                 };
             };
             
          /* curr is no longer valid -> reset.                              */
          device_list_head.curr=NULL;
          /* adjust counter */
          device_list_head.count--;
          
          /* now we can remove the item from memory. This means cleaning up */
          /* everything below. First clean up sublists...                   */
          for (i = 0; i < RSBAC_NR_FD_LISTS; i++)
            {
              fd_item_p = item_p->head_table[i].head;
              while (fd_item_p)
                {
                  fd_item_p2 = fd_item_p->next;
                  kfree(fd_item_p);
                  fd_item_p = fd_item_p2;
                }
            }
          /* dput() rsbac_dir_dentry_p, if set */
          if(item_p->rsbac_dir_dentry_p)
            dput(item_p->rsbac_dir_dentry_p);
          /* OK, lets remove the device item itself */
          kfree(item_p);
        };  /* end of if: item was found */

    }; /* end of remove_device_item() */


static void do_remove_file_dir_item(u_long id,
                                    struct rsbac_file_dir_list_head_t * head_p,
                                    kdev_t device)
    {
      struct rsbac_file_dir_list_item_t * item_p;
    
      /* first we must locate the item. */
      if (head_p && (item_p = do_lookup_file_dir(id, head_p)) )
        { /* ok, item was found */
          if ( ( (*head_p).head == item_p) )
             { /* item is head */
               if ( ( (*head_p).tail == item_p) )
                 { /* item is head and tail = only item -> list will be empty*/
                   head_p->head = NULL;
                   head_p->tail = NULL;
                   head_p->curr = NULL;
                 }
               else
                 { /* item is head, but not tail -> next item becomes head */
                   item_p->next->prev = NULL;
                   head_p->head = item_p->next;
                   head_p->curr = item_p->next;
                 };
             }
          else
             { /* item is not head */
               if ( ( (*head_p).tail == item_p) )
                 { /*item is not head, but tail -> previous item becomes tail*/
                   item_p->prev->next = NULL;
                   head_p->tail = item_p->prev;
                   head_p->curr = NULL;
                 }
               else
                 { /* item is neither head nor tail -> item is cut out */
                   item_p->prev->next = item_p->next;
                   item_p->next->prev = item_p->prev;
                   head_p->curr = item_p->next;
                 };
             };
             
          /* adjust counter */
          head_p->count--;
          /* now we can remove the item from memory */
          kfree(item_p);    
        };  /* end of if: item was found */
    };  /* end of remove_file_dir_item() */


static void remove_file_dir_item(u_long id,
                                 struct rsbac_device_list_item_t * device_p)
    {
      int head_nr = fd_hash(id);
      struct rsbac_file_dir_list_head_t * head_p;
    
      head_p = &(device_p->head_table[head_nr]);
      do_remove_file_dir_item(id, head_p, device_p->id);
    };  /* end of remove_file_dir_item() */
    

static void remove_dev_item(struct rsbac_dev_t dev)
    {
      struct rsbac_dev_list_item_t * item_p;
    
      /* first we must locate the item. */
      if ( (item_p = lookup_dev(dev)) )
        { /* ok, item was found */
          if ( (dev_list_head.head == item_p) )
             { /* item is head */
               if ( (dev_list_head.tail == item_p) )
                 { /* item is head and tail = only item -> list will be empty*/
                   dev_list_head.head = NULL;
                   dev_list_head.tail = NULL;
                 }
               else
                 { /* item is head, but not tail -> next item becomes head */
                   item_p->next->prev = NULL;
                   dev_list_head.head = item_p->next;
                 };
             }
          else
             { /* item is not head */
               if ( (dev_list_head.tail == item_p) )
                 { /*item is not head, but tail -> previous item becomes tail*/
                   item_p->prev->next = NULL;
                   dev_list_head.tail = item_p->prev;
                 }
               else
                 { /* item is neither head nor tail -> item is cut out */
                   item_p->prev->next = item_p->next;
                   item_p->next->prev = item_p->prev;
                 };
             };
             
          /* curr is no longer valid -> reset */
          dev_list_head.curr=NULL;
          /* adjust counter */
          dev_list_head.count--;
          /* now we can remove the item from memory */
          kfree(item_p);    
        };  /* end of if: item was found */
    }; /* end of remove_dev_item() */


static void remove_ipc_item(struct rsbac_ipc_t ipc)
    {
      struct rsbac_ipc_list_item_t * item_p;
    
      /* first we must locate the item. */
      if ( (item_p = lookup_ipc(ipc)) )
        { /* ok, item was found */
          if ( (ipc_list_head.head == item_p) )
             { /* item is head */
               if ( (ipc_list_head.tail == item_p) )
                 { /* item is head and tail = only item -> list will be empty*/
                   ipc_list_head.head = NULL;
                   ipc_list_head.tail = NULL;
                 }
               else
                 { /* item is head, but not tail -> next item becomes head */
                   item_p->next->prev = NULL;
                   ipc_list_head.head = item_p->next;
                 };
             }
          else
             { /* item is not head */
               if ( (ipc_list_head.tail == item_p) )
                 { /*item is not head, but tail -> previous item becomes tail*/
                   item_p->prev->next = NULL;
                   ipc_list_head.tail = item_p->prev;
                 }
               else
                 { /* item is neither head nor tail -> item is cut out */
                   item_p->prev->next = item_p->next;
                   item_p->next->prev = item_p->prev;
                 };
             };
             
          /* curr is no longer valid -> reset */
          ipc_list_head.curr=NULL;
          /* adjust counter */
          ipc_list_head.count--;
          /* now we can remove the item from memory */
          kfree(item_p);    
        };  /* end of if: item was found */

    }; /* end of remove_ipc_item() */


static void remove_user_item(rsbac_uid_t user)
    {
      struct rsbac_user_list_item_t * item_p;
#ifdef CONFIG_RSBAC_PM
      union rsbac_pm_set_id_t pm_set_id;
#endif
    
      /* first we must locate the item. */
      if ( (item_p = lookup_user(user)) )
        { /* ok, item was found */
#ifdef CONFIG_RSBAC_PM
          /* user items can have an attached pm_task_set -> remove first */
          if(item_p->aci.pm_task_set)
            {
              pm_set_id.task_set = item_p->aci.pm_task_set;
              rsbac_pm_remove_set(PS_TASK,pm_set_id);
            }
#endif
          if (user_list_head.head == item_p)
             { /* item is head */
               if (user_list_head.tail == item_p)
                 { /* item is head and tail = only item -> list will be empty*/
                   user_list_head.head = NULL;
                   user_list_head.tail = NULL;
                 }
               else
                 { /* item is head, but not tail -> next item becomes head */
                   item_p->next->prev = NULL;
                   user_list_head.head = item_p->next;
                 };
             }
          else
             { /* item is not head */
               if (user_list_head.tail == item_p)
                 { /*item is not head, but tail -> previous item becomes tail*/
                   item_p->prev->next = NULL;
                   user_list_head.tail = item_p->prev;
                 }
               else
                 { /* item is neither head nor tail -> item is cut out */
                   item_p->prev->next = item_p->next;
                   item_p->next->prev = item_p->prev;
                 };
             };
             
          /* curr is no longer valid -> reset */
          user_list_head.curr=NULL;
          /* adjust counter */
          user_list_head.count--;
          /* now we can remove the item from memory */
          kfree(item_p);    
        };  /* end of if: item was found */

    }; /* end of remove_user_item() */


static void remove_process_item(rsbac_pid_t process)
    {
      struct rsbac_process_list_item_t * item_p;
    
      /* first we must locate the item. */
      if ( (item_p = lookup_process(process)) )
        { /* ok, item was found */
          if (process_list_head.head == item_p)
             { /* item is head */
               if (process_list_head.tail == item_p)
                 { /* item is head and tail = only item -> list will be empty*/
                   process_list_head.head = NULL;
                   process_list_head.tail = NULL;
                 }
               else
                 { /* item is head, but not tail -> next item becomes head */
                   item_p->next->prev = NULL;
                   process_list_head.head = item_p->next;
                 };
             }
          else
             { /* item is not head */
               if (process_list_head.tail == item_p)
                 { /*item is not head, but tail -> previous item becomes tail*/
                   item_p->prev->next = NULL;
                   process_list_head.tail = item_p->prev;
                 }
               else
                 { /* item is neither head nor tail -> item is cut out */
                   item_p->prev->next = item_p->next;
                   item_p->next->prev = item_p->prev;
                 };
             };
             
          /* curr is no longer valid -> reset */
          process_list_head.curr=NULL;
          /* adjust counter */
          process_list_head.count--;
          /* now we can remove the item from memory */
          kfree(item_p);    
        };  /* end of if: item was found */

    }; /* end of remove_process_item() */

/****************************************************************/
/* The convert_aci() functions convert an old aci to a new one. */

void convert_old_file_dir_aci(struct rsbac_file_dir_old_aci_t * old_aci,
                              struct rsbac_file_dir_aci_t * new_aci)
  {
    new_aci->id = old_aci->id;
    switch(old_aci->sec_level)
      {
        case SL_old_rsbac_internal:
          new_aci->sec_level = SL_rsbac_internal;
          break;
        case SL_old_inherit:
          new_aci->sec_level = SL_inherit;
          break;
        case SL_old_none:
          new_aci->sec_level = SL_none;
          break;
        default:
          new_aci->sec_level = old_aci->sec_level;
      };
    switch(old_aci->mac_trusted_for_user)
      {
        case RSBAC_OLD_NO_USER:
          new_aci->mac_trusted_for_user = RSBAC_NO_USER;
          break;
        case RSBAC_OLD_ALL_USERS:
          new_aci->mac_trusted_for_user = RSBAC_ALL_USERS;
          break;
        default:
          new_aci->mac_trusted_for_user = old_aci->mac_trusted_for_user;
      };
    new_aci->mac_categories = old_aci->mac_categories;
    new_aci->object_category = old_aci->object_category;
    new_aci->data_type = old_aci->data_type;
    new_aci->pm_object_class = old_aci->pm_object_class;
    new_aci->pm_tp = old_aci->pm_tp;
    new_aci->pm_object_type = old_aci->pm_object_type;
    new_aci->ms_scanned = old_aci->ms_scanned;
    new_aci->ms_trusted = old_aci->ms_trusted;
    new_aci->ms_sock_trusted_tcp = old_aci->ms_sock_trusted_tcp;
    new_aci->ms_sock_trusted_udp = old_aci->ms_sock_trusted_udp;
    new_aci->ff_flags = old_aci->ff_flags;
    new_aci->rc_type_fd = old_aci->rc_type_fd;
    new_aci->rc_force_role = old_aci->rc_force_role;
    new_aci->auth_may_setuid = old_aci->auth_may_setuid;
    new_aci->auth_may_set_cap = old_aci->auth_may_set_cap;
    new_aci->log_array_low = old_aci->log_array_low;
    new_aci->log_array_high = old_aci->log_array_high;
    new_aci->log_program_based = default_fd_aci.log_program_based;
  };

void convert_old_old_file_dir_aci(struct rsbac_file_dir_old_old_aci_t * old_aci,
                                  struct rsbac_file_dir_aci_t * new_aci)
  {
    new_aci->id = old_aci->id;
    new_aci->sec_level = old_aci->sec_level;
    new_aci->mac_trusted_for_user = old_aci->mac_trusted_for_user;
    new_aci->mac_categories = default_fd_aci.mac_categories;
    new_aci->object_category = old_aci->object_category;
    new_aci->data_type = old_aci->data_type;
    new_aci->pm_object_class = old_aci->pm_object_class;
    new_aci->pm_tp = old_aci->pm_tp;
    new_aci->pm_object_type = old_aci->pm_object_type;
    new_aci->ms_scanned = old_aci->ms_scanned;
    new_aci->ms_trusted = old_aci->ms_trusted;
    new_aci->ms_sock_trusted_tcp = old_aci->ms_sock_trusted_tcp;
    new_aci->ms_sock_trusted_udp = old_aci->ms_sock_trusted_udp;
    new_aci->ff_flags = old_aci->ff_flags;
    new_aci->rc_type_fd = default_fd_aci.rc_type_fd;
    new_aci->rc_force_role = default_fd_aci.rc_force_role;
    new_aci->auth_may_setuid = default_fd_aci.auth_may_setuid;
    new_aci->auth_may_set_cap = default_fd_aci.auth_may_set_cap;
    new_aci->log_array_low = old_aci->log_array_low;
    new_aci->log_array_high = old_aci->log_array_high;
  };

void convert_old_dev_aci(struct rsbac_dev_old_aci_t * old_aci,
                         struct rsbac_dev_aci_t * new_aci)
  {
    new_aci->id = old_aci->id;
    switch(old_aci->sec_level)
      {
        case SL_old_rsbac_internal:
          new_aci->sec_level = SL_rsbac_internal;
          break;
        case SL_old_inherit:
          new_aci->sec_level = SL_inherit;
          break;
        case SL_old_none:
          new_aci->sec_level = SL_none;
          break;
        default:
          new_aci->sec_level = old_aci->sec_level;
      };
    new_aci->mac_categories = old_aci->mac_categories;
    new_aci->object_category = old_aci->object_category;
    new_aci->data_type = old_aci->data_type;
    new_aci->mac_check = old_aci->mac_check;
    new_aci->pm_object_class = old_aci->pm_object_class;
    new_aci->pm_object_type = old_aci->pm_object_type;
    new_aci->rc_type = old_aci->rc_type;
    new_aci->log_array_low = old_aci->log_array_low;
    new_aci->log_array_high = old_aci->log_array_high;
  };

void convert_old_old_dev_aci(struct rsbac_dev_old_old_aci_t * old_aci,
                             struct rsbac_dev_aci_t * new_aci)
  {
    new_aci->id = old_aci->id;
    new_aci->sec_level = old_aci->sec_level;
    new_aci->mac_categories = default_dev_aci.mac_categories;
    new_aci->object_category = old_aci->object_category;
    new_aci->data_type = old_aci->data_type;
    new_aci->mac_check = old_aci->mac_check;
    new_aci->pm_object_class = old_aci->pm_object_class;
    new_aci->pm_object_type = old_aci->pm_object_type;
    new_aci->rc_type = default_dev_aci.rc_type;
    new_aci->log_array_low = old_aci->log_array_low;
    new_aci->log_array_high = old_aci->log_array_high;
  };

void convert_old_user_aci(struct rsbac_user_old_aci_t * old_aci,
                          struct rsbac_user_aci_t * new_aci)
  {
    new_aci->id = old_aci->id;
    new_aci->pseudo = old_aci->pseudo;
    switch(old_aci->access_appr)
      {
        case SL_old_rsbac_internal:
          new_aci->access_appr = SL_rsbac_internal;
          break;
        case SL_old_inherit:
          new_aci->access_appr = SL_inherit;
          break;
        case SL_top_secret:
          new_aci->access_appr = SL_max;
          break;
        case SL_old_none:
          new_aci->access_appr = SL_none;
          break;
        default:
          new_aci->access_appr = old_aci->access_appr;
      };
    new_aci->mac_categories = old_aci->mac_categories;
    new_aci->system_role = old_aci->system_role;
    new_aci->pm_task_set = old_aci->pm_task_set;
    new_aci->pm_role = old_aci->pm_role;
    new_aci->rc_def_role = old_aci->rc_def_role;
    new_aci->log_user_based = default_u_aci.log_user_based;
  };

void convert_old_old_user_aci(struct rsbac_user_old_old_aci_t * old_aci,
                              struct rsbac_user_aci_t * new_aci)
  {
    new_aci->id = old_aci->id;
    new_aci->pseudo = old_aci->pseudo;
    new_aci->access_appr = old_aci->access_appr;
    new_aci->mac_categories = default_u_aci.mac_categories;
    new_aci->system_role = old_aci->system_role;
    new_aci->pm_task_set = old_aci->pm_task_set;
    new_aci->pm_role = old_aci->pm_role;
    new_aci->rc_def_role = default_u_aci.rc_def_role;
  };

/**************************************************/
/*       Externally visible help functions        */
/**************************************************/

/* helper, copied from open.d/do_truncate() */
static int rsbac_clear_file(struct dentry *dentry)
{
	struct inode *inode = dentry->d_inode;
	int error;
	struct iattr newattrs;

	down(&inode->i_sem);
	newattrs.ia_size = 0;
	newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
	error = notify_change(dentry, &newattrs);
	up(&inode->i_sem);
	return error;
}

#ifdef CONFIG_RSBAC_REG
EXPORT_SYMBOL(rsbac_read_open);
#endif
int  rsbac_read_open(char * name,
                     struct file  *  file_p,
                     kdev_t kdev)
  {
    struct dentry                 * dir_dentry_p;
    struct dentry                 * file_dentry_p;
    struct file                     file;
    struct rsbac_device_list_item_t * device_p;
    int                             tmperr, err;

    if(!name && !file_p)
      {
        if (rsbac_debug_ds)
          printk(KERN_WARNING "rsbac_read_open(): called with NULL pointer!");
        return(-RSBAC_EINVALIDPOINTER);
      }

    device_p = lookup_device(kdev);
    if(!device_p)
      return -RSBAC_EINVALIDDEV;

    /* lookup dentry of ACI_PATH on root device, returns errorcode, if   */
    /* failed */
    if ((err=lookup_aci_path_dentry(kdev, &dir_dentry_p, FALSE, device_p)))
      return(err);

    /* open file for reading - this must be done 'by hand', because     */
    /* standard system calls are now extended by rsbac decision calls.  */
    #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
    file_dentry_p = rsbac_lookup_dentry(name, dget(dir_dentry_p), 0);
    #else
    file_dentry_p = rsbac_lookup_one(name, dir_dentry_p);
    #endif
    if (!file_dentry_p || IS_ERR (file_dentry_p))
      { /* error in lookup */
        dput(dir_dentry_p);
        return(-RSBAC_EREADFAILED);
      }
    if (!file_dentry_p->d_inode)
      { /* file not found: trying backup */
        char * bname;
        int  name_len = strlen(name);

        dput(file_dentry_p);
        bname = kmalloc(RSBAC_MAXNAMELEN, GFP_KERNEL);
        if(!bname)
          {
            dput(dir_dentry_p);
            return -RSBAC_ENOMEM;
          }

        strcpy(bname,name);
        bname[name_len] = 'b';
        name_len++;
        bname[name_len] = (char) 0;
        if (rsbac_debug_ds)
          printk(KERN_DEBUG "rsbac_read_open(): could not lookup file %s, trying backup %s\n",
                 name,bname);
        #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
        file_dentry_p = rsbac_lookup_dentry(bname, dget(dir_dentry_p), 0);
        #else
        file_dentry_p = rsbac_lookup_one(bname, dir_dentry_p);
        #endif
        if (!file_dentry_p || IS_ERR (file_dentry_p))
          { /* error in lookup */
            dput(dir_dentry_p);
            return(-RSBAC_EREADFAILED);
          }
        if (!file_dentry_p->d_inode)
          { /* backup file also not found: return error */
            if (rsbac_debug_ds)
              printk(KERN_DEBUG "rsbac_read_open(): could not lookup backup file %s\n",
                     bname);
            dput(file_dentry_p);
            dput(dir_dentry_p);
            return(-RSBAC_ENOTFOUND);
          }
      }
    dput(dir_dentry_p);
    if ( !(S_ISREG(file_dentry_p->d_inode->i_mode)) )
      { /* this is not a file! -> error! */
        printk(KERN_WARNING "%s\n",
               "rsbac_read_open(): expected file is not a file!");
        dput(file_dentry_p);
        return(-RSBAC_EREADFAILED);
      }

    /* Now we fill the file structure and */
    /* if there is an open func for this file, use it, otherwise ignore */
    if ((tmperr = init_private_file(&file, file_dentry_p,O_RDONLY)))
      {
        dput(file_dentry_p);
        printk(KERN_WARNING "rsbac_read_open(): could not open file '%s'!\n", name);
        return -RSBAC_EREADFAILED;
      }

    /* if there is no read func, we get a problem -> error */
    if ((!file.f_op) || (!file.f_op->read))
      {
        if (!file.f_op)
          printk(KERN_WARNING "rsbac_read_open(): no f_op for file '%s'!\n", name);
        else
          {
            printk(KERN_WARNING "rsbac_read_open(): no file read func for file '%s'!\n", name);
            if (file.f_op->release)
              file.f_op->release(file_dentry_p->d_inode,&file);
          }
        dput(file_dentry_p);
        return -RSBAC_EREADFAILED;
      }
    *file_p = file;
    return 0;
  } /* end of rsbac_read_open */

#ifndef check_parent
#define check_parent(dir, dentry) \
	((dir) == (dentry)->d_parent && !list_empty(&dentry->d_hash))
#endif

#ifdef CONFIG_RSBAC_REG
EXPORT_SYMBOL(rsbac_write_open);
#endif
int  rsbac_write_open(char * name,
                      struct file  *  file_p,
                      kdev_t kdev)
  {
    struct dentry                  * dir_dentry_p = NULL;
    struct dentry                  * ldir_dentry_p = NULL;
    struct dentry                  * file_dentry_p = NULL;
    struct file                     file;
    int                             err = 0;
    int                             tmperr = 0;
    struct super_block           *  sb_p = NULL;
    
        if(!file_p || !name)
          {
            if (rsbac_debug_write)
              printk(KERN_DEBUG "rsbac_write_open(): called with NULL pointer!\n");
            return(-RSBAC_EINVALIDPOINTER);
          }

        /* get super_block-pointer */
        sb_p = get_super(kdev);
        if (!sb_p)
          {
            printk(KERN_WARNING
              "rsbac_write_open(): No super_block for device %u\n",
                     (u_int) kdev);
            return (-RSBAC_EINVALIDDEV);
          }
        if(!writable(sb_p))
          {
            if (rsbac_debug_write)
              printk(KERN_DEBUG
                     "rsbac_write_open(): called for non-writable device\n");
            return(-RSBAC_ENOTWRITABLE);
          }
          
        /* lookup dentry of ACI_PATH on this device (create, if needed and possible),
         * returns errorcode, if failed */
        if ((tmperr = lookup_aci_path_dentry(kdev, &dir_dentry_p, TRUE,
                                             lookup_device(kdev))))
          {
            return(tmperr);
          }

        /* open file for reading - this must be done 'by hand', because     */
        /* standard system calls are now extended by rsbac decision calls.  */
        #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
        file_dentry_p = rsbac_lookup_dentry(name, dget(dir_dentry_p), 0);
        #else
        file_dentry_p = rsbac_lookup_one(name, dir_dentry_p);
        #endif
        if (!file_dentry_p || IS_ERR(file_dentry_p))
          {
            if (rsbac_debug_write)
              printk(KERN_DEBUG
                     "rsbac_write_open(): lookup of %s returned error %li\n",
                     name, PTR_ERR(file_dentry_p));
            dput(dir_dentry_p);
            return(-RSBAC_EWRITEFAILED);
          }

#if 1
        if (file_dentry_p->d_inode)
          { /* file was found: try to rename it as backup file */
            if (   !dir_dentry_p->d_inode->i_op
                || !dir_dentry_p->d_inode->i_op->rename)
              {
                printk(KERN_WARNING
                       "rsbac_write_open(): File system supports no rename - no backup of %s made!",
                       name);
              }
            else
              {
                char * bname;
                int  name_len = strlen(name);
                struct dentry * new_file_dentry_p = NULL;
                struct dentry * old_dir_p, * new_dir_p;

                bname = kmalloc(RSBAC_MAXNAMELEN, GFP_KERNEL);
                if(!bname)
                  {
                    dput(dir_dentry_p);
                    dput(file_dentry_p);
                    return -RSBAC_ENOMEM;
                  }
                strcpy(bname,name);
                bname[name_len] = 'b';
                bname[name_len+1] = (char) 0;
                #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
                new_file_dentry_p = rsbac_lookup_dentry(bname,
                                                        dget(dir_dentry_p),
                                                        0);
                #else
                new_file_dentry_p = rsbac_lookup_one(bname, dir_dentry_p);
                #endif
                if(new_file_dentry_p && !IS_ERR(new_file_dentry_p))
                  {
                    /* lock parent == rsbac-dir for rest of rename */
                    old_dir_p = dget(file_dentry_p->d_parent);
                    new_dir_p = dget(new_file_dentry_p->d_parent);
                    double_lock(new_dir_p, old_dir_p);

                    DQUOT_INIT(old_dir_p->d_inode);
                    DQUOT_INIT(new_dir_p->d_inode);
                    /* try to rename file in rsbac dir */
                    /* if (rsbac_debug_write)
                     * printk(KERN_DEBUG "rsbac_write_open(): calling rename function\n");
                     */
                    err = -ENOENT;
                    if (   check_parent(old_dir_p, file_dentry_p)
                        && check_parent(new_dir_p, new_file_dentry_p))
                      {
                        err = dir_dentry_p->d_inode->i_op->rename(old_dir_p->d_inode,
                                                                  file_dentry_p,
                                                                  new_dir_p->d_inode,
                                                                  new_file_dentry_p);
                      }
                    else
                      {
                        printk(KERN_WARNING
                               "rsbac_write_open(): could not rename %s to %s on dev %02u:%02u, parent check failed - no backup!\n",
                               name,bname, MAJOR(kdev),MINOR(kdev));
                      }
                    /* unlock dir */
                    double_unlock(new_dir_p, old_dir_p);
                    if(err)
                      {
                        printk(KERN_WARNING
                               "rsbac_write_open(): could not rename %s to %s on dev %02u:%02u, error %i - no backup!\n",
                               name,bname, MAJOR(kdev),MINOR(kdev), err);
                      }
                    else
                      {
                        /* The following d_move() should become unconditional */
             #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
                        if (!(sb_p->s_flags & MS_ODD_RENAME))
                          d_move(file_dentry_p, new_file_dentry_p);
             #else
                        if (!(sb_p->s_flags & FS_ODD_RENAME))
                          d_move(file_dentry_p, new_file_dentry_p);
             #endif
                      }
                    /* free both file dentries */
                    dput(new_file_dentry_p);
                    dput(file_dentry_p);
                    /* re-init dentry structure */
                    #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
                    file_dentry_p = rsbac_lookup_dentry(name,
                                                        dget(dir_dentry_p),
                                                        0);
                    #else
                    file_dentry_p = rsbac_lookup_one(name, dir_dentry_p);
                    #endif
                    if (!file_dentry_p || IS_ERR (file_dentry_p))
                      {
                        if (rsbac_debug_write)
                          printk(KERN_DEBUG
                                 "rsbac_write_open(): relookup of %s returned error %li\n",
                                 name, PTR_ERR(file_dentry_p));
                        dput(dir_dentry_p);
                        return(-RSBAC_EWRITEFAILED);
                      }
                    if (file_dentry_p->d_inode)
                      {
                        printk(KERN_WARNING
                               "rsbac_write_open(): relookup of %s returned dentry with existing inode %li, trying unlink\n",
                               name, file_dentry_p->d_inode->i_ino);
                        /* file was found: try to delete it */
                        if (   !dir_dentry_p->d_inode->i_op
                            || !dir_dentry_p->d_inode->i_op->unlink)
                          {
                            printk(KERN_WARNING
                                   "rsbac_write_open(): File system supports no unlink - %s not deleted!",
                                   name);
                            dput(file_dentry_p);
                            dput(dir_dentry_p);
                            return(-RSBAC_EWRITEFAILED);
                          }
                        else
                          {
                            old_dir_p = lock_parent(file_dentry_p);

                            DQUOT_INIT(old_dir_p->d_inode);
                            err = -ENOENT;
                            if (check_parent(old_dir_p, file_dentry_p))
                              {
                                err = dir_dentry_p->d_inode->i_op->unlink(old_dir_p->d_inode,
                                                                          file_dentry_p);
                              }
                            else
                              {
                                printk(KERN_WARNING
                                       "rsbac_write_open(): could not unlink %s on dev %02u:%02u, parent check failed!\n",
                                       name, MAJOR(kdev),MINOR(kdev));
                              }
                            /* unlock parent dir */
                            unlock_dir(old_dir_p);
                            /* free file dentry */
                            dput(file_dentry_p);
                            if(err)
                              {
                                printk(KERN_WARNING
                                       "rsbac_write_open(): could not unlink %s on dev %02u:%02u, error %i!\n",
                                       name, MAJOR(kdev),MINOR(kdev), err);
                              }
                            /* re-init dentry structure */
                            #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
                            file_dentry_p = rsbac_lookup_dentry(name,
                                                                dget(dir_dentry_p),
                                                                0);
                            #else
                            file_dentry_p = rsbac_lookup_one(name, dir_dentry_p);
                            #endif
                            if (!file_dentry_p || IS_ERR (file_dentry_p))
                              {
                                if (rsbac_debug_write)
                                  printk(KERN_DEBUG
                                         "rsbac_write_open(): relookup of %s returned error %li\n",
                                         name, PTR_ERR(file_dentry_p));
                                dput(dir_dentry_p);
                                return(-RSBAC_EWRITEFAILED);
                              }
                            if (new_file_dentry_p->d_inode)
                              {
                                printk(KERN_WARNING
                                       "rsbac_write_open(): relookup of %s returned dentry with existing inode %li\n",
                                       name, file_dentry_p->d_inode->i_ino);
                                dput(file_dentry_p);
                                dput(dir_dentry_p);
                                return(-RSBAC_EWRITEFAILED);
                              }
                          }
                      }
                  }
                else
                  {
                    printk(KERN_WARNING
                           "rsbac_write_open(): rsbac_lookup_(dentry|one) for backup file %s on dev %02u:%02u failed with error %li - no backup!\n",
                           bname, MAJOR(kdev),MINOR(kdev), PTR_ERR(new_file_dentry_p));
                  }
              }
          }
#endif /* backup part */

        if(!file_dentry_p->d_inode)
          {
            /* file not found or renamed away: try to create a new one */
    /*            if (rsbac_debug_write)
                  printk(KERN_DEBUG
                         "rsbac_write_open(): could not lookup %s, trying to create it\n",
                         name); */
            if (   !dir_dentry_p->d_inode->i_op
                || !dir_dentry_p->d_inode->i_op->create)
              {
                printk(KERN_WARNING "%s\n",
                       "rsbac_write_open(): File system supports no create!");
                       dput(file_dentry_p);
                       dput(dir_dentry_p);
                       return(-RSBAC_EWRITEFAILED);
              }

            /* lock parent == rsbac-dir for create */
            ldir_dentry_p = lock_parent(file_dentry_p);
            if(IS_ERR(ldir_dentry_p))
              {
                if (rsbac_debug_write)
                  printk(KERN_DEBUG
                         "rsbac_write_open(): lock_parent of %s returned error %li\n",
                         name, PTR_ERR(ldir_dentry_p));
                dput(file_dentry_p);
                dput(dir_dentry_p);
                return(-RSBAC_EWRITEFAILED);
              }
            if (!check_parent(ldir_dentry_p, file_dentry_p))
              {
                if (rsbac_debug_write)
                  printk(KERN_DEBUG
                         "rsbac_write_open(): check_parent of %s returned error\n",
                         name);
                unlock_dir(ldir_dentry_p);
                dput(file_dentry_p);
                dput(dir_dentry_p);
                return(-RSBAC_EWRITEFAILED);
              }
            /* try to create file in rsbac dir */
            /* if (rsbac_debug_write)
             *   printk(KERN_DEBUG "%s\n",
             *          "rsbac_write_open(): calling create function");
             */
            DQUOT_INIT(ldir_dentry_p->d_inode);
            err = dir_dentry_p->d_inode->i_op->create(ldir_dentry_p->d_inode,
                                                      file_dentry_p,
                                                      RSBAC_ACI_FILE_MODE);
            unlock_dir(ldir_dentry_p);

            if (err)
              {
                /* dput */
                dput(file_dentry_p);
                dput(dir_dentry_p);
                return(err);
              }
            /* create was successful */
          }

        /* dir_dentry is no longer needed */
        dput(dir_dentry_p);

        if ( !(S_ISREG(file_dentry_p->d_inode->i_mode)) )
          { /* this is not a file! -> error! */
            printk(KERN_WARNING
                   "rsbac_write_open(): expected file is not a file, mode is %o!\n",
                   file_dentry_p->d_inode->i_mode);
            dput(file_dentry_p);
            return (-RSBAC_EWRITEFAILED);
          }
        /* Now we fill the file structure and */
        /* if there is an open func for this file, use it, otherwise ignore */
        if ((tmperr = init_private_file(&file, file_dentry_p,O_RDONLY)))
          {
            dput(file_dentry_p);
            printk(KERN_WARNING "rsbac_write_open(): could not init file!\n");
            return (-RSBAC_EWRITEFAILED);
          }

        /* Without a write function we get into troubles -> error */
	if ((!file.f_op) || (!file.f_op->write))
	  {
            printk(KERN_WARNING
                   "rsbac_write_open(): file write function missing!\n");
            if(file.f_op && file.f_op->release)
              {
                  file.f_op->release(file_dentry_p->d_inode,&file);
              }
            dput(file_dentry_p);
            return(-RSBAC_EWRITEFAILED);
	  }

        /* trying to get write access                                   */
        /* if (rsbac_debug_write)
         *   printk(KERN_DEBUG "rsbac_write_open(): trying to get write access!\n");
         */
        if (get_write_access(file_dentry_p->d_inode))
	  {
            printk(KERN_WARNING
                   "rsbac_write_open(): could not get write access!\n");
            if (file.f_op->release)
              file.f_op->release(file_dentry_p->d_inode,&file);
            dput(file_dentry_p);
            return(-RSBAC_EWRITEFAILED);
	  }
	/* truncating */
	if(rsbac_clear_file(file_dentry_p))
	  {
            if (file.f_op->release)
              file.f_op->release(file_dentry_p->d_inode,&file);
	    put_write_access(file_dentry_p->d_inode);
            printk(KERN_WARNING
                   "rsbac_write_open(): could not truncate!\n");
            dput(file_dentry_p);
            return(-RSBAC_EWRITEFAILED);
          }
    /* set synchronous mode for this file */
    file.f_flags |= O_SYNC;

    *file_p = file;

    return 0;
  }

#ifdef CONFIG_RSBAC_REG
EXPORT_SYMBOL(rsbac_read_close);
#endif
void rsbac_read_close(struct file * file_p)
  {
        /* cleanup copied from __fput */
	struct dentry * dentry = file_p->f_dentry;
	struct inode * inode = dentry->d_inode;

	if (file_p->f_op && file_p->f_op->release)
		file_p->f_op->release(inode, file_p);
	file_p->f_dentry = NULL;
	if (file_p->f_mode & FMODE_WRITE)
		put_write_access(inode);
	dput(dentry);
  }

#ifdef CONFIG_RSBAC_REG
EXPORT_SYMBOL(rsbac_write_close);
#endif
void rsbac_write_close(struct file * file_p)
  {
    rsbac_read_close(file_p);
  }

#ifdef CONFIG_RSBAC_LOG_FULL_PATH
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
int get_full_path(struct dentry * dentry_p, char path[], int maxlen)
  {
    int len=0;
    char * i_path;
    int   tmplen=0;

    if(!dentry_p || !path)
      return -RSBAC_EINVALIDPOINTER;
    if(maxlen <= 0)
      return -RSBAC_EINVALIDVALUE;
    i_path = kmalloc(maxlen + RSBAC_MAXNAMELEN, GFP_KERNEL);
    if(!i_path)
      return -RSBAC_ENOMEM;
    
    path[0] = 0;

    while(dentry_p && (len<maxlen) && dentry_p->d_name.len && dentry_p->d_name.name)
      {
        tmplen = dentry_p->d_covers->d_name.len;
        if((tmplen+1) > (maxlen-len))
          return(len);
        strncpy(i_path, dentry_p->d_covers->d_name.name, tmplen);
        if(len && (i_path[tmplen-1] != '/'))
          {
            i_path[tmplen] = '/';
            tmplen++;
          }
        i_path[tmplen]=0;
        strcat(i_path, path);
        strcpy(path, i_path);
        len += tmplen;
        if(   dentry_p->d_covers
           && dentry_p->d_covers->d_parent
           && (dentry_p->d_covers->d_parent != dentry_p)
          )
          dentry_p = dentry_p->d_covers->d_parent;
        else
          break;
      }
    kfree(i_path);
    return(len);
  };
#else
int get_full_path(struct dentry * dentry_p, char path[], int maxlen)
  {
    int len=0;
    char * i_path;
    int   tmplen=0;

    if(!dentry_p || !path)
      return -RSBAC_EINVALIDPOINTER;
    if(maxlen <= 0)
      return -RSBAC_EINVALIDVALUE;
    i_path = kmalloc(maxlen + RSBAC_MAXNAMELEN, GFP_KERNEL);
    if(!i_path)
      return -RSBAC_ENOMEM;

    path[0] = 0;

    while(dentry_p && (len<maxlen) && dentry_p->d_name.len && dentry_p->d_name.name)
      {
        tmplen = dentry_p->d_name.len;
        if((tmplen+1) > (maxlen-len))
          return(len);
        strncpy(i_path, dentry_p->d_name.name, tmplen);
        if(len && (i_path[tmplen-1] != '/'))
          {
            i_path[tmplen] = '/';
            tmplen++;
          }
        i_path[tmplen]=0;
        strcat(i_path, path);
        strcpy(path, i_path);
        len += tmplen;
        if(   dentry_p->d_parent
           && (dentry_p->d_parent != dentry_p)
           && (dentry_p->d_sb->s_root != dentry_p)
          )
          dentry_p = dentry_p->d_parent;
        else
          {
            struct rsbac_device_list_item_t * device_p;

            device_p = lookup_device(dentry_p->d_inode->i_dev);
            if(   device_p
               && device_p->d_covers
              )
              dentry_p = device_p->d_covers;
            else
              break;
          }
      }
    kfree(i_path);
    return(len);
  };
#endif
#endif

/************************************************* */
/*               RSBAC daemon                      */
/************************************************* */

#if defined(CONFIG_RSBAC_AUTO_WRITE) && (CONFIG_RSBAC_AUTO_WRITE > 0)
/* rsbac kernel timer for auto-write */
static void wakeup_rsbacd(u_long dummy)
  {
    wake_up(&rsbacd_wait);
  }

/* rsbac kernel daemon for auto-write */
static int rsbacd(void * dummy)
  {
    struct task_struct *tsk = current;
    char * name = kmalloc(RSBAC_MAXNAMELEN, GFP_KERNEL);
    tsk->session = 1;
    tsk->pgrp = 1;
    strcpy(tsk->comm, "rsbacd");

    lock_kernel(); 

    for(;;)
      {
        if(rsbac_initialized)
          {
            int err;
/*            if (rsbac_debug_auto)
              printk(KERN_DEBUG
                     "rsbacd(): calling rsbac_write()\n");
*/
            /* call rsbac_write with lock_kernel() */
            err = rsbac_write(TRUE);
            if(err < 0)
              {
                if(name)
                  printk(KERN_WARNING
                         "rsbacd(): rsbac_write returned error %s!\n",
                         get_error_name(name,err));
                else
                  printk(KERN_WARNING
                         "rsbacd(): rsbac_write returned error %i!\n",
                         err);
              }
            if (rsbac_debug_auto && (err > 0))
              printk(KERN_DEBUG
                     "rsbacd(): rsbac_write() wrote %i lists\n", err);
          }
        /* wait */
        spin_lock_irq(&tsk->sigmask_lock);
        flush_signals(tsk);
        spin_unlock_irq(&tsk->sigmask_lock);
        /* set new timer */
        /*mod_timer(&rsbac_timer, jiffies + auto_interval);*/
        interruptible_sleep_on(&rsbacd_wait);
	
	tsk->state = TASK_INTERRUPTIBLE;
	schedule_timeout(auto_interval);

      }
  }
#endif /* CONFIG_RSBAC_AUTO_WRITE > 0 */

/************************************************* */
/*               proc fs functions                 */
/************************************************* */

#if defined(CONFIG_RSBAC_PROC) && defined(CONFIG_PROC_FS)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
static int
devices_proc_info(char *buffer, char **start, off_t offset, int length, int dummy)
#else
static int
devices_proc_info(char *buffer, char **start, off_t offset, int length)
#endif
{
  int len = 0;
  off_t pos   = 0;
  off_t begin = 0;
  struct rsbac_device_list_item_t   * device_p;
  u_long dflags;

  if (!rsbac_initialized) return (-ENOSYS);

  len += sprintf(buffer, "%u RSBAC Devices\n---------------\n",
                 device_list_head.count);

  /* wait for read access to device_list_head */
  rsbac_read_lock(&device_list_head.lock, &dflags);
  /* OK, go on */
  for (device_p = device_list_head.head; device_p; device_p = device_p->next)
    {
      len += sprintf(buffer + len, "%02u:%02u with no_write = %u\n",
                     MAJOR(device_p->id), MINOR(device_p->id), device_p->no_write);
      pos = begin + len;
      if (pos < offset)
        {
          len = 0;
          begin = pos;
        }
      if (pos > offset+length) break;
    }
  
  /* free access to device_list_head */
  rsbac_read_unlock(&device_list_head.lock, &dflags);

  *start = buffer + (offset - begin);
  len -= (offset - begin);
  
  if (len > length)
    len = length;
  return len;
}

static ssize_t devices_proc_write(struct file * file, const char * buf,
                                  u_long count, void *ppos)
{
    ssize_t err = -EINVAL;
    char * k_buf;
    char * p;

    union rsbac_target_id_t       rsbac_target_id;
    union rsbac_attribute_value_t rsbac_attribute_value;

    if(count > PROC_BLOCK_SIZE) {
	return(-EOVERFLOW);
    }

    if (!(k_buf = (char *) __get_free_page(GFP_KERNEL)))
      return(-ENOMEM);
    copy_from_user(k_buf, buf, count);

  if(count < 13 || strncmp("devices", k_buf, 7))
    {
      goto out;
    }
  if (!rsbac_initialized)
    {
      err=-ENOSYS;
      goto out;
    }

  if (rsbac_debug_aef)
    printk(KERN_DEBUG "devices_proc_write(): calling ADF\n");
  rsbac_target_id.scd = ST_rsbac;
  rsbac_attribute_value.dummy = 0;
  if (!rsbac_adf_request(R_MODIFY_SYSTEM_DATA,
                         current->pid,
                         T_SCD,
                         rsbac_target_id,
                         A_none,
                         rsbac_attribute_value))
    {
      err=-EPERM;
      goto out;
    }

    /*
     * Usage: echo "devices no_write major:minor #N" > /proc/rsbac_info/devices
     *   to set no_write to on or off
     */
    if(!strncmp("no_write", k_buf + 8, 8)) 
    {
        unsigned int major, minor, value = 2;
        u_long flags;
        struct rsbac_device_list_item_t * device_p;

	p = k_buf + 8 + 9;

        if( *p == '\0' )
          goto out;

        major = simple_strtoul(p, NULL, 0);
        while((*p != ':') && (*p != '\0'))
          p++;
        if( *p == '\0' )
          goto out;
        p++;
        minor = simple_strtoul(p, NULL, 0);
        while((*p != ' ') && (*p != '\0'))
          p++;
        if( *p == '\0' )
          goto out;
        while(((*p < '0')||(*p > '9')) && (*p != '\0'))
          p++;
        if( *p == '\0' )
          goto out;
        value = simple_strtoul(p, NULL, 0);

        /* only accept values 0 and 1 (2 is preset for value) */
        if(value != 2)
          {
            rsbac_read_lock(&device_list_head.lock,&flags);
            device_p = lookup_device(MKDEV(major, minor));
            if(!device_p)
              {
                printk(KERN_WARNING
                       "devices_proc_write(): invalid device %02u:%02u!\n",
                       major,minor);
                rsbac_read_unlock(&device_list_head.lock,&flags);
                goto out;
              }
            printk(KERN_INFO
                   "devices_proc_write(): setting no_write for device %02u:%02u to %u\n",
                   major,minor,value);
            device_p->no_write = value;
            rsbac_read_unlock(&device_list_head.lock,&flags);
            err = count;
          }
        else
          {
            printk(KERN_INFO
                   "devices_proc_write(): rejecting invalid no_write value!\n");
          }
    }

out:
  free_page((ulong) k_buf);
  return(err);
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
static int
stats_proc_info(char *buffer, char **start, off_t offset, int length, int dummy)
#else
static int
stats_proc_info(char *buffer, char **start, off_t offset, int length)
#endif
{
    u_int len = 0;
    off_t pos   = 0;
    off_t begin = 0;
    struct rsbac_device_list_item_t   * device_p;
    int                                 i;
    u_long                              fd_count = 0, fd_sum = 0;
    u_int                               fd_dirty = 0, dirty = 0;
    u_long                              dflags;

    union rsbac_target_id_t       rsbac_target_id;
    union rsbac_attribute_value_t rsbac_attribute_value;

    if (!rsbac_initialized) return (-ENOSYS);

    if (rsbac_debug_aef) printk(KERN_DEBUG "stats_proc_info(): calling ADF\n");
    rsbac_target_id.scd = ST_rsbac;
    rsbac_attribute_value.dummy = 0;
    if (!rsbac_adf_request(R_GET_STATUS_DATA,
                           current->pid,
                           T_SCD,
                           rsbac_target_id,
                           A_none,
                           rsbac_attribute_value))
      {
        return -EPERM;
      }

#ifdef CONFIG_RSBAC_MAINT
    len = sprintf(buffer, "RSBAC Status\n------------\nRSBAC Version: %s (Maintenance Mode)\nSupported Modules:%s\n",
                   RSBAC_VERSION, compiled_modules);
#else
    len = sprintf(buffer, "RSBAC Status\n------------\nRSBAC Version: %s\nCompiled Modules:%s\n",
                   RSBAC_VERSION, compiled_modules);
#endif
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    #ifdef CONFIG_RSBAC_SWITCH
    {
      char * active_modules;

      active_modules = kmalloc(RSBAC_MAXNAMELEN, GFP_KERNEL);
      if(active_modules)
        {
          active_modules[0] = (char) 0;
          #ifdef CONFIG_RSBAC_MAC
          if(rsbac_switch_mac)
            strcat(active_modules, " MAC");
          #endif
          #ifdef CONFIG_RSBAC_FC
          if(rsbac_switch_fc)
            strcat(active_modules, " FC");
          #endif
          #ifdef CONFIG_RSBAC_SIM
          if(rsbac_switch_sim)
            strcat(active_modules, " SIM");
          #endif
          #ifdef CONFIG_RSBAC_PM
          if(rsbac_switch_pm)
            strcat(active_modules, " PM");
          #endif
          #ifdef CONFIG_RSBAC_MS
          if(rsbac_switch_ms)
            strcat(active_modules, " MS");
          #endif
          #ifdef CONFIG_RSBAC_FF
          if(rsbac_switch_ff)
            strcat(active_modules, " FF");
          #endif
          #ifdef CONFIG_RSBAC_RC
          if(rsbac_switch_rc)
            strcat(active_modules, " RC");
          #endif
          #ifdef CONFIG_RSBAC_AUTH
          if(rsbac_switch_auth)
            strcat(active_modules, " AUTH");
          #endif
          #ifdef CONFIG_RSBAC_REG
          strcat(active_modules, " REG");
          #endif
          #ifdef CONFIG_RSBAC_ACL
          if(rsbac_switch_acl)
            strcat(active_modules, " ACL");
          #endif
          len += sprintf(buffer+len, "Active Modules:  %s\n\n",
                         active_modules);
          kfree(active_modules);
          pos = begin + len;
          if (pos < offset)
            {
              len = 0;
              begin = pos;
            }
          if (pos > offset+length)
            goto out;
        }
    }
    #else
      len += sprintf(buffer+len, "All modules active (no switching)\n\n");
      pos = begin + len;
      if (pos < offset)
        {
          len = 0;
          begin = pos;
        }
      if (pos > offset+length)
        goto out;
    #endif

    /* wait for read access to device_list_head */
    rsbac_read_lock(&device_list_head.lock, &dflags);
    /* OK, go on */
    device_p = device_list_head.head;
    while (device_p)
      { /* for all sublists */
        fd_count = 0;
        fd_dirty = 0;
        for (i=0; i < RSBAC_NR_FD_LISTS; i++)
          {
            if(device_p->head_table[i].dirty)
              fd_dirty++;
            fd_count += device_p->head_table[i].count;
          };
        len += sprintf(buffer + len, "Device %02u:%02u has %lu fd-items and %u dirty lists\n",
                       MAJOR(device_p->id), MINOR(device_p->id), fd_count, fd_dirty);
        pos = begin + len;
        if (pos < offset)
          {
            len = 0;
            begin = pos;
          }
        if (pos > offset+length)
          {
            /* free access to device_list_head */
            rsbac_read_unlock(&device_list_head.lock, &dflags);
            goto out;
          }
        fd_sum += fd_count;
        dirty += fd_dirty;
        device_p = device_p->next;
      };
    len += sprintf(buffer + len, "Sum of %u Devices with %lu fd-items\n",
                   device_list_head.count, fd_sum);
    /* free access to device_list_head */
    rsbac_read_unlock(&device_list_head.lock, &dflags);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;
    
    /* dev list */
    if(dev_list_head.dirty)
      {
        dirty++;
        len += sprintf(buffer + len, "%u dev-items (list is dirty)\n",
                       dev_list_head.count);
      }
    else
      {
        len += sprintf(buffer + len, "%u dev-items\n",
                       dev_list_head.count);
      }
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;
    
    /* ipc list */
    len += sprintf(buffer + len, "%lu ipc-items\n",
                 ipc_list_head.count);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;
    
    /* user list */
    if(user_list_head.dirty)
      {
        dirty++;
        len += sprintf(buffer + len, "%u user-items (list is dirty)\n",
                       user_list_head.count);
      }
    else
      {
        len += sprintf(buffer + len, "%u user-items\n",
                       user_list_head.count);
      }
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;
    
    /* process list */
    len += sprintf(buffer + len, "%lu process-items\n",
                 process_list_head.count);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;
    
    len += sprintf(buffer + len,
                   "Total of %lu registered rsbac-items, %u lists dirty\n",
                   fd_sum + dev_list_head.count + ipc_list_head.count
                   + user_list_head.count + process_list_head.count,
                   dirty);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;
    
    len += sprintf(buffer + len,
                   "\nadf_request calls:\nfile: %lu, dir: %lu, dev: %lu, ipc: %lu, scd: %lu, user: %lu, process: %lu\n",
                   rsbac_adf_request_count[T_FILE],
                   rsbac_adf_request_count[T_DIR],
                   rsbac_adf_request_count[T_DEV],
                   rsbac_adf_request_count[T_IPC],
                   rsbac_adf_request_count[T_SCD],
                   rsbac_adf_request_count[T_USER],
                   rsbac_adf_request_count[T_PROCESS]);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;
    
    len += sprintf(buffer + len,
                   "adf_set_attr calls:\nfile: %lu, dir: %lu, dev: %lu, ipc: %lu, scd: %lu, user: %lu, process: %lu\n",
                   rsbac_adf_set_attr_count[T_FILE],
                   rsbac_adf_set_attr_count[T_DIR],
                   rsbac_adf_set_attr_count[T_DEV],
                   rsbac_adf_set_attr_count[T_IPC],
                   rsbac_adf_set_attr_count[T_SCD],
                   rsbac_adf_set_attr_count[T_USER],
                   rsbac_adf_set_attr_count[T_PROCESS]);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }

out:
  *start = buffer + (offset - begin);
  len -= (offset - begin);
  
  if (len > length)
    len = length;
  return len;
}

#ifdef CONFIG_RSBAC_XSTATS
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
static int
xstats_proc_info(char *buffer, char **start, off_t offset, int length, int dummy)
#else
static int
xstats_proc_info(char *buffer, char **start, off_t offset, int length)
#endif
{
    u_int len = 0;
    off_t pos   = 0;
    off_t begin = 0;
    int                                 i,j;
    char                                name[80];
    union rsbac_target_id_t       rsbac_target_id;
    union rsbac_attribute_value_t rsbac_attribute_value;

    if (!rsbac_initialized) return (-ENOSYS);

    if (rsbac_debug_aef) printk(KERN_DEBUG "xstats_proc_info(): calling ADF\n");
    rsbac_target_id.scd = ST_rsbac;
    rsbac_attribute_value.dummy = 0;
    if (!rsbac_adf_request(R_GET_STATUS_DATA,
                           current->pid,
                           T_SCD,
                           rsbac_target_id,
                           A_none,
                           rsbac_attribute_value))
      {
        return -EPERM;
      }

    len += sprintf(buffer,
                   "RSBAC ADF call Statistics\n-------------------------\nadf_request table:\n");
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    len += sprintf(buffer + len, "Request /\tFILE\tDIR\tDEV\tIPC\tSCD\tUSER\tPROCESS\tNONE");
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    for(i=0;i<R_NONE;i++)
      {
        get_request_name(name, i);
        name[15]=0;
        len += sprintf(buffer + len, "\n%-14s\t",name);
        pos = begin + len;
        if (pos < offset)
          {
            len = 0;
            begin = pos;
          }
        if (pos > offset+length)
          goto out;
        for(j=0;j<=T_NONE;j++)
          {
            len += sprintf(buffer + len, "%lu\t",
                           rsbac_adf_request_xcount[j][i]);
            pos = begin + len;
            if (pos < offset)
              {
                len = 0;
                begin = pos;
              }
            if (pos > offset+length)
              goto out;
          }
      }
    
    len += sprintf(buffer + len,
                   "\n\nadf_request calls:\nfile: %lu, dir: %lu, dev: %lu, ipc: %lu, scd: %lu, user: %lu, process: %lu, none: %lu\n",
                   rsbac_adf_request_count[T_FILE],
                   rsbac_adf_request_count[T_DIR],
                   rsbac_adf_request_count[T_DEV],
                   rsbac_adf_request_count[T_IPC],
                   rsbac_adf_request_count[T_SCD],
                   rsbac_adf_request_count[T_USER],
                   rsbac_adf_request_count[T_PROCESS],
                   rsbac_adf_request_count[T_NONE]);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    len += sprintf(buffer+len,
                   "\n\nadf_set_attr table:\nRequest /\tFILE\tDIR\tDEV\tIPC\tSCD\tUSER\tPROCESS\tNONE");
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    for(i=0;i<R_NONE;i++)
      {
        get_request_name(name, i);
        name[15]=0;
        len += sprintf(buffer + len, "\n%-14s\t",name);
        pos = begin + len;
        if (pos < offset)
          {
            len = 0;
            begin = pos;
          }
        if (pos > offset+length)
          goto out;
        for(j=0;j<=T_NONE;j++)
          {
            len += sprintf(buffer + len, "%lu\t",
                           rsbac_adf_set_attr_xcount[j][i]);
            pos = begin + len;
            if (pos < offset)
              {
                len = 0;
                begin = pos;
              }
            if (pos > offset+length)
              goto out;
          }
      }
    
    len += sprintf(buffer + len,
                   "\n\nadf_set_attr calls:\nfile: %lu, dir: %lu, dev: %lu, ipc: %lu, scd: %lu, user: %lu, process: %lu, none: %lu\n",
                   rsbac_adf_set_attr_count[T_FILE],
                   rsbac_adf_set_attr_count[T_DIR],
                   rsbac_adf_set_attr_count[T_DEV],
                   rsbac_adf_set_attr_count[T_IPC],
                   rsbac_adf_set_attr_count[T_SCD],
                   rsbac_adf_set_attr_count[T_USER],
                   rsbac_adf_set_attr_count[T_PROCESS],
                   rsbac_adf_set_attr_count[T_NONE]);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    len += sprintf(buffer + len,
                   "\n\nData Structures:\nSuccessful rsbac_get_attr calls:\nfile: %lu, dir: %lu, dev: %lu, ipc: %lu, scd: %lu, user: %lu, process: %lu\n",
                   get_attr_count[T_FILE],
                   get_attr_count[T_DIR],
                   get_attr_count[T_DEV],
                   get_attr_count[T_IPC],
                   get_attr_count[T_SCD],
                   get_attr_count[T_USER],
                   get_attr_count[T_PROCESS]);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    len += sprintf(buffer + len,
                   "\nSuccessful rsbac_set_attr calls:\nfile: %lu, dir: %lu, dev: %lu, ipc: %lu, scd: %lu, user: %lu, process: %lu\n",
                   set_attr_count[T_FILE],
                   set_attr_count[T_DIR],
                   set_attr_count[T_DEV],
                   set_attr_count[T_IPC],
                   set_attr_count[T_SCD],
                   set_attr_count[T_USER],
                   set_attr_count[T_PROCESS]);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    len += sprintf(buffer + len,
                   "\nrsbac_remove_target calls:\nfile: %lu, dir: %lu, dev: %lu, ipc: %lu, scd: %lu, user: %lu, process: %lu\n",
                   remove_count[T_FILE],
                   remove_count[T_DIR],
                   remove_count[T_DEV],
                   remove_count[T_IPC],
                   remove_count[T_SCD],
                   remove_count[T_USER],
                   remove_count[T_PROCESS]);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }

out:
  *start = buffer + (offset - begin);
  len -= (offset - begin);
  
  if (len > length)
    len = length;
  return len;
}
#endif

#if defined(CONFIG_RSBAC_AUTO_WRITE) && (CONFIG_RSBAC_AUTO_WRITE > 0)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
static int
auto_write_proc_info(char *buffer, char **start, off_t offset, int length, int dummy)
#else
static int
auto_write_proc_info(char *buffer, char **start, off_t offset, int length)
#endif
{
  int len = 0;
  off_t pos   = 0;
  off_t begin = 0;

  union rsbac_target_id_t       rsbac_target_id;
  union rsbac_attribute_value_t rsbac_attribute_value;

  if (!rsbac_initialized) return (-ENOSYS);

  if (rsbac_debug_aef) printk(KERN_DEBUG "auto_write_proc_info(): calling ADF\n");
  rsbac_target_id.scd = ST_rsbac;
  rsbac_attribute_value.dummy = 0;
  if (!rsbac_adf_request(R_GET_STATUS_DATA,
                         current->pid,
                         T_SCD,
                         rsbac_target_id,
                         A_none,
                         rsbac_attribute_value))
    {
      return -EPERM;
    }

  len = sprintf(buffer, "RSBAC auto write settings\n-------------------------\n");
  pos = begin + len;
  if (pos < offset)
    {
      len = 0;
      begin = pos;
    }
    if (pos > offset+length)
      goto out;

  len += sprintf(buffer + len, "auto interval %u jiffies (%i jiffies = 1 second)\n",
                 auto_interval, HZ);
  pos = begin + len;
  if (pos < offset)
    {
      len = 0;
      begin = pos;
    }
    if (pos > offset+length)
      goto out;

  len += sprintf(buffer + len, "debug level is %i\n",
                 rsbac_debug_auto);
  pos = begin + len;
  if (pos < offset)
    {
      len = 0;
      begin = pos;
    }

out:
  *start = buffer + (offset - begin);
  len -= (offset - begin);
  
  if (len > length)
    len = length;
  return len;
}

static ssize_t auto_write_proc_write(struct file * file, const char * buf,
                                       u_long count, void *ppos)
{
    ssize_t err = -EINVAL;
    char * k_buf;
    char * p;

    union rsbac_target_id_t       rsbac_target_id;
    union rsbac_attribute_value_t rsbac_attribute_value;

    if(count > PROC_BLOCK_SIZE) {
	return(-EOVERFLOW);
    }

    if (!(k_buf = (char *) __get_free_page(GFP_KERNEL)))
      return(-ENOMEM);
    copy_from_user(k_buf, buf, count);

  if(count < 13 || strncmp("auto", k_buf, 4))
    {
      goto out;
    }
  if (!rsbac_initialized)
    {
      err=-ENOSYS;
      goto out;
    }

  if (rsbac_debug_aef) printk(KERN_DEBUG "auto_write_proc_write(): calling ADF\n");
  rsbac_target_id.scd = ST_rsbac;
  rsbac_attribute_value.dummy = 0;
  if (!rsbac_adf_request(R_MODIFY_SYSTEM_DATA,
                         current->pid,
                         T_SCD,
                         rsbac_target_id,
                         A_none,
                         rsbac_attribute_value))
    {
      err = -EPERM;
      goto out;
    }

    /*
     * Usage: echo "auto interval #N" > /proc/rsbac_info/auto_write
     *   to set auto_interval to given value
     */
    if(!strncmp("interval", k_buf + 5, 8)) 
    {
        unsigned int interval;

	p = k_buf + 5 + 9;

        if( *p == '\0' )
           goto out;

        interval = simple_strtoul(p, NULL, 0);
        /* only accept minimum of 1 second */
        if(interval >= HZ)
          {
            printk(KERN_INFO
                   "auto_write_proc_write(): setting auto write interval to %u\n",
                   interval);
            auto_interval = interval;
            err = count;
            goto out;
          }
        else
          {
            printk(KERN_INFO
                   "auto_write_proc_write(): rejecting too short auto write interval %u (min. %i)\n",
                   interval, HZ);
            goto out;
          }
    }
    /*
     * Usage: echo "auto debug #N" > /proc/rsbac_info/auto_write
     *   to set rsbac_debug_auto to given value
     */
    if(!strncmp("debug", k_buf + 5, 5)) 
    {
        unsigned int debug_level;

	p = k_buf + 5 + 6;

        if( *p == '\0' )
          goto out;

        debug_level = simple_strtoul(p, NULL, 0);
        /* only accept 0 or 1 */
        if(!debug_level || (debug_level == 1))
          {
            printk(KERN_INFO
                   "auto_write_proc_write(): setting rsbac_debug_auto to %u\n",
                   debug_level);
            rsbac_debug_auto = debug_level;
            err = count;
          }
        else
          {
            printk(KERN_INFO
                   "auto_write_proc_write(): rejecting invalid debug level (should be 0 or 1)\n");
          }
    }

out:
  free_page((ulong) k_buf);
  return(err);
}
#endif /* CONFIG_RSBAC_AUTO_WRITE > 0 */

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
static int
versions_proc_info(char *buffer, char **start, off_t offset, int length, int dummy)
#else
static int
versions_proc_info(char *buffer, char **start, off_t offset, int length)
#endif
{
  int len = 0;
  off_t pos   = 0;
  off_t begin = 0;

  union rsbac_target_id_t       rsbac_target_id;
  union rsbac_attribute_value_t rsbac_attribute_value;

  if (!rsbac_initialized) return (-ENOSYS);

  if (rsbac_debug_aef) printk(KERN_DEBUG "versions_proc_info(): calling ADF\n");
  rsbac_target_id.scd = ST_rsbac;
  rsbac_attribute_value.dummy = 0;
  if (!rsbac_adf_request(R_GET_STATUS_DATA,
                         current->pid,
                         T_SCD,
                         rsbac_target_id,
                         A_none,
                         rsbac_attribute_value))
    {
      return -EPERM;
    }

  len = sprintf(buffer,
                "RSBAC version settings (%s)\n----------------------\n",
                RSBAC_VERSION);
  pos = begin + len;
  if (pos < offset)
    {
      len = 0;
      begin = pos;
    }
    if (pos > offset+length)
      goto out;

  len += sprintf(buffer + len, "file/dir lists: aci version is %i, aci entry size is %i, %i lists per device\n",
                 RSBAC_FD_ACI_VERSION, sizeof(struct rsbac_file_dir_aci_t), RSBAC_NR_FD_LISTS);
  pos = begin + len;
  if (pos < offset)
    {
      len = 0;
      begin = pos;
    }
    if (pos > offset+length)
      goto out;

  len += sprintf(buffer + len, "dev list:       aci version is %i, no_write is %i, aci entry size is %i\n",
                 RSBAC_DEV_ACI_VERSION, dev_list_head.no_write, sizeof(struct rsbac_dev_aci_t));
  pos = begin + len;
  if (pos < offset)
    {
      len = 0;
      begin = pos;
    }
    if (pos > offset+length)
      goto out;

  len += sprintf(buffer + len, "user list:      aci version is %i, no_write is %i, aci entry size is %i\n",
                 RSBAC_USER_ACI_VERSION, user_list_head.no_write, sizeof(struct rsbac_user_aci_t));
  pos = begin + len;
  if (pos < offset)
    {
      len = 0;
      begin = pos;
    }
    if (pos > offset+length)
      goto out;

  len += sprintf(buffer + len, "log_level array: adf request version is %i, no_write is %i, array size is %i\n",
                 RSBAC_ADF_REQUEST_ARRAY_VERSION, log_levels_no_write, R_NONE * sizeof(enum rsbac_adf_request_t));
  pos = begin + len;
  if (pos < offset)
    {
      len = 0;
      begin = pos;
    }

out:
  *start = buffer + (offset - begin);
  len -= (offset - begin);
  
  if (len > length)
    len = length;
  return len;
}

static ssize_t versions_proc_write(struct file * file, const char * buf,
                                   u_long count, void *ppos)
{
    ssize_t err = -EINVAL;
    char * k_buf;
    char * p;
    unsigned int value;

    union rsbac_target_id_t       rsbac_target_id;
    union rsbac_attribute_value_t rsbac_attribute_value;

    if(count > PROC_BLOCK_SIZE) {
	return(-EOVERFLOW);
    }

    if (!(k_buf = (char *) __get_free_page(GFP_KERNEL)))
      return(-ENOMEM);
    copy_from_user(k_buf, buf, count);

  if(count < 12 || strncmp("no_write", k_buf, 8))
    {
      goto out;
    }
  if (!rsbac_initialized)
    {
      err=-ENOSYS;
      goto out;
    }

  if (rsbac_debug_aef) printk(KERN_DEBUG "versions_proc_write(): calling ADF\n");
  rsbac_target_id.scd = ST_rsbac;
  rsbac_attribute_value.dummy = 0;
  if (!rsbac_adf_request(R_MODIFY_SYSTEM_DATA,
                         current->pid,
                         T_SCD,
                         rsbac_target_id,
                         A_none,
                         rsbac_attribute_value))
    {
      err = -EPERM;
      goto out;
    }

    /*
     * Usage: echo "no_write dev #N" > /proc/rsbac_info/versions
     *   to set no_write to given value
     */
    if(!strncmp("dev", k_buf + 9, 3)) 
    {
	p = k_buf + 9 + 4;

        if( *p == '\0' )
          goto out;

        value = simple_strtoul(p, NULL, 0);
        /* only accept minimum of 1 second */
        if(!value || (value == 1))
          {
            printk(KERN_INFO
                   "versions_proc_write(): setting no_write for dev list to %u\n",
                   value);
            dev_list_head.no_write = value;
            err = count;
            goto out;
          }
        else
          {
            printk(KERN_INFO
                   "versions_proc_write(): rejecting wrong no_write value %u (should be 0 or 1)\n",
                   value);
            goto out;
          }
    }
    /*
     * Usage: echo "no_write user #N" > /proc/rsbac_info/versions
     *   to set no_write to given value
     */
    if(!strncmp("user", k_buf + 9, 4)) 
    {
	p = k_buf + 9 + 5;

        if( *p == '\0' )
          goto out;

        value = simple_strtoul(p, NULL, 0);
        /* only accept minimum of 1 second */
        if(!value || (value == 1))
          {
            printk(KERN_INFO
                   "versions_proc_write(): setting no_write for user list to %u\n",
                   value);
            user_list_head.no_write = value;
            err = count;
            goto out;
          }
        else
          {
            printk(KERN_INFO
                   "versions_proc_write(): rejecting wrong no_write value %u (should be 0 or 1)\n",
                   value);
            goto out;
          }
    }
    /*
     * Usage: echo "no_write log_levels #N" > /proc/rsbac_info/versions
     *   to set no_write to given value
     */
    if(!strncmp("log_levels", k_buf + 9, 10)) 
    {
	p = k_buf + 9 + 11;

        if( *p == '\0' )
          goto out;

        value = simple_strtoul(p, NULL, 0);
        /* only accept 0 or 1 */
        if(!value || (value == 1))
          {
            printk(KERN_INFO
                   "versions_proc_write(): setting no_write for log_levels array to %u\n",
                   value);
            log_levels_no_write = value;
            err = count;
            goto out;
          }
        else
          {
            printk(KERN_INFO
                   "versions_proc_write(): rejecting wrong no_write value %u (should be 0 or 1)\n",
                   value);
            goto out;
          }
    }

out:
  free_page((ulong) k_buf);
  return(err);
}

static int register_all_rsbac_proc(void)
  {
    struct proc_dir_entry * tmp_entry_p;

    proc_rsbac_root_p = create_proc_entry("rsbac-info",
                                          S_IFDIR | S_IRUGO | S_IXUGO,
                                          &proc_root);
    if(!proc_rsbac_root_p)
      return -RSBAC_ECOULDNOTADDITEM;

    proc_rsbac_backup_p = create_proc_entry("backup",
                                          S_IFDIR | S_IRUGO | S_IXUGO,
                                          proc_rsbac_root_p);
    if(!proc_rsbac_root_p)
      return -RSBAC_ECOULDNOTADDITEM;

    tmp_entry_p = create_proc_entry("devices",
                                    S_IFREG | S_IRUGO | S_IWUGO,
                                    proc_rsbac_root_p);
    if(!tmp_entry_p)
      return -RSBAC_ECOULDNOTADDITEM;
    tmp_entry_p->get_info = devices_proc_info;
    tmp_entry_p->write_proc = devices_proc_write;

    tmp_entry_p = create_proc_entry("stats",
                                    S_IFREG | S_IRUGO,
                                    proc_rsbac_root_p);
    if(!tmp_entry_p)
      return -RSBAC_ECOULDNOTADDITEM;
    tmp_entry_p->get_info = stats_proc_info;

    #ifdef CONFIG_RSBAC_XSTATS
    tmp_entry_p = create_proc_entry("xstats",
                                    S_IFREG | S_IRUGO,
                                    proc_rsbac_root_p);
    if(!tmp_entry_p)
      return -RSBAC_ECOULDNOTADDITEM;
    tmp_entry_p->get_info = xstats_proc_info;
    #endif
    #if defined(CONFIG_RSBAC_AUTO_WRITE) && (CONFIG_RSBAC_AUTO_WRITE > 0)
    tmp_entry_p = create_proc_entry("auto_write",
                                    S_IFREG | S_IRUGO | S_IWUGO,
                                    proc_rsbac_root_p);
    if(!tmp_entry_p)
      return -RSBAC_ECOULDNOTADDITEM;
    tmp_entry_p->get_info = auto_write_proc_info;
    tmp_entry_p->write_proc = auto_write_proc_write;
    #endif
    tmp_entry_p = create_proc_entry("versions",
                                    S_IFREG | S_IRUGO | S_IWUGO,
                                    proc_rsbac_root_p);
    if(!tmp_entry_p)
      return -RSBAC_ECOULDNOTADDITEM;
    tmp_entry_p->get_info = versions_proc_info;
    tmp_entry_p->write_proc = versions_proc_write;

    return(0);
  }

/*
static int unregister_all_rsbac_proc(void)
  {
    remove_proc_entry("devices", proc_rsbac_root_p);
    remove_proc_entry("stats", proc_rsbac_root_p);
    remove_proc_entry("auto-write", proc_rsbac_root_p);
    remove_proc_entry("backup", proc_rsbac_root_p);
    remove_proc_entry("rsbac-info", &proc_root);
    return(0);
  }
*/
#endif /* CONFIG_PROC_FS && CONFIG_RSBAC_PROC */


/************************************************* */
/*               Init functions                    */
/************************************************* */

/* All functions return 0, if no error occurred, and a negative error code  */
/* otherwise. The error codes are defined in rsbac_error.h.                 */

/************************************************************************** */
/* Initialization, including ACI restoration for all mounted devices from   */
/* disk. After this call, all ACI is kept in memory for performance reasons,*/
/* but user and file/dir object ACI are written to disk on every change.    */

/* Since there can be no access to aci data structures before init,         */
/* rsbac_init() will initialize all rw-spinlocks to unlocked.               */

int __init rsbac_init(void)
  {
    int err = 0;
    struct super_block * sb_p;
    struct task_struct * p;
    struct rsbac_device_list_item_t * device_p;
    struct rsbac_process_aci_t  process_aci;
    u_long flags;
    struct fs_struct * fs;

    if (rsbac_initialized)
      {
        printk(KERN_WARNING "rsbac_init(): RSBAC already initialized\n");
        return(-RSBAC_EREINIT);
      }
    fs = current->fs;
    if (!fs)
      {
        printk(KERN_WARNING "rsbac_init(): current->fs is invalid!\n");
        return(-RSBAC_EINVALIDPOINTER);
      }
    compiled_modules[0] = (char) 0;
#ifdef CONFIG_RSBAC_MAINT
    printk(KERN_INFO
           "rsbac_init(): Initializing RSBAC %s (Maintenance Mode)\n",
           RSBAC_VERSION);
    #ifdef CONFIG_RSBAC_PM_MAINT
    strcat(compiled_modules, " PM");
    #endif
    #ifdef CONFIG_RSBAC_RC_MAINT
    strcat(compiled_modules, " RC");
    #endif
    #ifdef CONFIG_RSBAC_AUTH_MAINT
    strcat(compiled_modules, " AUTH");
    #endif
    #ifdef CONFIG_RSBAC_ACL_MAINT
    strcat(compiled_modules, " ACL");
    #endif
    printk(KERN_INFO
           "rsbac_init(): Supported module data structures:%s\n",
           compiled_modules);
#else
    #ifdef CONFIG_RSBAC_MAC
    strcat(compiled_modules, " MAC");
    #endif
    #ifdef CONFIG_RSBAC_FC
    strcat(compiled_modules, " FC");
    #endif
    #ifdef CONFIG_RSBAC_SIM
    strcat(compiled_modules, " SIM");
    #endif
    #ifdef CONFIG_RSBAC_PM
    strcat(compiled_modules, " PM");
    #endif
    #ifdef CONFIG_RSBAC_MS
    strcat(compiled_modules, " MS");
    #endif
    #ifdef CONFIG_RSBAC_FF
    strcat(compiled_modules, " FF");
    #endif
    #ifdef CONFIG_RSBAC_RC
    strcat(compiled_modules, " RC");
    #endif
    #ifdef CONFIG_RSBAC_AUTH
    strcat(compiled_modules, " AUTH");
    #endif
    #ifdef CONFIG_RSBAC_REG
    strcat(compiled_modules, " REG");
    #endif
    #ifdef CONFIG_RSBAC_ACL
    strcat(compiled_modules, " ACL");
    #endif
    printk(KERN_INFO
           "rsbac_init(): Initializing RSBAC %s\n",
           RSBAC_VERSION);
    printk(KERN_INFO
           "rsbac_init(): compiled modules:%s\n",
           compiled_modules);
#endif
    /* set rw-spinlocks to unlocked status and init data structures */
    device_list_head.lock=RW_LOCK_UNLOCKED;
    device_list_head.head = NULL;
    device_list_head.tail = NULL;
    device_list_head.curr = NULL;
    device_list_head.count = 0;
    dev_list_head.lock=RW_LOCK_UNLOCKED;
    dev_list_head.head = NULL;
    dev_list_head.tail = NULL;
    dev_list_head.curr = NULL;
    dev_list_head.dirty = FALSE;
    dev_list_head.count = 0;
    dev_list_head.no_write = FALSE;
    ipc_list_head.lock=RW_LOCK_UNLOCKED;
    ipc_list_head.head = NULL;
    ipc_list_head.tail = NULL;
    ipc_list_head.curr = NULL;
    ipc_list_head.count = 0;
    user_list_head.lock=RW_LOCK_UNLOCKED;
    user_list_head.head = NULL;
    user_list_head.tail = NULL;
    user_list_head.curr = NULL;
    user_list_head.dirty = FALSE;
    user_list_head.count = 0;
    user_list_head.no_write = FALSE;
    process_list_head.lock=RW_LOCK_UNLOCKED;
    process_list_head.head = NULL;
    process_list_head.tail = NULL;
    process_list_head.curr = NULL;
    process_list_head.count = 0;

    #ifdef CONFIG_RSBAC_REG
    rsbac_root_dev = ROOT_DEV;
    #endif
    /* read fd aci from root device */
    sb_p = get_super(ROOT_DEV);
/*      lock_super(sb_p); */              /* do we need superblock locking? */
    if (rsbac_debug_ds)
      printk(KERN_DEBUG
             "rsbac_init(): reading aci from device number %u\n",
             sb_p->s_dev);
    /* wait for write access to device_list_head */
    rsbac_write_lock(&device_list_head.lock, &flags);
    /* OK, go on */
    #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
    device_p = add_device_item(sb_p->s_dev);
    #else
    device_p = add_device_item(sb_p->s_dev, NULL);
    #endif
    /* device was added, allow access */
    rsbac_write_unlock(&device_list_head.lock, &flags);
    if (!device_p)
      {
/*      unlock_super(sb_p); */
        printk(KERN_CRIT "%s\n","rsbac_init(): Could not add device!");
        return(-RSBAC_ECOULDNOTADDDEVICE);
      }
    /* wait for read access to device_list_head */
    rsbac_read_lock(&device_list_head.lock, &flags);
    /* caution: single list locking is done in the read function */
    if ( (err = read_fd_lists(device_p, ROOT_DEV) ) )
      {
        printk(KERN_WARNING
               "rsbac_init(): File/Dir ACI could not be fully read from device %02u:%02u!\n",
               MAJOR(ROOT_DEV), MINOR(ROOT_DEV));
      };
    /* allow access */
    rsbac_read_unlock(&device_list_head.lock, &flags);
/*  unlock_super(sb_p); */

    /* read dev aci from root device */      
    if (rsbac_debug_ds)
      printk(KERN_DEBUG "rsbac_init(): reading dev list\n");
    /* wait for write access to dev_list_head */
    rsbac_write_lock(&dev_list_head.lock, &flags);
    if (read_dev_list())
      {
        printk(KERN_WARNING "%s\n","rsbac_init(): Dev ACI could not be read!");
      };
    /* allow access */
    rsbac_write_unlock(&dev_list_head.lock, &flags);

    /* read user aci from root device */      
    if (rsbac_debug_ds)
      printk(KERN_DEBUG "rsbac_init(): reading user list\n");
    /* wait for write access to user_list_head */
    rsbac_write_lock(&user_list_head.lock, &flags);
    if ((err = read_user_list()))
      {
        printk(KERN_WARNING
        "rsbac_init(): User ACI could not be read - generating standard entries!\n");
        if(!lookup_user(RSBAC_SYSADM_UID))
          {
            struct rsbac_user_aci_t aci = DEFAULT_U_SYSADM_ACI;
            if(!add_user_item(aci))
              printk(KERN_WARNING
                     "rsbac_init(): SYSADM user entry could not be added!\n");
          }
        if(!lookup_user(RSBAC_BIN_UID))
          {
            struct rsbac_user_aci_t aci = DEFAULT_U_BIN_ACI;
            if(!add_user_item(aci))
              printk(KERN_WARNING
                     "rsbac_init(): BIN user entry could not be added!\n");
          }
        if(!lookup_user(RSBAC_SECOFF_UID))
          {
            struct rsbac_user_aci_t aci = DEFAULT_U_SECOFF_ACI;
            if(!add_user_item(aci))
              printk(KERN_WARNING
                     "rsbac_init(): SECOFF user entry could not be added!\n");
          }
        if(!lookup_user(RSBAC_DATAPROT_UID))
          {
            struct rsbac_user_aci_t aci = DEFAULT_U_DATAPROT_ACI;
            if(!add_user_item(aci))
              printk(KERN_WARNING
                     "rsbac_init(): DATAPROT user entry could not be added!\n");
          }
      };
    /* allow access */
    rsbac_write_unlock(&user_list_head.lock, &flags);

/* Add all processes to list of processes as init processes */
/* First init */
    process_aci = default_init_p_aci;
    process_aci.id = 1;
    rsbac_write_lock(&process_list_head.lock, &flags);
    if (!add_process_item(process_aci))
      printk(KERN_WARNING
             "rsbac_init(): ACI for Init process 1 could not be added!");
    read_lock(&tasklist_lock);
    for_each_task(p)
      {
        /* not for kernel though... */
        if(!p->pid || (p->pid == 1))
          continue;
        if (rsbac_debug_ds)
          printk(KERN_DEBUG "rsbac_init(): setting aci for process %u\n",
                 p->pid);
        process_aci = default_init_p_aci;
        process_aci.id = p->pid;
        if (!add_process_item(process_aci))
          printk(KERN_WARNING
                 "rsbac_init(): ACI for process %u could not be added!",
                 p->pid);
      }
    read_unlock(&tasklist_lock);
    rsbac_write_unlock(&process_list_head.lock, &flags);

#if defined(CONFIG_RSBAC_PROC) && defined(CONFIG_PROC_FS)
    printk(KERN_INFO "rsbac_init(): Registering RSBAC proc dir\n");
    register_all_rsbac_proc();
#endif

/* Call other init functions                                            */
    #ifdef CONFIG_RSBAC_PM
    rsbac_init_pm();
    #endif

    #if defined(CONFIG_RSBAC_RC) || defined(CONFIG_RSBAC_RC_MAINT)
    rsbac_init_rc();
    #endif

    #if defined(CONFIG_RSBAC_AUTH) || defined(CONFIG_RSBAC_AUTH_MAINT)
    rsbac_init_auth();
    if (rsbac_auth_enable_login)
      {
        struct dentry * t_dentry;
        #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
        struct dentry * dir_dentry;
        #endif
        struct rsbac_file_dir_list_item_t * fd_item_p;
        struct rsbac_file_dir_aci_t         fd_aci = default_fd_aci;
        struct rsbac_device_list_item_t   * a_device_p=NULL;

        printk(KERN_WARNING
               "rsbac_init(): auth_enable_login is set: setting auth_may_setuid for %s\n",
               "/bin/login");

        /* lookup filename */
        #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
        t_dentry = rsbac_lookup_dentry("/bin/login",NULL,0);
        if (IS_ERR(t_dentry))
          {
            err = PTR_ERR(t_dentry);
            printk(KERN_WARNING "rsbac_init(): call to lookup_dentry() returned %i\n", err);
            goto out;
          }
        #else
        dir_dentry = rsbac_lookup_one("bin", sb_p->s_root);
        if (IS_ERR(dir_dentry))
          {
            err = PTR_ERR(dir_dentry);
            printk(KERN_WARNING "rsbac_init(): call to rsbac_lookup_one for /bin returned %i\n", err);
            goto out;
          }
        t_dentry = rsbac_lookup_one("login", dir_dentry);
        /* dput(dir_dentry); */
        if (IS_ERR(t_dentry))
          {
            err = PTR_ERR(t_dentry);
            printk(KERN_WARNING "rsbac_init(): call to rsbac_lookup_one for /bin returned %i\n", err);
            goto out;
          }
        #endif

        if (!t_dentry->d_inode)
          {
            printk(KERN_WARNING "rsbac_init(): file not found\n");
            err = -RSBAC_EINVALIDTARGET;
            goto out_dput;
          }
        /* is inode of type file or block/char device? */
        if (!S_ISREG(t_dentry->d_inode->i_mode))
          {
            printk(KERN_WARNING "rsbac_init(): %s is no file\n", "/bin/login");
            err = -RSBAC_EINVALIDTARGET;
            goto out_dput;
          }
        fd_item_p = lookup_file_dir(t_dentry->d_inode->i_ino, device_p);

        if (!fd_item_p)
          { /* Not found -> add a new item with default values */
            if (rsbac_debug_ds) printk(KERN_DEBUG "rsbac_set_attr(): adding file/dir item!\n");
            /* fd_aci was initialized with default_fd_aci, now set id */
            fd_aci.id = t_dentry->d_inode->i_ino;
            a_device_p = lookup_device(t_dentry->d_inode->i_dev);
            /* and add item */
            fd_item_p = add_file_dir_item(fd_aci, a_device_p);
            /* if could not add -> return error */
            if (!fd_item_p)
              {
                printk(KERN_CRIT
                       "rsbac_init(): Could not add file/dir item!\n");
                err = -RSBAC_ECOULDNOTADDITEM;
                goto out_dput;
              }
          }
        /* OK, now we can do the attribute setting itself */
        fd_item_p->aci.auth_may_setuid = TRUE;
        if(a_device_p)
          a_device_p->head_table[fd_hash(t_dentry->d_inode->i_ino)].dirty = TRUE;

      out_dput:
        #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
        dput(t_dentry);
        #endif
      out:
      }
    #endif

    #if defined(CONFIG_RSBAC_ACL) || defined(CONFIG_RSBAC_ACL_MAINT)
    rsbac_init_acl();
    #endif

    rsbac_init_debug();
    rsbac_init_adf();

#if defined(CONFIG_RSBAC_AUTO_WRITE) && (CONFIG_RSBAC_AUTO_WRITE > 0)
/* Start rsbac thread for auto write */
    printk(KERN_INFO "rsbac_init(): Starting rsbacd thread\n");
    kernel_thread(rsbacd, "rsbacd", CLONE_FS|CLONE_FILES|CLONE_SIGHAND);
    printk(KERN_INFO "rsbac_init(): Setting RSBAC auto timer\n");
    init_timer(&rsbac_timer);
    rsbac_timer.function = wakeup_rsbacd;
    rsbac_timer.data = 0;
    rsbac_timer.expires = jiffies + auto_interval;
    add_timer(&rsbac_timer);
#endif /* CONFIG_RSBAC_AUTO_WRITE > 0 */

/* Tell that rsbac is initialized                                       */    
    rsbac_initialized = TRUE;

/* Force a check, if configured */
#ifdef CONFIG_RSBAC_INIT_CHECK
    printk(KERN_INFO "rsbac_init(): Forcing consistency check.\n");
    rsbac_check(1,1);
    /* call other checks */
    #if defined(CONFIG_RSBAC_AUTH) || defined(CONFIG_RSBAC_AUTH_MAINT)
    rsbac_check_auth(1,1);
    #endif
    #if defined(CONFIG_RSBAC_ACL) || defined(CONFIG_RSBAC_ACL_MAINT)
    rsbac_check_acl(1,1);
    #endif
#endif

/* Ready. */
    printk(KERN_INFO "rsbac_init(): Ready.\n");
    return(err);
  };

/* To switch RSBAC off on shutdown: */

void rsbac_off(void)
  {
    u_long flags;

    if (!rsbac_initialized)
      {
        printk(KERN_WARNING "rsbac_off(): RSBAC not initialized\n");
      }
    else
      {
        struct rsbac_device_list_item_t * device_p;
        struct rsbac_device_list_item_t * next_p;

        /* wait for write access to device_list_head */
        rsbac_write_lock(&device_list_head.lock, &flags);
        /* OK, nobody else is working on it... */
        device_p = device_list_head.head;
        while(device_p)
          {
            /* -> remove device */
            next_p = device_p->next;
            remove_device_item(device_p->id);
            device_p = next_p;
          }
        rsbac_initialized = FALSE;
        /* allow access */
        rsbac_write_unlock(&device_list_head.lock, &flags);
      }
  };

/* To check, whether RSBAC is already initialized: */

#ifdef CONFIG_RSBAC_REG
EXPORT_SYMBOL(rsbac_is_initialized);
#endif
inline boolean rsbac_is_initialized(void)
  {
    return rsbac_initialized;
  };

/* When mounting a device, its ACI must be read and added to the ACI lists. */

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
int rsbac_mount(kdev_t kdev)
#else
int rsbac_mount(kdev_t kdev, struct dentry * d_covers)
#endif
  {
    int err = 0;
    struct rsbac_device_list_item_t * device_p;
    u_long flags;
    #ifdef CONFIG_RSBAC_REG
    int i;
    #endif

    if (!rsbac_initialized)
      {
        printk(KERN_WARNING "rsbac_mount(): RSBAC not initialized\n");
/*        return(rsbac_init()); */
          return(-RSBAC_ENOTINITIALIZED);
      }
    if (rsbac_debug_ds)
      printk(KERN_DEBUG "rsbac_mount(): mounting device %02u:%02u\n",
             MAJOR(kdev), MINOR(kdev));
    /* wait for write access to device_list_head */
    rsbac_write_lock(&device_list_head.lock, &flags);
    /* OK, go on */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
    device_p = add_device_item(kdev); 
#else
    device_p = add_device_item(kdev, d_covers); 
#endif
    /* device was added, allow access */
    rsbac_write_unlock(&device_list_head.lock, &flags);
    if(device_p)
      {
        /* wait for read access to device_list_head */
        rsbac_read_lock(&device_list_head.lock, &flags);
        /* read attributes */
        err=read_fd_lists(device_p,kdev);
        /* free access */
        rsbac_read_unlock(&device_list_head.lock, &flags);
        /* call other mount functions */
        #if defined(CONFIG_RSBAC_AUTH) || defined(CONFIG_RSBAC_AUTH_MAINT)
        rsbac_mount_auth(kdev);
        #endif
        /****** REG *******/
        #ifdef CONFIG_RSBAC_REG
        for(i=0;i<RSBAC_REG_NR_ENTRIES;i++)
          {
            if(rsbac_reg_entry[i].mount_func)
              {
                err |= rsbac_reg_entry[i].mount_func(kdev);
              }
          }
        #endif  /* REG */
        #if defined(CONFIG_RSBAC_ACL) || defined(CONFIG_RSBAC_ACL_MAINT)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
        rsbac_mount_acl(kdev);
#else
        rsbac_mount_acl(kdev, d_covers);
#endif
        #endif
      }
    else
      /* error: */
      err=-RSBAC_ECOULDNOTADDDEVICE;

    return(err);
  };
  
/* When umounting a device, its ACI must be removed from the ACI lists.     */
/* Removing the device ACI should be no problem.                            */

int rsbac_umount(kdev_t kdev)
  {
    u_long flags;
    #ifdef CONFIG_RSBAC_REG
    int i;
    #endif

    if (!rsbac_initialized)
      {
        printk(KERN_WARNING "rsbac_umount(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }

    if (rsbac_debug_ds)
      printk(KERN_DEBUG "rsbac_umount(): umounting device %02u:%02u\n",
             MAJOR(kdev), MINOR(kdev));
    /* sync attribute lists */
#if defined(CONFIG_RSBAC_AUTO_WRITE)
    rsbac_write(FALSE);
#endif /* CONFIG_RSBAC_AUTO_WRITE */
    /* call other umount functions */
    #if defined(CONFIG_RSBAC_AUTH) || defined(CONFIG_RSBAC_AUTH_MAINT)
    rsbac_umount_auth(kdev);
    #endif
    /****** REG *******/
    #ifdef CONFIG_RSBAC_REG
    for(i=0;i<RSBAC_REG_NR_ENTRIES;i++)
      {
        if(rsbac_reg_entry[i].umount_func)
          {
            rsbac_reg_entry[i].umount_func(kdev);
          }
      }
    #endif  /* REG */
    #if defined(CONFIG_RSBAC_ACL) || defined(CONFIG_RSBAC_ACL_MAINT)
    rsbac_umount_acl(kdev);
    #endif
    /* wait for write access to device_list_head */
    rsbac_write_lock(&device_list_head.lock, &flags);
    /* OK, nobody else is working on it... */
      /* -> remove device */
      remove_device_item(kdev);
      /* allow access */
    rsbac_write_unlock(&device_list_head.lock, &flags);
    return(0);
  };

/***************************************************/
/* We also need some status information...         */

int rsbac_stats(void)
  {
    struct rsbac_device_list_item_t   * device_p;
    int                                 i;
    u_long                              fd_count = 0, fd_sum = 0;
    u_int                               fd_dirty = 0, dirty = 0;
    u_long                              dflags;
  
    if (!rsbac_initialized)
      {
        printk(KERN_WARNING "rsbac_stats(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    /* wait for read access to device_list_head */
    rsbac_read_lock(&device_list_head.lock, &dflags);
    /* OK, go on */
/*    printk(KERN_INFO "rsbac_stats(): currently %u processes working on file/dir aci\n",
                     device_list_head.lock.lock); */
    device_p = device_list_head.head;
    while (device_p)
      { /* for all sublists */
        fd_count = 0;
        fd_dirty = 0;
        for (i=0; i < RSBAC_NR_FD_LISTS; i++)
          {
            if(device_p->head_table[i].dirty)
              fd_dirty++;
            fd_count += device_p->head_table[i].count;
          };
        printk(KERN_INFO "rsbac_stats(): Device %02u:%02u has %lu fd-items and %u dirty lists\n",
               MAJOR(device_p->id), MINOR(device_p->id), fd_count, fd_dirty);
        fd_sum += fd_count;
        dirty += fd_dirty;
        device_p = device_p->next;
      };
    printk(KERN_INFO "rsbac_stats(): Sum of %u Devices with %lu fd-items\n",
                 device_list_head.count, fd_sum);
    /* free access to device_list_head */
    rsbac_read_unlock(&device_list_head.lock, &dflags);
    
    /* dev list */
    if(dev_list_head.dirty)
      {
        dirty++;
        printk(KERN_INFO
               "rsbac_stats(): %u dev-items (list is dirty)\n",
               dev_list_head.count);
      }
    else
      {
        printk(KERN_INFO
               "rsbac_stats(): %u dev-items\n",
               dev_list_head.count);
      }
    
    /* ipc list */
    printk(KERN_INFO "rsbac_stats(): %lu ipc-items\n",
                 ipc_list_head.count);
    
    /* user list */
    if(user_list_head.dirty)
      {
        dirty++;
        printk(KERN_INFO
               "rsbac_stats(): %u user-items (list is dirty)\n",
               user_list_head.count);
      }
    else
      {
        printk(KERN_INFO
               "rsbac_stats(): %u user-items\n",
               user_list_head.count);
      }
    
    /* process list */
    printk(KERN_INFO "rsbac_stats(): %lu process-items\n",
                 process_list_head.count);
    
    printk(KERN_INFO
           "rsbac_stats(): Total of %lu registered rsbac-items, %u lists dirty\n",
           fd_sum + dev_list_head.count + ipc_list_head.count
             + user_list_head.count + process_list_head.count,
           dirty);

    printk(KERN_INFO
           "adf_request calls: file: %lu, dir: %lu, dev: %lu, ipc: %lu, scd: %lu, user: %lu, process: %lu\n",
           rsbac_adf_request_count[T_FILE],
           rsbac_adf_request_count[T_DIR],
           rsbac_adf_request_count[T_DEV],
           rsbac_adf_request_count[T_IPC],
           rsbac_adf_request_count[T_SCD],
           rsbac_adf_request_count[T_USER],
           rsbac_adf_request_count[T_PROCESS]);
    printk(KERN_INFO
           "adf_set_attr calls: file: %lu, dir: %lu, dev: %lu, ipc: %lu, scd: %lu, user: %lu, process: %lu\n",
           rsbac_adf_set_attr_count[T_FILE],
           rsbac_adf_set_attr_count[T_DIR],
           rsbac_adf_set_attr_count[T_DEV],
           rsbac_adf_set_attr_count[T_IPC],
           rsbac_adf_set_attr_count[T_SCD],
           rsbac_adf_set_attr_count[T_USER],
           rsbac_adf_set_attr_count[T_PROCESS]);

    #if defined(CONFIG_RSBAC_PM)
    rsbac_stats_pm();
    #endif
    #if defined(CONFIG_RSBAC_RC) || defined(CONFIG_RSBAC_RC_MAINT)
    rsbac_stats_rc();
    #endif
    #if defined(CONFIG_RSBAC_AUTH) || defined(CONFIG_RSBAC_AUTH_MAINT)
    rsbac_stats_auth();
    #endif
    #if defined(CONFIG_RSBAC_ACL) || defined(CONFIG_RSBAC_ACL_MAINT)
    rsbac_stats_acl();
    #endif
    return(0);
  };

/***************************************************/
/* consistency checking (as far as possible)       */

int rsbac_check(int correct, int check_inode)
  {
    struct rsbac_device_list_item_t   * device_p;
    int                                 i;
    boolean                             no_write;
    u_long                              fd_count = 0, fd_sum = 0, tmp_count,
                                        r_count, u_count, b_count, def_count, wrong_count;
    u_int                               fd_dirty = 0, dirty = 0;
    u_long                              flags, dflags;
    struct super_block                * sb_p;
    struct inode                      * inode_p;
    struct rsbac_file_dir_list_item_t * fd_item_p, * fd_item_tmp_p;
    struct rsbac_file_dir_aci_t         def_fd_aci = DEFAULT_FD_ACI;
    struct rsbac_dev_list_item_t      * d_item_p;
    struct rsbac_ipc_list_item_t      * i_item_p;
    struct rsbac_user_list_item_t     * u_item_p;
    struct rsbac_process_list_item_t  * p_item_p, * p_item_tmp_p;
    struct task_struct                * task_p;
    struct rsbac_file_dir_list_head_t * head_p;
  
    if (!rsbac_initialized)
      {
        printk(KERN_WARNING "rsbac_check(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    /* wait for read access to device_list_head */
    rsbac_read_lock(&device_list_head.lock, &dflags);
    /* OK, go on */
    device_p = device_list_head.head;
    while (device_p)
      {
        fd_count = 0;
        r_count = 0;
        u_count = 0;
        b_count = 0;
        def_count = 0;
        wrong_count = 0;
        fd_dirty = 0;
        /* let no new write start on device */
        no_write = device_p->no_write;
        device_p->no_write = TRUE;
        if(check_inode)
          {
            sb_p = get_super(device_p->id);
            if(!sb_p)
              {
                printk(KERN_WARNING "rsbac_check(): no super block for device %02u:%02u!\n",
                       MAJOR(device_p->id), MINOR(device_p->id));
              }
            else
              {
                /* is last write finished? (only, if writable) */
                if(writable(sb_p))
                  {
                    /* sync to be sure */
                    printk(KERN_INFO "rsbac_check(): syncing device %02u:%02u\n",
                           MAJOR(device_p->id), MINOR(device_p->id));
                    fsync_dev(device_p->id);
                  }
              }
          }
        else
          sb_p = NULL;

        /* OK, go ahead - for all sublists */
        for (i=0; i < RSBAC_NR_FD_LISTS; i++)
          {
            tmp_count = 0;
            head_p = &device_p->head_table[i];
            rsbac_write_lock(&head_p->lock, &flags);
            fd_item_p = head_p->head;
            while(fd_item_p)
              {
                /* check, whether entry is in correct list */
                if(fd_hash(fd_item_p->aci.id) != i)
                  {
                    wrong_count++;
                    if(correct)
                      {
                        if(rsbac_debug_ds)
                          printk(KERN_DEBUG
                                 "rsbac_check(): fd_item for inode %lu on device %02u:%02u, list %u, should be in list %u, removing!\n",
                                 fd_item_p->aci.id, MAJOR(device_p->id), MINOR(device_p->id),
                                 i, fd_hash(fd_item_p->aci.id));
                        fd_item_tmp_p = fd_item_p;
                        fd_item_p = fd_item_p->next;
                        remove_file_dir_item(fd_item_tmp_p->aci.id, device_p);
                        head_p->dirty = TRUE;
                        continue;
                      }
                    else
                      {
                        if(rsbac_debug_ds)
                          printk(KERN_DEBUG
                                 "rsbac_check(): fd_item for inode %lu on device %02u:%02u, list %u, should be in list %u!\n",
                                 fd_item_p->aci.id, MAJOR(device_p->id), MINOR(device_p->id),
                                 i, fd_hash(fd_item_p->aci.id));
                      }
                  }

                /* check for inode on disk */
                if(sb_p)
                  {
                    inode_p = iget(sb_p, fd_item_p->aci.id);
                    if(is_bad_inode(inode_p))
                      { /* inode is bad -> remove */
                        b_count++;
                        if(correct)
                          {
                            printk(KERN_INFO
                                   "rsbac_check(): fd_item for bad inode %lu on device %02u:%02u, list %u, removing!\n",
                                   fd_item_p->aci.id, MAJOR(device_p->id), MINOR(device_p->id), i);
                            fd_item_tmp_p = fd_item_p;
                            fd_item_p = fd_item_p->next;
                            head_p->curr = fd_item_tmp_p;
                            do_remove_file_dir_item(fd_item_tmp_p->aci.id, head_p, device_p->id);
                            head_p->dirty = TRUE;
                            continue;
                          }
                        else
                          {
                            printk(KERN_INFO
                                   "rsbac_check(): fd_item for bad inode %lu on device %02u:%02u, list %u!\n",
                                   fd_item_p->aci.id, MAJOR(device_p->id), MINOR(device_p->id), i);
                          }
                      } /* end of bad_inode */
                    else
                      { /* good inode */
                        /* currently only deletion checking of ext2 inodes is possible */
                        if(sb_p->s_magic == EXT2_SUPER_MAGIC)
                          {
                            if(inode_p->u.ext2_i.i_dtime)
                              { /* inode has been deleted -> remove */
                                r_count++;
                                if(correct)
                                  {
                                    printk(KERN_INFO
                                           "rsbac_check(): fd_item for deleted inode %lu on device %02u:%02u, list %u, removing!\n",
                                           fd_item_p->aci.id, MAJOR(device_p->id), MINOR(device_p->id), i);
                                    fd_item_tmp_p = fd_item_p;
                                    fd_item_p = fd_item_p->next;
                                    head_p->curr = fd_item_tmp_p;
                                    do_remove_file_dir_item(fd_item_tmp_p->aci.id, head_p, device_p->id);
                                    head_p->dirty = TRUE;
                                    continue;
                                  }
                                else
                                  {
                                    printk(KERN_INFO
                                           "rsbac_check(): fd_item for deleted inode %lu on device %02u:%02u, list %u!\n",
                                           fd_item_p->aci.id, MAJOR(device_p->id), MINOR(device_p->id), i);
                                  }
                              }
                            else
                              {
                                if(inode_p->i_nlink <= 0)
                                  { /* inode has been unlinked, but no dtime is set -> warn */
                                    u_count++;
                                    if(correct >= 2)
                                      {
                                        printk(KERN_INFO
                                               "rsbac_check(): fd_item for inode %lu with nlink <= 0 on device %02u:%02u, list %u, removing!\n",
                                               fd_item_p->aci.id, MAJOR(device_p->id), MINOR(device_p->id), i);
                                        fd_item_tmp_p = fd_item_p;
                                        fd_item_p = fd_item_p->next;
                                        head_p->curr = fd_item_tmp_p;
                                        do_remove_file_dir_item(fd_item_tmp_p->aci.id, head_p, device_p->id);
                                        head_p->dirty = TRUE;
                                        continue;
                                      }
                                    else
                                      {
                                        printk(KERN_INFO
                                               "rsbac_check(): deleted inode %lu on device %02u:%02u, list %u, has no dtime!\n",
                                               fd_item_p->aci.id, MAJOR(device_p->id), MINOR(device_p->id), i);
                                      }
                                  }
                              }
                          }
                      } /* end of is_good_inode */
                    iput(inode_p);
                  } /* end of sb_p */

                /* if all values for this item (apart from id) are default values,
                   remove it */
                def_fd_aci.id = fd_item_p->aci.id;
                if(!memcmp(&def_fd_aci,
                           &fd_item_p->aci,
                           sizeof(def_fd_aci)
                          )
                  )
                  {
                    def_count++;
                    if(correct)
                      {
                        if(rsbac_debug_ds)
                          printk(KERN_DEBUG
                                 "rsbac_check(): fd_item for inode %lu on device %02u:%02u, list %u, has only default values, removing!\n",
                                 fd_item_p->aci.id, MAJOR(device_p->id), MINOR(device_p->id), i);
                        fd_item_tmp_p = fd_item_p;
                        fd_item_p = fd_item_p->next;
                        remove_file_dir_item(fd_item_tmp_p->aci.id, device_p);
                        head_p->dirty = TRUE;
                        continue;
                      }
                    else
                      {
                        if(rsbac_debug_ds)
                          printk(KERN_DEBUG
                                 "rsbac_check(): fd_item for inode %lu on device %02u:%02u, list %u, has only default values!\n",
                                 fd_item_p->aci.id, MAJOR(device_p->id), MINOR(device_p->id), i);
                      }
                  }
                tmp_count++;
                fd_item_p = fd_item_p->next;
              }
            if(tmp_count != head_p->count)
              {
                printk(KERN_WARNING
                       "rsbac_check(): correcting count mismatch for device %02u:%02u, list %u - was %lu, counted %lu!\n",
                       MAJOR(device_p->id), MINOR(device_p->id), i, head_p->count, tmp_count);
                head_p->count = tmp_count;
              }
            if(head_p->dirty)
              fd_dirty++;
            rsbac_write_unlock(&head_p->lock, &flags);
            fd_count += head_p->count;
          }
        switch(correct)
          {
            case 2:  printk(KERN_INFO
                            "rsbac_check(): Device %02u:%02u has %lu fd-items (%lu removed (%lu on wrong list, %lu bad inodes, %lu dtimed inodes, %lu unlinked inodes, %lu had only default values)) and %u dirty lists\n",
                            MAJOR(device_p->id), MINOR(device_p->id), fd_count, wrong_count + b_count + r_count + u_count + def_count,
                            wrong_count, b_count, r_count, u_count, def_count, fd_dirty);
                            break;
            case 1:  printk(KERN_INFO
                            "rsbac_check(): Device %02u:%02u has %lu fd-items (%lu removed (%lu on wrong list, %lu bad inodes, %lu dtimed inodes, %lu had only default values), %lu unlinked inodes) and %u dirty lists\n",
                            MAJOR(device_p->id), MINOR(device_p->id), fd_count, wrong_count + b_count + r_count + def_count,
                            wrong_count, b_count, r_count, def_count, u_count, fd_dirty);
                            break;
            default: printk(KERN_INFO
                            "rsbac_check(): Device %02u:%02u has %lu fd-items (%lu on wrong list, %lu with bad inodes, %lu with dtimed inodes, %lu unlinked inodes, %lu with default values) and %u dirty lists\n",
                            MAJOR(device_p->id), MINOR(device_p->id), fd_count,
                            wrong_count, b_count, r_count, u_count, def_count, fd_dirty);
          }
        fd_sum += fd_count;
        dirty += fd_dirty;
        /* set no_write back to old state */
        device_p->no_write = no_write;
        /* go on */
        device_p = device_p->next;
      };
    printk(KERN_INFO "rsbac_check(): Sum of %u Devices with %lu fd-items\n",
                 device_list_head.count, fd_sum);
    /* free access to device_list_head */
    rsbac_read_unlock(&device_list_head.lock, &dflags);
    
    /* dev list */
    rsbac_write_lock(&dev_list_head.lock, &flags);
    tmp_count = 0;
    d_item_p = dev_list_head.head;
    while(d_item_p)
      {
        d_item_p = d_item_p->next;
        tmp_count++;
      }
    if(tmp_count != dev_list_head.count)
      {
        printk(KERN_WARNING
               "rsbac_check(): correcting count mismatch for dev list - was %u, counted %lu!\n",
               dev_list_head.count, tmp_count);
        dev_list_head.count = tmp_count;
      }
    rsbac_write_unlock(&dev_list_head.lock, &flags);
    if(dev_list_head.dirty)
      {
        dirty++;
        printk(KERN_INFO
               "rsbac_check(): %u dev-items (list is dirty)\n",
               dev_list_head.count);
      }
    else
      {
        printk(KERN_INFO
               "rsbac_check(): %u dev-items\n",
               dev_list_head.count);
      }
    
    /* ipc list */
    rsbac_write_lock(&ipc_list_head.lock, &flags);
    tmp_count = 0;
    i_item_p = ipc_list_head.head;
    while(i_item_p)
      {
        i_item_p = i_item_p->next;
        tmp_count++;
      }
    if(tmp_count != ipc_list_head.count)
      {
        printk(KERN_WARNING
               "rsbac_check(): correcting count mismatch for ipc list - was %lu, counted %lu!\n",
               ipc_list_head.count, tmp_count);
        ipc_list_head.count = tmp_count;
      }
    rsbac_write_unlock(&ipc_list_head.lock, &flags);
    printk(KERN_INFO "rsbac_check(): %lu ipc-items\n",
                 ipc_list_head.count);
    
    /* user list */
    rsbac_write_lock(&user_list_head.lock, &flags);
    tmp_count = 0;
    u_item_p = user_list_head.head;
    while(u_item_p)
      {
        u_item_p = u_item_p->next;
        tmp_count++;
      }
    if(tmp_count != user_list_head.count)
      {
        printk(KERN_WARNING
               "rsbac_check(): correcting count mismatch for user list - was %u, counted %lu!\n",
               user_list_head.count, tmp_count);
        user_list_head.count = tmp_count;
      }
    rsbac_write_unlock(&user_list_head.lock, &flags);
    if(user_list_head.dirty)
      {
        dirty++;
        printk(KERN_INFO
               "rsbac_check(): %u user-items (list is dirty)\n",
               user_list_head.count);
      }
    else
      {
        printk(KERN_INFO
               "rsbac_check(): %u user-items\n",
               user_list_head.count);
      }
    
    /* process list */
    rsbac_write_lock(&process_list_head.lock, &flags);
    tmp_count = 0;
    p_item_p = process_list_head.head;
    while(p_item_p)
      {
        read_lock(&tasklist_lock);
        task_p = find_task_by_pid(p_item_p->aci.id);
        read_unlock(&tasklist_lock);
        if(!task_p)
          {
            p_item_tmp_p = p_item_p;
            p_item_p = p_item_p->next;
            if(correct)
              {
                printk(KERN_WARNING
                       "rsbac_check(): process item has invalid id %u, removed!\n",
                       p_item_tmp_p->aci.id);
                remove_process_item(p_item_tmp_p->aci.id);
                continue;
              }
            else
              {
                printk(KERN_WARNING
                       "rsbac_check(): process item has invalid id %u!\n",
                       p_item_tmp_p->aci.id);
              }
          }
        else
          {
            p_item_p = p_item_p->next;
          }
        tmp_count++;
      }
    if(tmp_count != process_list_head.count)
      {
        printk(KERN_WARNING
               "rsbac_check(): correcting count mismatch for process list - was %lu, counted %lu!\n",
               process_list_head.count, tmp_count);
        process_list_head.count = tmp_count;
      }
    rsbac_write_unlock(&process_list_head.lock, &flags);
    printk(KERN_INFO "rsbac_check(): %lu process-items\n",
                 process_list_head.count);
    
    printk(KERN_INFO
           "rsbac_check(): Total of %lu registered rsbac-items, %u lists dirty\n",
           fd_sum + dev_list_head.count + ipc_list_head.count
             + user_list_head.count + process_list_head.count,
           dirty);

    return(0);
  };

/***************************************************/
/* rsbac_write() to write all dirty lists to disk  */
/*               returns no. of lists written      */

#if defined(CONFIG_RSBAC_AUTO_WRITE)
int rsbac_write(boolean need_lock)
  {
    struct rsbac_device_list_item_t   * device_p;
    int                                 i,err=0;
    u_int                               count = 0;
    int                                 subcount;
    u_long                              dflags;
  
    if (!rsbac_initialized)
      {
        printk(KERN_WARNING "rsbac_write(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    /* wait for read access to device_list_head */
    rsbac_read_lock(&device_list_head.lock, &dflags);
    /* OK, go on */
/*    printk(KERN_INFO "rsbac_write(): currently %u processes working on file/dir aci\n",
                     device_list_head.lock.lock); */
    device_p = device_list_head.head;
    while (device_p)
      { /* for all sublists */
        for (i=0; i < RSBAC_NR_FD_LISTS; i++)
          {
            if(device_p->head_table[i].dirty)
              {
                if(need_lock)
                  lock_kernel();
                device_p->head_table[i].dirty = FALSE;
                err=write_fd_list(i, &(device_p->head_table[i]), device_p);
                if(need_lock)
                  unlock_kernel();
                if(!err)
                  {
                    count++;
                  }
                else
                  {
                    device_p->head_table[i].dirty = TRUE;
                    if(err != -RSBAC_ENOTWRITABLE)
                      printk(KERN_WARNING
                             "rsbac_write(): write_fd_list() returned error %i for list %u on device %02u:%02u\n",
                             err,
                             i,
                             MAJOR(device_p->id),
                             MINOR(device_p->id));
                  }
              }
          };
        device_p = device_p->next;
      };
    if(rsbac_debug_write)
      printk(KERN_DEBUG "rsbac_write(): %u fd lists written\n",
             count);
    /* free access to device_list_head */
    rsbac_read_unlock(&device_list_head.lock, &dflags);
    
    if(dev_list_head.dirty)
      {
        if(need_lock)
          lock_kernel();
        dev_list_head.dirty = FALSE;
        err=write_dev_list();
        if(need_lock)
          unlock_kernel();
        if(!err)
          {
            count++;
            if(rsbac_debug_write)
              printk(KERN_DEBUG "rsbac_write(): dev list written\n");
          }
        else
          {
            dev_list_head.dirty = TRUE;
            if(err != -RSBAC_ENOTWRITABLE)
              printk(KERN_WARNING
                     "rsbac_write(): write_dev_list() returned error %i\n",
                     err);
          }
      }

    /* user list */
    if(user_list_head.dirty)
      {
        if(need_lock)
          lock_kernel();
        user_list_head.dirty = FALSE;
        err=write_user_list();
        if(need_lock)
          unlock_kernel();
        if(!err)
          {
            count++;
            if(rsbac_debug_write)
              printk(KERN_DEBUG "rsbac_write(): user list written\n");
          }
        else
          {
            user_list_head.dirty = TRUE;
            if(err != -RSBAC_ENOTWRITABLE)
              printk(KERN_WARNING
                     "rsbac_write(): write_user_list() returned error %i\n",
                     err);
          }
      }
    
    subcount = rsbac_write_debug(need_lock);
    if(subcount > 0)
      {
        count += subcount;
      }
    if(subcount < 0)
      {
        err = subcount;
        if(err != -RSBAC_ENOTWRITABLE)
          printk(KERN_WARNING
                 "rsbac_write(): rsbac_write_debug() returned error %i\n",
                 err);
      }

#ifdef CONFIG_RSBAC_PM
    subcount = rsbac_write_pm(need_lock);
    if(subcount > 0)
      {
        count += subcount;
      }
    if(subcount < 0)
      {
        err = subcount;
        if(err != -RSBAC_ENOTWRITABLE)
          printk(KERN_WARNING
                 "rsbac_write(): rsbac_write_pm() returned error %i\n",
                 err);
      }
#endif

#if defined(CONFIG_RSBAC_RC) || defined(CONFIG_RSBAC_RC_MAINT)
    subcount = rsbac_write_rc(need_lock);
    if(subcount > 0)
      {
        count += subcount;
      }
    if(subcount < 0)
      {
        err = subcount;
        if(err != -RSBAC_ENOTWRITABLE)
          printk(KERN_WARNING
                 "rsbac_write(): rsbac_write_rc() returned error %i\n",
                 err);
      }
#endif

#if defined(CONFIG_RSBAC_AUTH) || defined(CONFIG_RSBAC_AUTH_MAINT)
    subcount = rsbac_write_auth(need_lock);
    if(subcount > 0)
      {
        count += subcount;
      }
    if(subcount < 0)
      {
        err = subcount;
        if(err != -RSBAC_ENOTWRITABLE)
          printk(KERN_WARNING
                 "rsbac_write(): rsbac_write_auth() returned error %i\n",
                 err);
      }
#endif

/****** REG *******/
#ifdef CONFIG_RSBAC_REG
for(i=0;i<RSBAC_REG_NR_ENTRIES;i++)
  {
    if(rsbac_reg_entry[i].write_func)
      {
        subcount = rsbac_reg_entry[i].write_func(need_lock);
        if(subcount > 0)
          {
            count += subcount;
          }
        if(subcount < 0)
          {
            err = subcount;
            if(err != -RSBAC_ENOTWRITABLE)
              printk(KERN_WARNING
                     "rsbac_write(): write_func() for REG module %s returned error %i\n",
                     rsbac_reg_entry[i].name, err);
          }
      }
  }
#endif  /* REG */

#if defined(CONFIG_RSBAC_ACL) || defined(CONFIG_RSBAC_ACL_MAINT)
    subcount = rsbac_write_acl(need_lock);
    if(subcount > 0)
      {
        count += subcount;
      }
    if(subcount < 0)
      {
        err = subcount;
        if(err != -RSBAC_ENOTWRITABLE)
          printk(KERN_WARNING
                 "rsbac_write(): rsbac_write_acl() returned error %i\n",
                 err);
      }
#endif

    if(rsbac_debug_write)
      printk(KERN_DEBUG "rsbac_write(): total of %u lists written\n",
             count);
    return(count);
  };
#endif /* CONFIG_RSBAC_AUTO_WRITE */

/************************************************* */
/*               Attribute functions               */
/************************************************* */

/* A rsbac_set_attr() call for a non-existing object, user                  */
/* or process entry will first add the target and then set the attribute.   */
/* Invalid combinations and trying to set security_level to or from         */
/* SL_rsbac_internal return an error.                                       */
/* A rsbac_get_attr() call for a non-existing target will return the        */
/* default value stored in default_aci, which should be the first enum item.*/

/* All these procedures handle the rw-spinlocks to protect the targets during */
/* access.                                                                  */

/* The last parameter to rsbac_get_attr() and rsbac_set_attr() is a pointer */
/* to the appropiate data structure holding the attribute value.            */                         


#ifdef CONFIG_RSBAC_REG
EXPORT_SYMBOL(rsbac_get_attr);
#endif
int rsbac_get_attr(enum rsbac_target_t target,
                   union rsbac_target_id_t tid,
                   enum rsbac_attribute_t attr,
                   union rsbac_attribute_value_t * value,
                   boolean inherit)
    { 
      int head_nr;
      int err = 0;
      int i;
      struct rsbac_device_list_item_t   * device_p;
      struct rsbac_file_dir_list_item_t * fd_item_p;
      struct rsbac_dev_list_item_t      * dev_item_p;
      struct rsbac_ipc_list_item_t      * ipc_item_p;
      struct rsbac_user_list_item_t     * u_item_p;
      struct rsbac_process_list_item_t  * p_item_p;
      u_long                              flags,dflags;
    
      if (!rsbac_initialized)
        {
          printk(KERN_WARNING "rsbac_get_attr(): RSBAC not initialized\n");
          return(-RSBAC_ENOTINITIALIZED);
        }
      if (!value)
        return(-RSBAC_EINVALIDVALUE);
      if (in_interrupt())
        {
          printk(KERN_WARNING "rsbac_get_attr(): called from interrupt!\n");
        }
      switch (target)
        {
          case T_FILE:
          case T_DIR:
            if (rsbac_debug_ds)
                printk(KERN_DEBUG "rsbac_get_attr(): Getting file/dir attribute %u for device %02u:%02u, inode %lu, dentry_p %p\n",
                       attr, MAJOR(tid.file.device),MINOR(tid.file.device), (u_long) tid.file.inode, tid.file.dentry_p);
            /* get list number */
            head_nr = fd_hash(tid.file.inode);
            /* wait for read access to device_list_head */
            rsbac_read_lock(&device_list_head.lock, &dflags);
            /* OK, go on */
/*            if (rsbac_debug_ds)
                printk(KERN_DEBUG
                       "rsbac_get_attr(): passed device read lock\n"); */

            /* lookup device */
            device_p = lookup_device(tid.file.device);
            if (!device_p)
              {
                printk(KERN_WARNING
                       "rsbac_get_attr(): Could not lookup device!\n");
                /* free read lock */
                rsbac_read_unlock(&device_list_head.lock, &dflags);
                return(-RSBAC_EINVALIDDEV);
              }
            /* protect this list */
            rsbac_read_lock(&device_p->head_table[head_nr].lock, &flags);
/*         if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_get_attr(): passed file/dir list head lock %i\n",
                      head_nr); */
            fd_item_p = lookup_file_dir(tid.file.inode, device_p);
            switch (attr)
              {
                case A_security_level:
                  if (fd_item_p)
                    value->security_level = fd_item_p->aci.sec_level;
                  else
                    value->security_level = default_fd_aci.sec_level;
                  if(value->security_level == SL_inherit && inherit)
                    {
                    #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
                      if(   (tid.file.dentry_p)
                           /* stop recursing on ROOT_DEV's root dentry */
                         && (   (tid.file.device != ROOT_DEV)
                             || (tid.file.dentry_p->d_sb->s_root != tid.file.dentry_p))
                         && (tid.file.dentry_p->d_covers)
                         && (tid.file.dentry_p->d_covers->d_parent)
                           /* inode there? (d_covers is used do step over mount points) */
                         && (tid.file.dentry_p->d_covers->d_parent->d_inode)
                        )
                        { /* OK, recurse to parent */
                          /* unprotect the list - parent might be in another */
                          rsbac_read_unlock(&device_p->head_table[head_nr].lock, &flags);
                          /* free access to device_list_head - see above */
                          rsbac_read_unlock(&device_list_head.lock, &dflags);
                          /* parent must always be DIR */
                          target = T_DIR;
                          tid.file.device
                            = tid.file.dentry_p->d_covers->d_parent->d_inode->i_dev;
                          tid.file.inode
                            = tid.file.dentry_p->d_covers->d_parent->d_inode->i_ino;
                          tid.file.dentry_p = tid.file.dentry_p->d_covers->d_parent;
                          return (rsbac_get_attr(target, tid, attr, value, TRUE));
                        }
                      else
                        value->security_level = default_root_dir_aci.sec_level;

                    #else /* VERSION >= 2.4.0 */
                      value->security_level = default_root_dir_aci.sec_level;
                      if(!tid.file.dentry_p)
                        break;
                      /* Is this dentry root of a mounted device? */
                      if(tid.file.dentry_p->d_sb->s_root == tid.file.dentry_p)
                        {
                          if(   !device_p->d_covers
                             || !device_p->d_covers->d_parent
                             || !device_p->d_covers->d_parent->d_inode
                            )
                            break;
                          tid.file.device = device_p->d_covers->d_parent->d_inode->i_dev;
                          tid.file.inode = device_p->d_covers->d_parent->d_inode->i_ino;
                          tid.file.dentry_p = device_p->d_covers->d_parent;
                        }
                      else
                        { /* no root of filesystem -> simply use d_parent, dev keeps unchanged */
                          if(tid.file.dentry_p == tid.file.dentry_p->d_parent)
                            {
                              printk(KERN_DEBUG
                                     "rsbac_get_attr(): oops - d_parent == dentry_p!\n");
                              break;
                            }
                          tid.file.inode = tid.file.dentry_p->d_parent->d_inode->i_ino;
                          tid.file.dentry_p = tid.file.dentry_p->d_parent;
                        }
                      /* OK, recurse to parent */
                      /* unprotect the list - parent might be in another */
                      rsbac_read_unlock(&device_p->head_table[head_nr].lock, &flags);
                      /* free access to device_list_head - see above */
                      rsbac_read_unlock(&device_list_head.lock, &dflags);
                      /* parent must always be DIR */
                      return (rsbac_get_attr(T_DIR, tid, attr, value, TRUE));
                    #endif /* #else of Version < 2.4.0 */
                    }
                  break;
                case A_mac_categories:
                  if (fd_item_p)
                      value->mac_categories = fd_item_p->aci.mac_categories;
                  else
                      value->mac_categories = default_fd_aci.mac_categories;
                  if(   (value->mac_categories == RSBAC_MAC_INHERIT_CAT_VECTOR)
                     && inherit
                    )
                    {
                    #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
                      if(   (tid.file.dentry_p)
                           /* stop recursing on ROOT_DEV's root dentry */
                         && (   (tid.file.device != ROOT_DEV)
                             || (tid.file.dentry_p->d_sb->s_root != tid.file.dentry_p))
                         && (tid.file.dentry_p->d_covers)
                         && (tid.file.dentry_p->d_covers->d_parent)
                           /* inode there? (d_covers is used do step over mount points) */
                         && (tid.file.dentry_p->d_covers->d_parent->d_inode)
                        )
                        { /* OK, recurse to parent */
                          /* unprotect the list - parent might be in another */
                          rsbac_read_unlock(&device_p->head_table[head_nr].lock, &flags);
                          /* free access to device_list_head - see above */
                          rsbac_read_unlock(&device_list_head.lock, &dflags);
                          /* parent must always be DIR */
                          target = T_DIR;
                          tid.dir.device
                            = tid.file.dentry_p->d_covers->d_parent->d_inode->i_dev;
                          tid.dir.inode
                            = tid.file.dentry_p->d_covers->d_parent->d_inode->i_ino;
                          tid.dir.dentry_p = tid.file.dentry_p->d_covers->d_parent;
                          return (rsbac_get_attr(target, tid, attr, value, TRUE));
                        }
                      else
                        {
                          value->mac_categories = default_root_dir_aci.mac_categories;
                        }
                    #else /* VERSION >= 2.4.0 */
                      value->mac_categories = default_root_dir_aci.mac_categories;
                      if(!tid.file.dentry_p)
                        break;
                      /* Is this dentry root of a mounted device? */
                      if(tid.file.dentry_p->d_sb->s_root == tid.file.dentry_p)
                        {
                          if(   !device_p->d_covers
                             || !device_p->d_covers->d_parent
                             || !device_p->d_covers->d_parent->d_inode
                            )
                            break;
                          tid.file.device = device_p->d_covers->d_parent->d_inode->i_dev;
                          tid.file.inode = device_p->d_covers->d_parent->d_inode->i_ino;
                          tid.file.dentry_p = device_p->d_covers->d_parent;
                        }
                      else
                        { /* no root of filesystem -> simply use d_parent, dev keeps unchanged */
                          if(tid.file.dentry_p == tid.file.dentry_p->d_parent)
                            {
                              printk(KERN_DEBUG
                                     "rsbac_get_attr(): oops - d_parent == dentry_p!\n");
                              break;
                            }
                          tid.file.inode = tid.file.dentry_p->d_parent->d_inode->i_ino;
                          tid.file.dentry_p = tid.file.dentry_p->d_parent;
                        }
                      /* OK, recurse to parent */
                      /* unprotect the list - parent might be in another */
                      rsbac_read_unlock(&device_p->head_table[head_nr].lock, &flags);
                      /* free access to device_list_head - see above */
                      rsbac_read_unlock(&device_list_head.lock, &dflags);
                      /* parent must always be DIR */
                      return (rsbac_get_attr(T_DIR, tid, attr, value, TRUE));
                    #endif /* Version < or >= 2.4.0 */
                    }
                  break;
                case A_object_category:
                  if (fd_item_p)
                    value->object_category = fd_item_p->aci.object_category;
                  else
                    value->object_category = default_fd_aci.object_category;
                  if(value->object_category == OC_inherit && inherit)
                    {
                    #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
                      if(   (tid.file.dentry_p)
                           /* stop recursing on ROOT_DEV's root dentry */
                         && (   (tid.file.device != ROOT_DEV)
                             || (tid.file.dentry_p->d_sb->s_root != tid.file.dentry_p))
                         && (tid.file.dentry_p->d_covers)
                         && (tid.file.dentry_p->d_covers->d_parent)
                           /* inode there? (d_covers is used do step over mount points) */
                         && (tid.file.dentry_p->d_covers->d_parent->d_inode)
                        )
                        { /* OK, recurse to parent */
                          /* unprotect the list - parent might be in another */
                          rsbac_read_unlock(&device_p->head_table[head_nr].lock, &flags);
                          /* free access to device_list_head - see above */
                          rsbac_read_unlock(&device_list_head.lock, &dflags);
                          /* parent must always be DIR */
                          target = T_DIR;
                          tid.file.device
                            = tid.file.dentry_p->d_covers->d_parent->d_inode->i_dev;
                          tid.file.inode
                            = tid.file.dentry_p->d_covers->d_parent->d_inode->i_ino;
                          tid.file.dentry_p = tid.file.dentry_p->d_covers->d_parent;
                          return (rsbac_get_attr(target, tid, attr, value, TRUE));
                        }
                      else
                        {
                          value->object_category
                            = default_root_dir_aci.object_category;
                        }
                    #else /* VERSION >= 2.4.0 */
                      value->object_category
                        = default_root_dir_aci.object_category;
                      if(!tid.file.dentry_p)
                        break;
                      /* Is this dentry root of a mounted device? */
                      if(tid.file.dentry_p->d_sb->s_root == tid.file.dentry_p)
                        {
                          break;
                        }
                      else
                        { /* no root of filesystem -> simply use d_parent, dev keeps unchanged */
                          if(tid.file.dentry_p == tid.file.dentry_p->d_parent)
                            {
                              printk(KERN_DEBUG
                                     "rsbac_get_attr(): oops - d_parent == dentry_p!\n");
                              break;
                            }
                          tid.file.inode = tid.file.dentry_p->d_parent->d_inode->i_ino;
                          tid.file.dentry_p = tid.file.dentry_p->d_parent;
                        }
                      /* OK, recurse to parent */
                      /* unprotect the list - parent might be in another */
                      rsbac_read_unlock(&device_p->head_table[head_nr].lock, &flags);
                      /* free access to device_list_head - see above */
                      rsbac_read_unlock(&device_list_head.lock, &dflags);
                      /* parent must always be DIR */
                      return (rsbac_get_attr(T_DIR, tid, attr, value, TRUE));
                    #endif
                    }
                  break;
                case A_data_type:
                  if (fd_item_p)
                    value->data_type = fd_item_p->aci.data_type;
                  else
                    value->data_type = default_fd_aci.data_type;
                  if(value->data_type == DT_inherit && inherit)
                    {
                    #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
                      if(   (tid.file.dentry_p)
                           /* stop recursing on ROOT_DEV's root dentry */
                         && (   (tid.file.device != ROOT_DEV)
                             || (tid.file.dentry_p->d_sb->s_root != tid.file.dentry_p))
                         && (tid.file.dentry_p->d_covers)
                         && (tid.file.dentry_p->d_covers->d_parent)
                           /* inode there? (d_covers is used do step over mount points) */
                         && (tid.file.dentry_p->d_covers->d_parent->d_inode)
                        )
                        { /* OK, recurse to parent */
                          /* unprotect the list - parent might be in another */
                          rsbac_read_unlock(&device_p->head_table[head_nr].lock, &flags);
                          /* free access to device_list_head - see above */
                          rsbac_read_unlock(&device_list_head.lock, &dflags);
                          /* parent must always be DIR */
                          target = T_DIR;
                          tid.file.device
                            = tid.file.dentry_p->d_covers->d_parent->d_inode->i_dev;
                          tid.file.inode
                            = tid.file.dentry_p->d_covers->d_parent->d_inode->i_ino;
                          tid.file.dentry_p = tid.file.dentry_p->d_covers->d_parent;
                          return (rsbac_get_attr(target, tid, attr, value, TRUE));
                        }
                      else
                        {
                          value->data_type
                            = default_root_dir_aci.data_type;
                        }
                    #else /* VERSION >= 2.4.0 */
                      value->data_type
                        = default_root_dir_aci.data_type;
                      if(!tid.file.dentry_p)
                        break;
                      /* Is this dentry root of a mounted device? */
                      if(tid.file.dentry_p->d_sb->s_root == tid.file.dentry_p)
                        {
                          if(   !device_p->d_covers
                             || !device_p->d_covers->d_parent
                             || !device_p->d_covers->d_parent->d_inode
                            )
                            break;
                          tid.file.device = device_p->d_covers->d_parent->d_inode->i_dev;
                          tid.file.inode = device_p->d_covers->d_parent->d_inode->i_ino;
                          tid.file.dentry_p = device_p->d_covers->d_parent;
                        }
                      else
                        { /* no root of filesystem -> simply use d_parent, dev keeps unchanged */
                          if(tid.file.dentry_p == tid.file.dentry_p->d_parent)
                            {
                              printk(KERN_DEBUG
                                     "rsbac_get_attr(): oops - d_parent == dentry_p!\n");
                              break;
                            }
                          tid.file.inode = tid.file.dentry_p->d_parent->d_inode->i_ino;
                          tid.file.dentry_p = tid.file.dentry_p->d_parent;
                        }
                      /* OK, recurse to parent */
                      /* unprotect the list - parent might be in another */
                      rsbac_read_unlock(&device_p->head_table[head_nr].lock, &flags);
                      /* free access to device_list_head - see above */
                      rsbac_read_unlock(&device_list_head.lock, &dflags);
                      /* parent must always be DIR */
                      return (rsbac_get_attr(T_DIR, tid, attr, value, TRUE));
                    #endif /* Version < or >= 2.4.0 */
                    }
                  break;
                case A_pm_object_class:
                  if (fd_item_p)
                    value->pm_object_class = fd_item_p->aci.pm_object_class;
                  else
                    value->pm_object_class = default_fd_aci.pm_object_class;
                  break;
                case A_pm_tp:
                  if (fd_item_p)
                    value->pm_tp = fd_item_p->aci.pm_tp;
                  else
                    value->pm_tp = default_fd_aci.pm_tp;
                  break;
                case A_pm_object_type:
                  if (fd_item_p)
                    value->pm_object_type = fd_item_p->aci.pm_object_type;
                  else
                    value->pm_object_type = default_fd_aci.pm_object_type;
                  break;
                case A_mac_trusted_for_user:
                  if (fd_item_p)
                    value->mac_trusted_for_user = fd_item_p->aci.mac_trusted_for_user;
                  else
                    value->mac_trusted_for_user = default_fd_aci.mac_trusted_for_user;
                  break;
                case A_ms_scanned:
                  if (fd_item_p)
                    value->ms_scanned = fd_item_p->aci.ms_scanned;
                  else
                    value->ms_scanned = default_fd_aci.ms_scanned;
                  break;
                case A_ms_trusted:
                  if (fd_item_p)
                    value->ms_trusted = fd_item_p->aci.ms_trusted;
                  else
                    value->ms_trusted = default_fd_aci.ms_trusted;
                  break;
                case A_ms_sock_trusted_tcp:
                  if (fd_item_p)
                    value->ms_sock_trusted_tcp = fd_item_p->aci.ms_sock_trusted_tcp;
                  else
                    value->ms_sock_trusted_tcp = default_fd_aci.ms_sock_trusted_tcp;
                  break;
                case A_ms_sock_trusted_udp:
                  if (fd_item_p)
                    value->ms_sock_trusted_udp = fd_item_p->aci.ms_sock_trusted_udp;
                  else
                    value->ms_sock_trusted_udp = default_fd_aci.ms_sock_trusted_udp;
                  break;
                case A_ff_flags:
                  if (fd_item_p)
                    value->ff_flags = fd_item_p->aci.ff_flags;
                  else
                    value->ff_flags = default_fd_aci.ff_flags;
                  if((value->ff_flags & FF_add_inherited) && inherit)
                    {
                      union rsbac_attribute_value_t rvalue;

                      /* all values for inheritance present? */
                    #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
                      if(   (tid.file.dentry_p)
                           /* stop recursing on ROOT_DEV's root dentry */
                         && (   (tid.file.device != ROOT_DEV)
                             || (tid.file.dentry_p->d_sb->s_root != tid.file.dentry_p))
                         && (tid.file.dentry_p->d_covers)
                         && (tid.file.dentry_p->d_covers->d_parent)
                           /* inode there? (d_covers is used do step over mount points) */
                         && (tid.file.dentry_p->d_covers->d_parent->d_inode)
                        )
                        {
                          /* OK, recurse to parent */
                          /* unprotect the list - parent might be in another */
                          rsbac_read_unlock(&device_p->head_table[head_nr].lock, &flags);
                          /* free access to device_list_head - see above */
                          rsbac_read_unlock(&device_list_head.lock, &dflags);
                          /* parent must always be DIR */
                          target = T_DIR;
                          tid.file.device
                            = tid.file.dentry_p->d_covers->d_parent->d_inode->i_dev;
                          tid.file.inode
                            = tid.file.dentry_p->d_covers->d_parent->d_inode->i_ino;
                          tid.file.dentry_p = tid.file.dentry_p->d_covers->d_parent;
                          /* get parent's value, possibly inherited itself */
                          err = rsbac_get_attr(target, tid, attr, &rvalue, TRUE);
                          if(!err)
                            {
                              /* add new flags */
                              value->ff_flags |= rvalue.ff_flags;
                              /* remove inherit flag */
                              value->ff_flags &= ~FF_add_inherited;
                            }
                          return err;
                        }
                      else
                        {
                          /* no more inheritance possible, mask out add_inherited */
                          value->ff_flags &= ~FF_add_inherited;
                        }
                    #else /* VERSION >= 2.4.0 */
                      /* no more inheritance possible, mask out add_inherited */
                      value->ff_flags &= ~FF_add_inherited;
                      if(!tid.file.dentry_p)
                        break;
                      /* Is this dentry root of a mounted device? */
                      if(tid.file.dentry_p->d_sb->s_root == tid.file.dentry_p)
                        {
                          if(   !device_p->d_covers
                             || !device_p->d_covers->d_parent
                             || !device_p->d_covers->d_parent->d_inode
                            )
                            break;
                          tid.file.device = device_p->d_covers->d_parent->d_inode->i_dev;
                          tid.file.inode = device_p->d_covers->d_parent->d_inode->i_ino;
                          tid.file.dentry_p = device_p->d_covers->d_parent;
                        }
                      else
                        { /* no root of filesystem -> simply use d_parent, dev keeps unchanged */
                          if(tid.file.dentry_p == tid.file.dentry_p->d_parent)
                            {
                              printk(KERN_DEBUG
                                     "rsbac_get_attr(): oops - d_parent == dentry_p!\n");
                              break;
                            }
                          tid.file.inode = tid.file.dentry_p->d_parent->d_inode->i_ino;
                          tid.file.dentry_p = tid.file.dentry_p->d_parent;
                        }
                      /* OK, recurse to parent */
                      /* unprotect the list - parent might be in another */
                      rsbac_read_unlock(&device_p->head_table[head_nr].lock, &flags);
                      /* free access to device_list_head - see above */
                      rsbac_read_unlock(&device_list_head.lock, &dflags);
                      /* parent must always be DIR */
                      /* get parent's value, possibly inherited itself */
                      err = rsbac_get_attr(T_DIR, tid, attr, &rvalue, TRUE);
                      if(!err)
                        {
                          /* add new flags */
                          value->ff_flags |= rvalue.ff_flags;
                          /* remove inherit flag */
                          value->ff_flags &= ~FF_add_inherited;
                        }
                      return err;
                    #endif
                    }
                  break;
                case A_rc_type_fd:
                  if (fd_item_p)
                    value->rc_type_fd = fd_item_p->aci.rc_type_fd;
                  else
                    value->rc_type_fd = default_fd_aci.rc_type_fd;
                  if(value->rc_type_fd == RC_type_inherit_parent && inherit)
                    {
                    #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
                      if(   (tid.file.dentry_p)
                           /* stop recursing on ROOT_DEV's root dentry */
                         && (   (tid.file.device != ROOT_DEV)
                             || (tid.file.dentry_p->d_sb->s_root != tid.file.dentry_p))
                         && (tid.file.dentry_p->d_covers)
                         && (tid.file.dentry_p->d_covers->d_parent)
                           /* inode there? (d_covers is used do step over mount points) */
                         && (tid.file.dentry_p->d_covers->d_parent->d_inode)
                        )
                        { /* OK, recurse to parent */
                          /* unprotect the list - parent might be in another */
                          rsbac_read_unlock(&device_p->head_table[head_nr].lock, &flags);
                          /* free access to device_list_head - see above */
                          rsbac_read_unlock(&device_list_head.lock, &dflags);
                          /* parent must always be DIR */
                          target = T_DIR;
                          tid.file.device
                            = tid.file.dentry_p->d_covers->d_parent->d_inode->i_dev;
                          tid.file.inode
                            = tid.file.dentry_p->d_covers->d_parent->d_inode->i_ino;
                          tid.file.dentry_p = tid.file.dentry_p->d_covers->d_parent;
                          return (rsbac_get_attr(target, tid, attr, value, TRUE));
                        }
                      else
                        {
                          value->rc_type_fd
                            = default_root_dir_aci.rc_type_fd;
                        }
                    #else /* VERSION >= 2.4.0 */
                          value->rc_type_fd
                            = default_root_dir_aci.rc_type_fd;
                      if(!tid.file.dentry_p)
                        break;
                      /* Is this dentry root of a mounted device? */
                      if(tid.file.dentry_p->d_sb->s_root == tid.file.dentry_p)
                        {
                          if(   !device_p->d_covers
                             || !device_p->d_covers->d_parent
                             || !device_p->d_covers->d_parent->d_inode
                            )
                            break;
                          tid.file.device = device_p->d_covers->d_parent->d_inode->i_dev;
                          tid.file.inode = device_p->d_covers->d_parent->d_inode->i_ino;
                          tid.file.dentry_p = device_p->d_covers->d_parent;
                        }
                      else
                        { /* no root of filesystem -> simply use d_parent, dev keeps unchanged */
                          if(tid.file.dentry_p == tid.file.dentry_p->d_parent)
                            {
                              printk(KERN_DEBUG
                                     "rsbac_get_attr(): oops - d_parent == dentry_p!\n");
                              break;
                            }
                          tid.file.inode = tid.file.dentry_p->d_parent->d_inode->i_ino;
                          tid.file.dentry_p = tid.file.dentry_p->d_parent;
                        }
                      /* OK, recurse to parent */
                      /* unprotect the list - parent might be in another */
                      rsbac_read_unlock(&device_p->head_table[head_nr].lock, &flags);
                      /* free access to device_list_head - see above */
                      rsbac_read_unlock(&device_list_head.lock, &dflags);
                      /* parent must always be DIR */
                      return (rsbac_get_attr(T_DIR, tid, attr, value, TRUE));
                    #endif
                    }
                  break;
                case A_rc_force_role:
                  if (fd_item_p)
                    value->rc_force_role = fd_item_p->aci.rc_force_role;
                  else
                    value->rc_force_role = default_fd_aci.rc_force_role;
                  if(value->rc_force_role == RC_role_inherit_parent && inherit)
                    {
                    #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
                      if(   (tid.file.dentry_p)
                           /* stop recursing on ROOT_DEV's root dentry */
                         && (   (tid.file.device != ROOT_DEV)
                             || (tid.file.dentry_p->d_sb->s_root != tid.file.dentry_p))
                         && (tid.file.dentry_p->d_covers)
                         && (tid.file.dentry_p->d_covers->d_parent)
                           /* inode there? (d_covers is used do step over mount points) */
                         && (tid.file.dentry_p->d_covers->d_parent->d_inode)
                        )
                        { /* OK, recurse to parent */
                          /* unprotect the list - parent might be in another */
                          rsbac_read_unlock(&device_p->head_table[head_nr].lock, &flags);
                          /* free access to device_list_head - see above */
                          rsbac_read_unlock(&device_list_head.lock, &dflags);
                          /* parent must always be DIR */
                          target = T_DIR;
                          tid.file.device
                            = tid.file.dentry_p->d_covers->d_parent->d_inode->i_dev;
                          tid.file.inode
                            = tid.file.dentry_p->d_covers->d_parent->d_inode->i_ino;
                          tid.file.dentry_p = tid.file.dentry_p->d_covers->d_parent;
                          return (rsbac_get_attr(target, tid, attr, value, TRUE));
                        }
                      else
                        {
                          value->rc_force_role
                            = default_root_dir_aci.rc_force_role;
                        }
                    #else /* VERSION >= 2.4.0 */
                      value->rc_force_role
                        = default_root_dir_aci.rc_force_role;
                      if(!tid.file.dentry_p)
                        break;
                      /* Is this dentry root of a mounted device? */
                      if(tid.file.dentry_p->d_sb->s_root == tid.file.dentry_p)
                        {
                          if(   !device_p->d_covers
                             || !device_p->d_covers->d_parent
                             || !device_p->d_covers->d_parent->d_inode
                            )
                            break;
                          tid.file.device = device_p->d_covers->d_parent->d_inode->i_dev;
                          tid.file.inode = device_p->d_covers->d_parent->d_inode->i_ino;
                          tid.file.dentry_p = device_p->d_covers->d_parent;
                        }
                      else
                        { /* no root of filesystem -> simply use d_parent, dev keeps unchanged */
                          if(tid.file.dentry_p == tid.file.dentry_p->d_parent)
                            {
                              printk(KERN_DEBUG
                                     "rsbac_get_attr(): oops - d_parent == dentry_p!\n");
                              break;
                            }
                          tid.file.inode = tid.file.dentry_p->d_parent->d_inode->i_ino;
                          tid.file.dentry_p = tid.file.dentry_p->d_parent;
                        }
                      /* OK, recurse to parent */
                      /* unprotect the list - parent might be in another */
                      rsbac_read_unlock(&device_p->head_table[head_nr].lock, &flags);
                      /* free access to device_list_head - see above */
                      rsbac_read_unlock(&device_list_head.lock, &dflags);
                      /* parent must always be DIR */
                      return (rsbac_get_attr(T_DIR, tid, attr, value, TRUE));
                    #endif
                    }
                  break;
                case A_auth_may_setuid:
                  if (fd_item_p)
                    value->auth_may_setuid = fd_item_p->aci.auth_may_setuid;
                  else
                    value->auth_may_setuid = default_fd_aci.auth_may_setuid;
                  break;
                case A_auth_may_set_cap:
                  if (fd_item_p)
                    value->auth_may_set_cap = fd_item_p->aci.auth_may_set_cap;
                  else
                    value->auth_may_set_cap = default_fd_aci.auth_may_set_cap;
                  break;
                case A_log_array_low:
                  if (fd_item_p)
                    value->log_array_low = fd_item_p->aci.log_array_low;
                  else
                    value->log_array_low = default_fd_aci.log_array_low;
                  break;
                case A_log_array_high:
                  if (fd_item_p)
                    value->log_array_high = fd_item_p->aci.log_array_high;
                  else
                    value->log_array_high = default_fd_aci.log_array_high;
                  break;
                case A_log_program_based:
                  if (fd_item_p)
                    value->log_program_based = fd_item_p->aci.log_program_based;
                  else
                    value->log_program_based = default_fd_aci.log_program_based;
                  break;
                default:
                  err = -RSBAC_EINVALIDATTR;
              }
            /* we are ready, so unprotect the list */
            rsbac_read_unlock(&device_p->head_table[head_nr].lock, &flags);
            /* free access to device_list_head */
            rsbac_read_unlock(&device_list_head.lock, &dflags);
            /* and return                          */
            break;
            
          case T_DEV:
/*            if (rsbac_debug_ds)
                printk(KERN_DEBUG
                       "rsbac_get_attr(): Getting dev attribute\n"); */
            /* protect the list */
            rsbac_read_lock(&dev_list_head.lock, &flags);
            dev_item_p = lookup_dev(tid.dev);
            switch (attr)
              {
                case A_security_level:
                  if (dev_item_p)
                    value->security_level = dev_item_p->aci.sec_level;
                  else
                    value->security_level = default_dev_aci.sec_level;
                  break;
                case A_mac_categories:
                  if (dev_item_p)
                    value->mac_categories = dev_item_p->aci.mac_categories;
                  else
                    value->mac_categories = default_dev_aci.mac_categories;
                  break;
                case A_object_category:
                  if (dev_item_p)
                    value->object_category = dev_item_p->aci.object_category;
                  else
                    value->object_category = default_dev_aci.object_category;
                  break;
                case A_data_type:
                  if (dev_item_p)
                    value->data_type = dev_item_p->aci.data_type;
                  else
                    value->data_type = default_dev_aci.data_type;
                  break;
                case A_mac_check:
                  if (dev_item_p)
                    value->mac_check = dev_item_p->aci.mac_check;
                  else
                    value->mac_check = default_dev_aci.mac_check;
                  break;
                case A_pm_object_type:
                  if (dev_item_p)
                    value->pm_object_type = dev_item_p->aci.pm_object_type;
                  else
                    value->pm_object_type = default_dev_aci.pm_object_type;
                  break;
                case A_pm_object_class:
                  if (dev_item_p)
                    value->pm_object_class = dev_item_p->aci.pm_object_class;
                  else
                    value->pm_object_class = default_dev_aci.pm_object_class;
                  break;
                case A_rc_type:
                  if (dev_item_p)
                    value->rc_type = dev_item_p->aci.rc_type;
                  else
                    value->rc_type = default_dev_aci.rc_type;
                  break;
                case A_log_array_low:
                  if (dev_item_p)
                    value->log_array_low = dev_item_p->aci.log_array_low;
                  else
                    value->log_array_low = default_dev_aci.log_array_low;
                  break;
                case A_log_array_high:
                  if (dev_item_p)
                    value->log_array_high = dev_item_p->aci.log_array_high;
                  else
                    value->log_array_high = default_dev_aci.log_array_high;
                  break;
                default:
                  err = -RSBAC_EINVALIDATTR;
              }
            /* we are ready, so unprotect the list */
            rsbac_read_unlock(&dev_list_head.lock, &flags);
            /* and return                          */
            break;
            
          case T_IPC:
/*            if (rsbac_debug_ds) printk(KERN_DEBUG
                        "rsbac_get_attr(): Getting ipc attribute\n"); */
            if(tid.ipc.type == I_sockid)
              return(-RSBAC_EINVALIDTARGET);
            /* lookup only, if not sock or (sock-id != NULL), OK with NULL fifo */
/*            if (   (tid.ipc.type == I_sock)
                && (!tid.ipc.id.sock)
               )
              {
                printk(KERN_WARNING
                       "rsbac_get_attr(): called for NULL sock\n");
                return(-RSBAC_EINVALIDTARGET);
              }
*/
            /* protect the list */
            rsbac_read_lock(&ipc_list_head.lock, &flags);
            ipc_item_p = lookup_ipc(tid.ipc);

            switch (attr)
              {
                case A_security_level:
                  if (ipc_item_p)
                    value->security_level = ipc_item_p->aci.sec_level;
                  else
                    value->security_level = default_ipc_aci.sec_level;
                  break;
                case A_mac_categories:
                  if (ipc_item_p)
                    value->mac_categories = ipc_item_p->aci.mac_categories;
                  else
                    value->mac_categories = default_ipc_aci.mac_categories;
                  break;
                case A_object_category:
                  if (ipc_item_p)
                    value->object_category = ipc_item_p->aci.object_category;
                  else
                    value->object_category = default_ipc_aci.object_category;
                  break;
                case A_data_type:
                  if (ipc_item_p)
                    value->data_type = ipc_item_p->aci.data_type;
                  else
                    value->data_type = default_ipc_aci.data_type;
                  break;
                case A_pm_object_class:
                  if (ipc_item_p)
                    value->pm_object_class = ipc_item_p->aci.pm_object_class;
                  else
                    value->pm_object_class = default_ipc_aci.pm_object_class;
                  break;
                case A_pm_ipc_purpose:
                  if (ipc_item_p)
                    value->pm_ipc_purpose = ipc_item_p->aci.pm_ipc_purpose;
                  else
                    value->pm_ipc_purpose = default_ipc_aci.pm_ipc_purpose;
                  break;
                case A_pm_object_type:
                  if (ipc_item_p)
                    value->pm_object_type = ipc_item_p->aci.pm_object_type;
                  else
                    value->pm_object_type = default_ipc_aci.pm_object_type;
                  break;
                case A_ms_backbuf:
                  if (ipc_item_p)
                    value->ms_backbuf = ipc_item_p->aci.ms_backbuf;
                  else
                    value->ms_backbuf = default_ipc_aci.ms_backbuf;
                  break;
                case A_ms_buflen:
                  if (ipc_item_p)
                    value->ms_buflen = ipc_item_p->aci.ms_buflen;
                  else
                    value->ms_buflen = default_ipc_aci.ms_buflen;
                  break;
                case A_ms_str_nr:
                  for(i=0; i<RSBAC_MS_NR_MALWARE; i++)
                    if (ipc_item_p)
                      value->ms_str_nr[i] = ipc_item_p->aci.ms_str_nr[i];
                    else
                      value->ms_str_nr[i] = default_ipc_aci.ms_str_nr[i];
                  break;
                case A_ms_str_offset:
                  for(i=0; i<RSBAC_MS_NR_MALWARE; i++)
                    if (ipc_item_p)
                      value->ms_str_offset[i] = ipc_item_p->aci.ms_str_offset[i];
                    else
                      value->ms_str_offset[i] = default_ipc_aci.ms_str_offset[i];
                  break;
                case A_ms_scanned:
                  if (ipc_item_p)
                    value->ms_scanned = ipc_item_p->aci.ms_scanned;
                  else
                    value->ms_scanned = default_ipc_aci.ms_scanned;
                  break;
                case A_rc_type:
                  if (ipc_item_p)
                    value->rc_type = ipc_item_p->aci.rc_type;
                  else
                    value->rc_type = default_ipc_aci.rc_type;
                  break;
                default:
                  err = -RSBAC_EINVALIDATTR;
              }
            /* we are ready, so unprotect the list */
            rsbac_read_unlock(&ipc_list_head.lock, &flags);
            /* and return                          */
            break;
            
          case T_USER:
/*            if (rsbac_debug_ds) printk(KERN_DEBUG
                           "rsbac_get_attr(): Getting user attribute\n");
*/
            /* protect the list */
            rsbac_read_lock(&user_list_head.lock, &flags);
            u_item_p = lookup_user(tid.user);
            switch (attr)
              {
                case A_pseudo:
                  if (u_item_p)
                    value->pseudo = u_item_p->aci.pseudo;
                  else
                    value->pseudo = default_u_aci.pseudo;
                  break;
                case A_security_level:
                  if (u_item_p)
                    value->security_level = u_item_p->aci.access_appr;
                  else
                    value->security_level = default_u_aci.access_appr;
                  break;
                case A_mac_categories:
                  if (u_item_p)
                    value->mac_categories = u_item_p->aci.mac_categories;
                  else
                    value->mac_categories = default_u_aci.mac_categories;
                  break;
                case A_system_role:
                  if (u_item_p)
                    value->system_role = u_item_p->aci.system_role;
                  else
                    value->system_role = default_u_aci.system_role;
                  break;
                case A_pm_task_set:
                  if (u_item_p)
                    value->pm_task_set = u_item_p->aci.pm_task_set;
                  else
                    value->pm_task_set = default_u_aci.pm_task_set;
                  break;
                case A_pm_role:
                  if (u_item_p)
                    value->pm_role = u_item_p->aci.pm_role;
                  else
                    value->pm_role = default_u_aci.pm_role;
                  break;
                case A_rc_def_role:
                  if (u_item_p)
                    value->rc_def_role = u_item_p->aci.rc_def_role;
                  else
                    value->rc_def_role = default_u_aci.rc_def_role;
                  break;
                case A_log_user_based:
                  if (u_item_p)
                    value->log_user_based = u_item_p->aci.log_user_based;
                  else
                    value->log_user_based = default_u_aci.log_user_based;
                  break;
                default:
                  err = -RSBAC_EINVALIDATTR;
              }
            /* we are ready, so unprotect the list */
            rsbac_read_unlock(&user_list_head.lock, &flags);
            /* and return */
            break;
            
          case T_PROCESS:
/*            if (rsbac_debug_ds) printk(KERN_DEBUG "%s\n",
                        "rsbac_get_attr(): Getting process attribute");
*/
            /* protect the list */
            rsbac_read_lock(&process_list_head.lock, &flags);
            p_item_p = lookup_process(tid.process);
            switch (attr)
              {
#ifdef CONFIG_RSBAC_USE_RSBAC_OWNER
                case A_owner:
                  if (p_item_p)
                    value->owner = p_item_p->aci.owner;
                  else
                    value->owner = default_p_aci.owner;
                  break;
#endif
                case A_security_level:
                  if (p_item_p)
                    value->security_level = p_item_p->aci.owner_sec_level;
                  else
                    value->security_level = default_p_aci.owner_sec_level;
                  break;
                case A_mac_categories:
                  if (p_item_p)
                    value->mac_categories = p_item_p->aci.mac_owner_categories;
                  else
                    value->mac_categories = default_p_aci.mac_owner_categories;
                  break;
                case A_current_sec_level:
                  if (p_item_p)
                    value->current_sec_level = p_item_p->aci.current_sec_level;
                  else
                    value->current_sec_level = default_p_aci.current_sec_level;
                  break;
                case A_mac_curr_categories:
                  if (p_item_p)
                    value->mac_categories = p_item_p->aci.mac_curr_categories;
                  else
                    value->mac_categories = default_p_aci.mac_curr_categories;
                  break;
                case A_min_write_open:
                  if (p_item_p)
                    value->min_write_open = p_item_p->aci.min_write_open;
                  else
                    value->min_write_open = default_p_aci.min_write_open;
                  break;
                case A_min_write_categories:
                  if (p_item_p)
                    value->mac_categories = p_item_p->aci.min_write_categories;
                  else
                    value->mac_categories = default_p_aci.min_write_categories;
                  break;
                case A_max_read_open:
                  if (p_item_p)
                    value->max_read_open = p_item_p->aci.max_read_open;
                  else
                    value->max_read_open = default_p_aci.max_read_open;
                  break;
                case A_max_read_categories:
                  if (p_item_p)
                    value->mac_categories = p_item_p->aci.max_read_categories;
                  else
                    value->mac_categories = default_p_aci.max_read_categories;
                  break;
                case A_mac_auto:
                  if (p_item_p)
                    value->mac_auto = p_item_p->aci.mac_auto;
                  else
                    value->mac_auto = default_p_aci.mac_auto;
                  break;
                case A_mac_trusted:
                  if (p_item_p)
                    value->mac_trusted = p_item_p->aci.mac_trusted;
                  else
                    value->mac_trusted = default_p_aci.mac_trusted;
                  break;
                case A_pm_tp:
                  if (p_item_p)
                    value->pm_tp = p_item_p->aci.pm_tp;
                  else
                    value->pm_tp = default_p_aci.pm_tp;
                  break;
                case A_pm_current_task:
                  if (p_item_p)
                    value->pm_current_task = p_item_p->aci.pm_current_task;
                  else
                    value->pm_current_task = default_p_aci.pm_current_task;
                  break;
                case A_pm_process_type:
                  if (p_item_p)
                    value->pm_process_type = p_item_p->aci.pm_process_type;
                  else
                    value->pm_process_type = default_p_aci.pm_process_type;
                  break;
                case A_ms_trusted:
                  if (p_item_p)
                    value->ms_trusted = p_item_p->aci.ms_trusted;
                  else
                    value->ms_trusted = default_p_aci.ms_trusted;
                  break;
                case A_ms_sock_trusted_tcp:
                  if (p_item_p)
                    value->ms_sock_trusted_tcp = p_item_p->aci.ms_sock_trusted_tcp;
                  else
                    value->ms_sock_trusted_tcp = default_p_aci.ms_sock_trusted_tcp;
                  break;
                case A_ms_sock_trusted_udp:
                  if (p_item_p)
                    value->ms_sock_trusted_udp = p_item_p->aci.ms_sock_trusted_udp;
                  else
                    value->ms_sock_trusted_udp = default_p_aci.ms_sock_trusted_udp;
                  break;
                case A_rc_role:
                  if (p_item_p)
                    value->rc_role = p_item_p->aci.rc_role;
                  else
                    value->rc_role = default_p_aci.rc_role;
                  break;
                case A_rc_type:
                  if (p_item_p)
                    value->rc_type = p_item_p->aci.rc_type;
                  else
                    value->rc_type = default_p_aci.rc_type;
                  break;
                case A_rc_force_role:
                  if (p_item_p)
                    value->rc_force_role = p_item_p->aci.rc_force_role;
                  else
                    value->rc_force_role = default_p_aci.rc_force_role;
                  break;
                case A_auth_may_setuid:
                  if (p_item_p)
                    value->auth_may_setuid = p_item_p->aci.auth_may_setuid;
                  else
                    value->auth_may_setuid = default_p_aci.auth_may_setuid;
                  break;
                case A_auth_may_set_cap:
                  if (p_item_p)
                    value->auth_may_set_cap = p_item_p->aci.auth_may_set_cap;
                  else
                    value->auth_may_set_cap = default_p_aci.auth_may_set_cap;
                  break;
                case A_log_program_based:
                  if (p_item_p)
                    value->log_program_based = p_item_p->aci.log_program_based;
                  else
                    value->log_program_based = default_p_aci.log_program_based;
                  break;
                default:
                  err = -RSBAC_EINVALIDATTR;
              }
            /* we are ready, so unprotect the list */
            rsbac_read_unlock(&process_list_head.lock, &flags);
            /* and return                          */
            break;

          /* switch target: no valid target */
          default:  
            return(-RSBAC_EINVALIDTARGET);          
        }
      #ifdef CONFIG_RSBAC_XSTATS
      if(!err)
        get_attr_count[target]++;
      #endif
      return(err);
    };      /* end of rsbac_get_attr() */

/************************************************************************** */

#ifdef CONFIG_RSBAC_REG
EXPORT_SYMBOL(rsbac_set_attr);
#endif
int rsbac_set_attr(enum rsbac_target_t target,
                   union rsbac_target_id_t tid,
                   enum rsbac_attribute_t attr,
                   union rsbac_attribute_value_t value)

    { 
      int head_nr;
      int err = 0;
      int i;
      struct rsbac_device_list_item_t   * device_p;
      struct rsbac_file_dir_list_item_t * fd_item_p;
      struct rsbac_file_dir_aci_t         fd_aci = default_fd_aci;
      struct rsbac_dev_list_item_t      * dev_item_p;
      struct rsbac_ipc_list_item_t      * ipc_item_p;
      struct rsbac_user_list_item_t     * u_item_p;
      struct rsbac_process_list_item_t  * p_item_p;
      u_long                              flags,dflags;
    
      if (!rsbac_initialized)
        {
          printk(KERN_WARNING "rsbac_set_attr(): RSBAC not initialized\n");
          return(-RSBAC_ENOTINITIALIZED);
        }
      if (in_interrupt())
        {
          printk(KERN_WARNING "rsbac_set_attr(): called from interrupt!\n");
        }
      switch (target)
        {
          case T_FILE:
          case T_DIR:
            if (rsbac_debug_ds) printk(KERN_DEBUG "rsbac_set_attr(): Setting file/dir attribute for device %u, inode %u\n",
                         (u_int) tid.file.device, tid.file.inode);
            if(!tid.file.device)
              {
                printk(KERN_WARNING
                       "rsbac_set_attr(): device is 0!\n");
                return(-RSBAC_EINVALIDDEV);
              }
            if(!tid.file.inode)
              {
                printk(KERN_WARNING
                       "rsbac_set_attr(): inode is 0!\n");
                return(-RSBAC_EINVALIDTARGET);
              }
            /* get list number */
            head_nr = fd_hash(tid.file.inode);

            /* wait for read access to device_list_head */
            rsbac_read_lock(&device_list_head.lock, &dflags);
            /* OK, go on */

            /* lookup device */
            device_p = lookup_device(tid.file.device);
            if (!device_p)
              {
                printk(KERN_WARNING
                       "rsbac_set_attr(): Could not lookup device!\n");
                /* free access to device_list_head */
                rsbac_read_unlock(&device_list_head.lock, &dflags);
                return(-RSBAC_EINVALIDDEV);
              }
            /* protect this list */
            rsbac_write_lock(&device_p->head_table[head_nr].lock, &flags);
            /* look up the item */
/*            if (rsbac_debug_ds) printk(KERN_DEBUG "rsbac_set_attr(): looking up file/dir item!\n");
 */
            fd_item_p = lookup_file_dir(tid.file.inode, device_p);
            
            if (!fd_item_p)
              { /* Not found -> add a new item with default values */
                if (rsbac_debug_ds) printk(KERN_DEBUG "rsbac_set_attr(): adding file/dir item!\n");
                /* fd_aci was initialized with default_fd_aci, now set id */
                fd_aci.id = tid.file.inode;
                /* and add item */
                fd_item_p = add_file_dir_item(fd_aci, device_p);
                /* if could not add -> return error */
                if (!fd_item_p)
                  {
                    printk(KERN_CRIT
                           "rsbac_set_attr(): Could not add file/dir item!\n");
                    /* unprotect the list */
                    rsbac_write_unlock(&device_p->head_table[head_nr].lock, &flags);
                    /* free access to device_list_head */
                    rsbac_read_unlock(&device_list_head.lock, &dflags);
                    return(-RSBAC_ECOULDNOTADDITEM);
                  }
/*                if (rsbac_debug_ds) printk(KERN_DEBUG "%s\n",
                            "rsbac_set_attr(): File/dir item added."); */
              }

            /* OK, now we can do the attribute setting itself */
/*            if (rsbac_debug_ds) printk(KERN_DEBUG "rsbac_set_attr(): set file/dir attribute!\n");
 */
            switch (attr)
              {
                case A_security_level:
                  /* security_level SL_rsbac_internal is only set internally */
                  if (   (value.security_level == SL_rsbac_internal)
                      || (fd_item_p->aci.sec_level == SL_rsbac_internal) )
                    {
                      err = -RSBAC_EINTERNONLY;
                      printk(KERN_WARNING
                             "rsbac_set_attr(): SL_rsbac_internal for file/dir, value not set.\n");
                    }
                  else
                    fd_item_p->aci.sec_level = value.security_level;
                  break;
                case A_mac_categories:
                  fd_item_p->aci.mac_categories = value.mac_categories;
                  break;
                case A_object_category:
                  fd_item_p->aci.object_category = value.object_category;
                  break;
                case A_data_type:
                  fd_item_p->aci.data_type = value.data_type;
                  break;
                case A_pm_object_class:
                  fd_item_p->aci.pm_object_class = value.pm_object_class;
                  break;
                case A_pm_tp:
                  fd_item_p->aci.pm_tp = value.pm_tp;
                  break;
                case A_pm_object_type:
                  fd_item_p->aci.pm_object_type = value.pm_object_type;
                  break;
                case A_mac_trusted_for_user:
                  fd_item_p->aci.mac_trusted_for_user = value.mac_trusted_for_user;
                  break;
                case A_ms_scanned:
                  fd_item_p->aci.ms_scanned = value.ms_scanned;
                  break;
                case A_ms_trusted:
                  fd_item_p->aci.ms_trusted = value.ms_trusted;
                  break;
                case A_ms_sock_trusted_tcp:
                  fd_item_p->aci.ms_sock_trusted_tcp = value.ms_sock_trusted_tcp;
                  break;
                case A_ms_sock_trusted_udp:
                  fd_item_p->aci.ms_sock_trusted_udp = value.ms_sock_trusted_udp;
                  break;
                case A_ff_flags:
                  fd_item_p->aci.ff_flags = value.ff_flags;
                  break;
                case A_rc_type_fd:
                  fd_item_p->aci.rc_type_fd = value.rc_type_fd;
                  break;
                case A_rc_force_role:
                  fd_item_p->aci.rc_force_role = value.rc_force_role;
                  break;
                case A_auth_may_setuid:
                  fd_item_p->aci.auth_may_setuid = value.auth_may_setuid;
                  break;
                case A_auth_may_set_cap:
                  fd_item_p->aci.auth_may_set_cap = value.auth_may_set_cap;
                  break;
                case A_log_array_low:
                  fd_item_p->aci.log_array_low = value.log_array_low;
                  break;
                case A_log_array_high:
                  fd_item_p->aci.log_array_high = value.log_array_high;
                  break;
                case A_log_program_based:
                  fd_item_p->aci.log_program_based = value.log_program_based;
                  break;
                default:
                  err = -RSBAC_EINVALIDATTR;
              }
            /* we are ready, so unprotect the list */
            rsbac_write_unlock(&device_p->head_table[head_nr].lock, &flags);
            /* if no error, mark dirty */
            if (!err)
              {
                device_p->head_table[head_nr].dirty = TRUE;
              }
            /* free access to device_list_head */
            rsbac_read_unlock(&device_list_head.lock, &dflags);
            /* and return                          */
            break;
            
          case T_DEV:
/*            if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_set_attr(): Setting dev attribute\n"); */
            /* protect the list */
            rsbac_write_lock(&dev_list_head.lock, &flags);
            dev_item_p = lookup_dev(tid.dev);
            if (!dev_item_p)
              { /* Not found -> add a new item with default values */
                dev_item_p = add_dev_item(default_dev_aci);
                /* if could not add -> return error */
                if (!dev_item_p)
                  {
                    printk(KERN_CRIT
                           "rsbac_set_attr(): Could not add dev item!\n");
                    /* free list */
                    rsbac_write_unlock(&dev_list_head.lock, &flags);
                    return(-RSBAC_ECOULDNOTADDITEM);
                  }
                /* set file/dir id - no need to do it earlier, since add() */
                /* does not need it. */
                dev_item_p->aci.id = tid.dev;
                if (rsbac_debug_ds)
                  printk(KERN_DEBUG
                         "rsbac_set_attr(): dev item added\n");
                /* dev list changed, but changed again later, so don't */
                /* write_dev_list(); */
              }
              
            /* OK, now we can do the attribute setting itself */
            switch (attr)
              {
                case A_security_level:
                  /* security_level SL_rsbac_internal is only set internally */
                  if (   (value.security_level == SL_rsbac_internal)
                      || (dev_item_p->aci.sec_level == SL_rsbac_internal) )
                    {
                      err = -RSBAC_EINTERNONLY;
                      printk(KERN_WARNING "%s\n",
                      "rsbac_set_attr(): SL_rsbac_internal for dev, value not set.");
                    }
                  else
                    dev_item_p->aci.sec_level = value.security_level;
                  break;
                case A_mac_categories:
                  dev_item_p->aci.mac_categories = value.mac_categories;
                  break;
                case A_object_category:
                  dev_item_p->aci.object_category = value.object_category;
                  break;
                case A_data_type:
                  dev_item_p->aci.data_type = value.data_type;
                  break;
                case A_mac_check:
                  dev_item_p->aci.mac_check = value.mac_check;
                  break;
                case A_pm_object_type:
                  dev_item_p->aci.pm_object_type = value.pm_object_type;
                  break;
                case A_pm_object_class:
                  dev_item_p->aci.pm_object_class = value.pm_object_class;
                  break;
                case A_rc_type:
                  dev_item_p->aci.rc_type = value.rc_type;
                  break;
                case A_log_array_low:
                  dev_item_p->aci.log_array_low = value.log_array_low;
                  break;
                case A_log_array_high:
                  dev_item_p->aci.log_array_high = value.log_array_high;
                  break;
                default:
                  err = -RSBAC_EINVALIDATTR;
              }
            /* we are ready, so unprotect the list */
            rsbac_write_unlock(&dev_list_head.lock, &flags);
            /* if changed, mark dirty */
            if (!err)
              {
                dev_list_head.dirty = TRUE;
              }
            
            /* and return                          */
            break;
            
          case T_IPC:
/*            if (rsbac_debug_ds) printk(KERN_DEBUG "%s\n",
                        "rsbac_set_attr(): Setting ipc attribute"); */
            if (   (   (tid.ipc.type == I_sock)
                    && (!tid.ipc.id.sock)
                   )
                || (   (tid.ipc.type == I_fifo)
                    && (!tid.ipc.id.fifo)
                   )
               )
              {
                printk(KERN_WARNING
                       "rsbac_set_attr(): called for NULL sock or fifo\n");
                return(-RSBAC_EINVALIDTARGET);
              }
            /* protect the list */
            rsbac_write_lock(&ipc_list_head.lock, &flags);
            ipc_item_p = lookup_ipc(tid.ipc);
            if (!ipc_item_p)
              { /* Not found -> add a new item with default values */
                ipc_item_p = add_ipc_item(default_ipc_aci);
                /* if could not add -> return error */
                if (!ipc_item_p)
                  {
                    printk(KERN_CRIT
                           "rsbac_set_attr(): Could not add ipc item!\n");
                    /* free list lock */
                    rsbac_write_unlock(&ipc_list_head.lock, &flags);
                    return(-RSBAC_ECOULDNOTADDITEM);
                  }
                /* set ipc id - no need to do it earlier, since add() */
                /* does not need it. */
                ipc_item_p->aci.id = tid.ipc;
                if (rsbac_debug_ds)
                  printk(KERN_DEBUG
                         "rsbac_set_attr(): ipc item added.\n");
              }
              
            /* OK, now we can do the attribute setting itself */
            switch (attr)
              {
                case A_security_level:
                  /* security_level SL_rsbac_internal is only set internally */
                  if (   (value.security_level == SL_rsbac_internal)
                      || (ipc_item_p->aci.sec_level == SL_rsbac_internal) )
                    {
                      err = -RSBAC_EINTERNONLY;
                      printk(KERN_WARNING
                             "rsbac_set_attr(): SL_rsbac_internal for ipc not set.\n");
                    }
                  else
                    ipc_item_p->aci.sec_level = value.security_level;
                  break;
                case A_mac_categories:
                  ipc_item_p->aci.mac_categories = value.mac_categories;
                  break;
                case A_object_category:
                  ipc_item_p->aci.object_category = value.object_category;
                  break;
                case A_data_type:
                  ipc_item_p->aci.data_type = value.data_type;
                  break;
                case A_pm_object_class:
                  ipc_item_p->aci.pm_object_class = value.pm_object_class;
                  break;
                case A_pm_ipc_purpose:
                  ipc_item_p->aci.pm_ipc_purpose = value.pm_ipc_purpose;
                  break;
                case A_pm_object_type:
                  ipc_item_p->aci.pm_object_type = value.pm_object_type;
                  break;
                case A_ms_backbuf:
                  strncpy(ipc_item_p->aci.ms_backbuf,
                          value.ms_backbuf,
                          RSBAC_MS_MAX_STR_LEN);
                  break;
                case A_ms_buflen:
                  ipc_item_p->aci.ms_buflen = value.ms_buflen;
                  break;
                case A_ms_str_nr:
                  for(i=0; i<RSBAC_MS_NR_MALWARE; i++)
                    ipc_item_p->aci.ms_str_nr[i] = value.ms_str_nr[i];
                  break;
                case A_ms_str_offset:
                  for(i=0; i<RSBAC_MS_NR_MALWARE; i++)
                    ipc_item_p->aci.ms_str_offset[i] = value.ms_str_offset[i];
                  break;
                case A_ms_scanned:
                  ipc_item_p->aci.ms_scanned = value.ms_scanned;
                  break;
                case A_rc_type:
                  ipc_item_p->aci.rc_type = value.rc_type;
                  break;
                default:
                  err = -RSBAC_EINVALIDATTR;
              }
            /* we are ready, so unprotect the list */
            rsbac_write_unlock(&ipc_list_head.lock, &flags);
            /* and return                          */
            break;
            
          case T_USER:
            if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_set_attr(): Setting user attribute %i for %u to %i\n",
                     attr, tid.user, value.dummy);
            /* protect the list */
            rsbac_write_lock(&user_list_head.lock, &flags);
            u_item_p = lookup_user(tid.user);
            if (!u_item_p)
              { /* Not found -> add a new item with default values */
                u_item_p = add_user_item(default_u_aci);
                /* if could not add -> return error */
                if (!u_item_p)
                  {
                    printk(KERN_CRIT "%s\n",
                          "rsbac_set_attr(): Could not add user item!");
                    /* free list lock */
                    rsbac_write_unlock(&user_list_head.lock, &flags);
                    return(-RSBAC_ECOULDNOTADDITEM);
                  }
                /* set file/dir id - no need to do it earlier, since add() */
                /* does not need it. */
                u_item_p->aci.id = tid.user;
                if (rsbac_debug_ds) printk(KERN_DEBUG "%s\n",
                            "rsbac_set_attr(): user item added.");
                /* user list changed, but changed again later, so don't */
                /* write_user_list(); */
              }
              
            /* OK, now we can do the attribute setting itself */
            switch (attr)
              {
                case A_pseudo:
                  u_item_p->aci.pseudo = value.pseudo;
                  break;
                case A_security_level:
                  /* security_level SL_rsbac_internal is only set internally */
                  if (value.security_level == SL_rsbac_internal)
                    {
                      err = -RSBAC_EINTERNONLY;
                      printk(KERN_WARNING "%s\n",
                      "rsbac_set_attr(): SL_rsbac_internal for user not set.");
                    }
                  else
                    u_item_p->aci.access_appr = value.security_level;
                  break;
                case A_mac_categories:
                  u_item_p->aci.mac_categories = value.mac_categories;
                  break;
                case A_system_role:
                  u_item_p->aci.system_role = value.system_role;
                  break;
                case A_pm_task_set:
                  u_item_p->aci.pm_task_set = value.pm_task_set;
                  break;
                case A_pm_role:
                  u_item_p->aci.pm_role = value.pm_role;
                  break;
                case A_rc_def_role:
                  u_item_p->aci.rc_def_role = value.rc_def_role;
                  break;
                case A_log_user_based:
                  u_item_p->aci.log_user_based = value.log_user_based;
                  break;
                default:
                  err = -RSBAC_EINVALIDATTR;
              }
            /* we are ready, so unprotect the list */
            rsbac_write_unlock(&user_list_head.lock, &flags);
            /* if changed, mark dirty */
            if (!err)
              {
                user_list_head.dirty = TRUE;
              }
            /* and return                          */
            break;
            
          case T_PROCESS:
/*            if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_set_attr(): Setting process attribute\n"); */
            if (!tid.process)
              {
                printk(KERN_WARNING
                       "rsbac_set_attr(): Trying to set attribute for process 0!\n");
                return -RSBAC_EINVALIDTARGET;
              }
            /* protect the list */
            rsbac_write_lock(&process_list_head.lock, &flags);
            p_item_p = lookup_process(tid.process);
            
            if (!p_item_p)
              { /* Not found -> add a new item with default values */
                p_item_p = add_process_item(default_p_aci);
                /* if could not add -> return error */
                if (!p_item_p)
                  {
                    printk(KERN_CRIT
                           "rsbac_set_attr(): Could not add process item!\n");
                    /* free list lock */
                    rsbac_write_unlock(&process_list_head.lock, &flags);
                    return(-RSBAC_ECOULDNOTADDITEM);
                  }
                /* set process id - no need to do it earlier, since add() */
                /* does not need it. */
                p_item_p->aci.id = tid.process;
                if (rsbac_debug_ds)
                  printk(KERN_DEBUG
                         "rsbac_set_attr(): process item added.\n");
              }
            /* OK, now we can do the attribute setting itself */
            switch (attr)
              {
#ifdef CONFIG_RSBAC_USE_RSBAC_OWNER
                case A_owner:
                  p_item_p->aci.owner = value.owner;
                  break;
#endif
                case A_security_level:
                  p_item_p->aci.owner_sec_level = value.security_level;
                  break;
                case A_mac_categories:
                  p_item_p->aci.mac_owner_categories = value.mac_categories;
                  break;
                case A_current_sec_level:
                  p_item_p->aci.current_sec_level = value.current_sec_level;
                  break;
                case A_mac_curr_categories:
                  p_item_p->aci.mac_curr_categories = value.mac_categories;
                  break;
                case A_min_write_open:
                  p_item_p->aci.min_write_open = value.min_write_open;
                  break;
                case A_min_write_categories:
                  p_item_p->aci.min_write_categories = value.mac_categories;
                  break;
                case A_max_read_open:
                  p_item_p->aci.max_read_open = value.max_read_open;
                  break;
                case A_max_read_categories:
                  p_item_p->aci.max_read_categories = value.mac_categories;
                  break;
                case A_mac_auto:
                  p_item_p->aci.mac_auto = value.mac_auto;
                  break;
                case A_mac_trusted:
                  p_item_p->aci.mac_trusted = value.mac_trusted;
                  break;
                case A_pm_tp:
                  p_item_p->aci.pm_tp = value.pm_tp;
                  break;
                case A_pm_current_task:
                  p_item_p->aci.pm_current_task = value.pm_current_task;
                  break;
                case A_pm_process_type:
                  p_item_p->aci.pm_process_type = value.pm_process_type;
                  break;
                case A_ms_trusted:
                  p_item_p->aci.ms_trusted = value.ms_trusted;
                  break;
                case A_ms_sock_trusted_tcp:
                  p_item_p->aci.ms_sock_trusted_tcp = value.ms_sock_trusted_tcp;
                  break;
                case A_ms_sock_trusted_udp:
                  p_item_p->aci.ms_sock_trusted_udp = value.ms_sock_trusted_udp;
                  break;
                case A_rc_role:
                  p_item_p->aci.rc_role = value.rc_role;
                  break;
                case A_rc_type:
                  p_item_p->aci.rc_type = value.rc_type;
                  break;
                case A_rc_force_role:
                  p_item_p->aci.rc_force_role = value.rc_force_role;
                  break;
                case A_auth_may_setuid:
                  p_item_p->aci.auth_may_setuid = value.auth_may_setuid;
                  break;
                case A_auth_may_set_cap:
                  p_item_p->aci.auth_may_set_cap = value.auth_may_set_cap;
                  break;
                case A_log_program_based:
                  p_item_p->aci.log_program_based = value.log_program_based;
                  break;
                default:
                  err = -RSBAC_EINVALIDATTR;
              }
            /* we are ready, so unprotect the list */
            rsbac_write_unlock(&process_list_head.lock, &flags);
            /* and return                          */
            break;

          /* switch target: no valid target */
          default:  
            return(-RSBAC_EINVALIDTARGET);          
        }
      #ifdef CONFIG_RSBAC_XSTATS
      if(!err)
        set_attr_count[target]++;
      #endif
      return(err);
    };      /* end of rsbac_set_attr() */

/************************************************************************** */

#ifdef CONFIG_RSBAC_REG
EXPORT_SYMBOL(rsbac_remove_target);
#endif
int rsbac_remove_target(enum rsbac_target_t target,
                        union rsbac_target_id_t tid)
    { 
      int head_nr;
      int error=0;
      struct rsbac_device_list_item_t   * device_p;
      struct rsbac_file_dir_list_item_t * fd_item_p;
      u_long                              flags,dflags;
    
      if (!rsbac_initialized)
        {
          printk(KERN_WARNING "rsbac_remove_target(): RSBAC not initialized\n");
          return(-RSBAC_ENOTINITIALIZED);
        }
      if (in_interrupt())
        {
          printk(KERN_WARNING "rsbac_remove_target(): called from interrupt!\n");
        }
      switch (target)
        {
          case T_FILE:
          case T_DIR:
            if (rsbac_debug_ds) printk(KERN_DEBUG "%s\n",
                        "rsbac_remove_target(): Removing file/dir ACI");
#if defined(CONFIG_RSBAC_AUTH) || defined(CONFIG_RSBAC_AUTH_MAINT)
            /* file items can also have an auth_f_capset -> remove first */
            if(target == T_FILE)
              error = rsbac_auth_remove_f_capset(tid.file);
#endif
#if defined(CONFIG_RSBAC_ACL) || defined(CONFIG_RSBAC_ACL_MAINT)
            /* file items can also have an acl_fd_item -> remove first */
            error = rsbac_acl_remove_acl(target, tid);
#endif
            /* get list number */
            head_nr = fd_hash(tid.file.inode);

            /* wait for read access to device_list_head */
            rsbac_read_lock(&device_list_head.lock, &dflags);
            /* OK, go on */

            /* lookup device */
            device_p = lookup_device(tid.file.device);
            if (device_p)
              {
                /* protect this list */
                rsbac_write_lock(&device_p->head_table[head_nr].lock, &flags);
                /* look up the item */
                fd_item_p = lookup_file_dir(tid.file.inode, device_p);
                if (fd_item_p)
                  {
                    /* call the remove function */
                    remove_file_dir_item(tid.file.inode, device_p);
                    /* we are ready, so unprotect the list */
                    rsbac_write_unlock(&device_p->head_table[head_nr].lock, &flags);

                    /* since the list was changed, it must be saved to disk or marked */
                    device_p->head_table[head_nr].dirty = TRUE;
                  }
                else
                  {
                    /* we are ready, so unprotect the list */
                    rsbac_write_unlock(&device_p->head_table[head_nr].lock, &flags);
                  }
              }
            else
              error = -RSBAC_EINVALIDDEV;

            /* free access to device_list_head */
            rsbac_read_unlock(&device_list_head.lock, &dflags);
            break;
          case T_DEV:
            if (rsbac_debug_ds) printk(KERN_DEBUG "%s\n",
                        "rsbac_remove_target(): Removing dev ACI");
            /* protect the list */
            rsbac_write_lock(&dev_list_head.lock, &flags);
            /* call the remove function */
            remove_dev_item(tid.dev);
            /* we are ready, so unprotect the list */
            rsbac_write_unlock(&dev_list_head.lock, &flags);
            /* since the list was changed, it must be saved to disk or marked dirty */
            dev_list_head.dirty = TRUE;
            break;
          case T_IPC:
            if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_remove_target(): Removing ipc ACI\n");
            if (   (tid.ipc.type == I_sock)
                && (!tid.ipc.id.sock)
               )
              {
                if(rsbac_debug_ds)
                  printk(KERN_DEBUG
                         "rsbac_remove_target(): called for NULL sock\n");
                return(-RSBAC_EINVALIDTARGET);
              }
            /* protect the list */
            rsbac_write_lock(&ipc_list_head.lock, &flags);
            /* call the remove function */
            remove_ipc_item(tid.ipc);
            /* we are ready, so unprotect the list */
            rsbac_write_unlock(&ipc_list_head.lock, &flags);
            break;
          case T_USER:
            if (rsbac_debug_ds) printk(KERN_DEBUG "%s\n",
                        "rsbac_remove_target(): Removing user ACI");
            /* protect the list */
            rsbac_write_lock(&user_list_head.lock, &flags);
            /* call the remove function */
            remove_user_item(tid.user);
            /* we are ready, so unprotect the list */
            rsbac_write_unlock(&user_list_head.lock, &flags);
            /* since the list was changed, it must be saved to disk or marked dirty */
            user_list_head.dirty = TRUE;
            break;
          case T_PROCESS:
/* too noisy... kicked out.
            if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_remove_target(): Removing process ACI\n"); */
#if defined(CONFIG_RSBAC_AUTH) || defined(CONFIG_RSBAC_AUTH_MAINT)
            /* process items can also have an auth_p_capset -> remove first */
            error = rsbac_auth_remove_p_capset(tid.process);
#endif
#if defined(CONFIG_RSBAC_ACL) || defined(CONFIG_RSBAC_ACL_MAINT)
            /* process items can also have an acl_p_item -> remove first */
            error = rsbac_acl_remove_acl(target, tid);
#endif
            /* protect the list */
            rsbac_write_lock(&process_list_head.lock, &flags);
            /* call the remove function */
            remove_process_item(tid.process);
            /* we are ready, so unprotect the list */
            rsbac_write_unlock(&process_list_head.lock, &flags);
            break;
          default:
            return(-RSBAC_EINVALIDTARGET);          
        }
      #ifdef CONFIG_RSBAC_XSTATS
      remove_count[target]++;
      #endif
      return(error);
    };      /* end of rsbac_remove_target() */


/* end of rsbac_aci_data_structures.c */
