/************************************************* */
/* Rule Set Based Access Control                   */
/* Implementation of RC data structures            */
/* Author and (C) 1999,2000: Amon Ott              */
/*                                                 */
/* Last modified: 18/Sep/2000                      */
/************************************************* */

#include <linux/string.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/file.h>
#include <linux/mm.h>
#include <linux/malloc.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include <rsbac/aci_data_structures.h>
#include <rsbac/rc_types.h>
#include <rsbac/rc_data_structures.h>
#include <rsbac/error.h>
#include <rsbac/helpers.h>
#include <rsbac/fs.h>
#include <rsbac/adf.h>
#include <rsbac/getname.h>
#include <rsbac/rc_getname.h>
#include <rsbac/proc_fs.h>
#include <linux/smp_lock.h>

#ifndef NULL
#define NULL ((void *) 0)
#endif

#ifdef CONFIG_RSBAC_RC_NR_ROLES
#endif

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

/* The following global variables are needed for access to RC data.         */

static struct rsbac_rc_role_entry_t           roles[RSBAC_RC_NR_ROLES];
static struct rsbac_rc_type_entry_t           types[RSBAC_RC_NR_TYPES];

boolean rsbac_rc_roles_dirty = FALSE;
boolean rsbac_rc_types_dirty = FALSE;

static boolean roles_no_write = FALSE;
static boolean types_no_write = FALSE;

static rwlock_t role_lock = RW_LOCK_UNLOCKED;
static rwlock_t type_lock = RW_LOCK_UNLOCKED;

/**************************************************/
/*       Declarations of external functions       */
/**************************************************/

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

/* As some function use later defined functions, we declare those here.   */

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

/* These help functions do NOT handle data consistency protection by  */
/* rw-spinlocks! This is unnecessary due to static arrays!            */

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

void convert_old_role_entry(rsbac_rc_role_id_t role_nr,
                            struct rsbac_rc_old_role_entry_t * old_role_entry_p,
                            struct rsbac_rc_role_entry_t * role_entry_p)
  {
    int i;

    role_entry_p->role_comp = old_role_entry_p->role_comp;
    memcpy(role_entry_p->type_comp_fd, old_role_entry_p->type_comp_fd,
           sizeof(role_entry_p->type_comp_fd));
    memcpy(role_entry_p->type_comp_dev, old_role_entry_p->type_comp_dev,
           sizeof(role_entry_p->type_comp_dev));
    memcpy(role_entry_p->type_comp_process, old_role_entry_p->type_comp_process,
           sizeof(role_entry_p->type_comp_process));
    memcpy(role_entry_p->type_comp_ipc, old_role_entry_p->type_comp_ipc,
           sizeof(role_entry_p->type_comp_ipc));
    memcpy(role_entry_p->type_comp_scd, old_role_entry_p->type_comp_scd,
           sizeof(role_entry_p->type_comp_scd));
    strcpy(role_entry_p->name, old_role_entry_p->name);
    role_entry_p->def_fd_create_type = old_role_entry_p->def_fd_create_type;
    role_entry_p->def_process_create_type = old_role_entry_p->def_process_create_type;
    role_entry_p->def_process_chown_type = old_role_entry_p->def_process_chown_type;
    role_entry_p->def_process_execute_type = old_role_entry_p->def_process_execute_type;
    role_entry_p->def_ipc_create_type = old_role_entry_p->def_ipc_create_type;
    role_entry_p->admin_type = old_role_entry_p->admin_type;
    switch(old_role_entry_p->admin_type)
      {
        case RC_role_admin:
          role_entry_p->admin_roles = -1;
          role_entry_p->assign_roles = -1;
          for(i=0; i<RSBAC_RC_NR_TYPES; i++)
            {
              role_entry_p->type_comp_fd[i] |= RSBAC_RC_SPECIAL_RIGHTS_VECTOR;
              role_entry_p->type_comp_dev[i] |= RSBAC_RC_SPECIAL_RIGHTS_VECTOR;
              role_entry_p->type_comp_process[i] |= RSBAC_RC_SPECIAL_RIGHTS_VECTOR;
              role_entry_p->type_comp_ipc[i] |= RSBAC_RC_SPECIAL_RIGHTS_VECTOR;
              role_entry_p->type_comp_scd[i] |= RSBAC_RC_SPECIAL_RIGHTS_VECTOR;
            }
          break;
        case RC_system_admin:
          role_entry_p->admin_roles = 0;
          role_entry_p->assign_roles = (rsbac_rc_role_vector_t) 1 << role_nr;
          break;
        default:
          role_entry_p->admin_roles = 0;
          role_entry_p->assign_roles = 0;
      }
  };

/*******************/
/* read_roles() */
/* reading the system wide RC role array from root filesystem */

static int read_roles(void)
  {
    struct file                       file;
    int                               err = 0;
    int                               role_nr = 0;
    int                               tmperr;
    struct rsbac_rc_role_entry_t      role_entry;
    struct rsbac_rc_old_role_entry_t  old_role_entry;
    u_int           aci_version;
    mm_segment_t oldfs;

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

        /* OK, now we can start reading */
        /* There is a read function for this file, so read as many items */
        /* 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);
        /* read and compare version */
        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_roles(): 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_RC_ROLE_VERSION:
                break;
            case RSBAC_RC_OLD_ROLE_VERSION:
                printk(KERN_WARNING
                       "read_roles(): upconverting old role entries - check user_comp of all roles!\n");
                if(rsbac_debug_ds)
                  printk(KERN_DEBUG
                         "read_roles(): old role entry version %u, current %u!\n",
                         aci_version, RSBAC_RC_ROLE_VERSION);
                /* set dirty to force saving */
                rsbac_rc_roles_dirty = TRUE;
                break;
            default:
                printk(KERN_WARNING
                       "read_roles(): wrong version %u, expected %u - skipping file and setting no_write for DEV list!\n",
                       aci_version, RSBAC_RC_ROLE_VERSION);
                roles_no_write = TRUE;
                err = -RSBAC_EREADFAILED;
                goto end_read;
          }
	do
	  {
	    switch(aci_version)
	      {
                case RSBAC_RC_ROLE_VERSION:
                    tmperr = file.f_op->read(&file,
	                                     (char *) &role_entry,
	                                     sizeof(role_entry),
	                                     &file.f_pos);
                    break;
                case RSBAC_RC_OLD_ROLE_VERSION:
                    tmperr = file.f_op->read(&file,
	                                     (char *) &old_role_entry,
	                                     sizeof(old_role_entry),
	                                     &file.f_pos);
                    break;
                default:
                    err |= -RSBAC_EREADFAILED;
                    goto end_read;
              }
            /* if successful, add item without writing to disk */
	    if (tmperr > 0)
	      { /* ensure string termination */
                switch(aci_version)
	          {
                    case RSBAC_RC_OLD_ROLE_VERSION:
                        convert_old_role_entry(role_nr, &old_role_entry, &role_entry);
                        break;
                    default:
                  }
	        role_entry.name[RSBAC_RC_NAME_LEN - 1] = (char) 0;
	        memcpy(&roles[role_nr], &role_entry, sizeof(role_entry));
                if (rsbac_debug_ds_rc)
                  printk(KERN_DEBUG "read_roles(): read role %i (%s)\n",
                         role_nr, role_entry.name);

                role_nr++;
	      }
	  }
	while (tmperr > 0 && role_nr < RSBAC_RC_NR_ROLES); /* end of do */

	if (tmperr < 0)
	  {
            printk(KERN_WARNING "%s\n",
                    "read_roles(): read error from file!");
            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_rc)
          printk(KERN_DEBUG "read_roles(): %i entries read.\n",
                 role_nr);
        /* We do not need this file any more */
        rsbac_read_close(&file);
        
        return(err);
  }; /* end of read_roles() */

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

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

    /* test no_write */
    if(roles_no_write)
      return(-RSBAC_ENOTWRITABLE);
    /* allocate mem */
    buflen = sizeof(aci_version) + RSBAC_RC_NR_ROLES * sizeof(struct rsbac_rc_role_entry_t);
    /* 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_roles(): vmalloc used!\n");
      }
    if(!buffer)
      {
        return(-RSBAC_ENOMEM);
      }
    /* copy version */
    memcpy(buffer,
           (char *) &aci_version,
           sizeof(aci_version));
    write_count = sizeof(aci_version);

    /* lock list */
    rsbac_read_lock(&role_lock, &flags);
    /* copy list */
    for(i=0; i<RSBAC_RC_NR_ROLES; i++) 
      {
        memcpy(buffer+write_count,
               &(roles[i]),
	       sizeof(roles[i]));
        write_count += sizeof(roles[i]);
      }
    /* unlock list */
    rsbac_read_unlock(&role_lock, &flags);

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

    /* open file */
    if ((err = rsbac_write_open(RSBAC_RC_ROLE_FILE_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(tmperr > 0)
          {
            written += tmperr;
          }
      }
    if (tmperr < 0)
      {
        printk(KERN_WARNING
               "write_roles(): 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_roles(): %lu entries written.\n",
             write_count / sizeof(struct rsbac_rc_role_entry_t));
    /* 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_roles() */
#endif /* ifndef CONFIG_RSBAC_NO_WRITE */

/*******************/
/* read_types() */
/* reading the system wide RC type array from root filesystem */

static int read_types(void)
  {
    struct file                     file;
    int                             err = 0;
    int                             type_nr = 0;
    int                             tmperr;
    struct rsbac_rc_type_entry_t    type_entry;
    u_int           aci_version;
    mm_segment_t oldfs;

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

        /* OK, now we can start reading */
        /* There is a read function for this file, so read as many items */
        /* 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);
        /* read and compare version */
        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_types(): read error from file!\n");
            err = -RSBAC_EREADFAILED;
            goto end_read;
          }
        /* if wrong aci version, set no_write and skip */
        if (aci_version != RSBAC_RC_TYPE_VERSION)
          {
            printk(KERN_WARNING
                   "read_types(): wrong version %u, expected %u - skipping file and setting no_write for DEV list!\n",
                   aci_version, RSBAC_RC_TYPE_VERSION);
            types_no_write = TRUE;
            err = -RSBAC_EREADFAILED;
            goto end_read;
	  }
	do
	  {
	    tmperr = file.f_op->read(&file,
	                             (char *) &type_entry,
	                             sizeof(type_entry),
	                             &file.f_pos);
            /* if successful, add item without writing to disk */
	    if (tmperr > 0)
	      { /* ensure string termination */
	        type_entry.fd_name[RSBAC_RC_NAME_LEN - 1] = (char) 0;
	        type_entry.dev_name[RSBAC_RC_NAME_LEN - 1] = (char) 0;
	        type_entry.ipc_name[RSBAC_RC_NAME_LEN - 1] = (char) 0;
	        type_entry.process_name[RSBAC_RC_NAME_LEN - 1] = (char) 0;
                /* copy */
	        memcpy(&types[type_nr], &type_entry, sizeof(type_entry));
                if (rsbac_debug_ds_rc)
                  printk(KERN_DEBUG "read_types(): read type %i (%s, %s, %s, %s)\n",
                         type_nr,
                         type_entry.fd_name,
                         type_entry.dev_name,
                         type_entry.ipc_name,
                         type_entry.process_name);

                type_nr++;
	      }
	  }
	while (tmperr > 0 && type_nr < RSBAC_RC_NR_TYPES); /* end of do */

	if (tmperr < 0)
	  {
            printk(KERN_WARNING "read_types(): 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_rc)
          printk(KERN_DEBUG "read_types(): %i entries read.\n",
                 type_nr);
        /* We do not need this file any more */
        rsbac_read_close(&file);
        
        return(err);
  }; /* end of read_types() */

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

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

    /* test no_write */
    if(types_no_write)
      return(-RSBAC_ENOTWRITABLE);
    /* allocate mem */
    buflen = sizeof(aci_version) + RSBAC_RC_NR_TYPES * sizeof(struct rsbac_rc_type_entry_t);
    /* 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_types(): vmalloc used!\n");
      }
    if(!buffer)
      {
        return(-RSBAC_ENOMEM);
      }
    /* copy version */
    memcpy(buffer,
           (char *) &aci_version,
           sizeof(aci_version));
    write_count = sizeof(aci_version);

    /* lock list */
    rsbac_read_lock(&type_lock, &flags);
    /* copy list */
    for(i=0; i<RSBAC_RC_NR_TYPES; i++) 
      {
        memcpy(buffer+write_count,
               &(types[i]),
	       sizeof(types[i]));
        write_count += sizeof(types[i]);
      }
    /* unlock list */
    rsbac_read_unlock(&type_lock, &flags);

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

    /* open file */
    if ((err = rsbac_write_open(RSBAC_RC_TYPE_FILE_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(tmperr > 0)
          {
            written += tmperr;
          }
      }
    if (tmperr < 0)
      {
        printk(KERN_WARNING
               "write_types(): 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_types(): %lu entries written.\n",
             write_count / sizeof(struct rsbac_rc_type_entry_t));
    /* 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_types() */
#endif /* ifndef CONFIG_RSBAC_NO_WRITE */

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

#if defined(CONFIG_RSBAC_PROC) && defined(CONFIG_PROC_FS)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
static int
stats_rc_proc_info(char *buffer, char **start, off_t offset, int length, int dummy)
#else
static int
stats_rc_proc_info(char *buffer, char **start, off_t offset, int length)
#endif
{
    union rsbac_target_id_t rsbac_target_id;
    union rsbac_attribute_value_t rsbac_attribute_value;

    u_int len = 0;
    off_t pos   = 0;
    off_t begin = 0;
    int   i;
    int   count=0;
    int   fd_count=0;
    int   dev_count=0;
    int   ipc_count=0;
    int   proc_count=0;

    if (!rsbac_is_initialized)
      {
        printk(KERN_WARNING "stats_rc_proc_info(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    if (rsbac_debug_aef_rc)
      printk(KERN_DEBUG "stats_rc_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, "RC Status\n---------\n");

    if(rsbac_rc_roles_dirty)
        len += sprintf(buffer + len, "Role array is dirty\n");
    else
        len += sprintf(buffer + len, "Role array is clean\n");
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    for (i=0; i<RSBAC_RC_NR_ROLES; i++)
      if(roles[i].name[0] != (char) 0)
        count++;
    len += sprintf(buffer + len, "Role entry size is %i, %i of %i entries used, mem usage is %i\n",
           sizeof(struct rsbac_rc_role_entry_t),
           count,
           RSBAC_RC_NR_ROLES,
           RSBAC_RC_NR_ROLES * sizeof(struct rsbac_rc_role_entry_t));
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    if(rsbac_rc_types_dirty)
        len += sprintf(buffer + len, "\nType array is dirty\n");
    else
        len += sprintf(buffer + len, "\nType array is clean\n");
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    len += sprintf(buffer + len, "Type entry size is %i, %i entries, mem usage is %i\n",
           sizeof(struct rsbac_rc_type_entry_t),
           RSBAC_RC_NR_TYPES,
           RSBAC_RC_NR_TYPES * sizeof(struct rsbac_rc_type_entry_t));
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    for (i=0; i<RSBAC_RC_NR_TYPES; i++)
      {
        if(types[i].fd_name[0] != (char) 0)
          fd_count++;
        if(types[i].dev_name[0] != (char) 0)
          dev_count++;
        if(types[i].ipc_name[0] != (char) 0)
          ipc_count++;
        if(types[i].process_name[0] != (char) 0)
          proc_count++;
      }
    len += sprintf(buffer + len, "Used type entries: fd: %i, dev: %i, ipc: %i, process: %i\n",
           fd_count,
           dev_count,
           ipc_count,
           proc_count);
    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;
}

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

  int             i;
  u_long          flags;
  u_int           aci_version = RSBAC_RC_ROLE_VERSION;

  union rsbac_target_id_t         rsbac_target_id;
  union rsbac_attribute_value_t   rsbac_attribute_value;

  if (!rsbac_is_initialized())
    return (-ENOSYS);

  if (rsbac_debug_aef_rc)
    printk(KERN_DEBUG "rc_role_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;
    }

  /* copy version */
  memcpy(buffer,
         &aci_version,
         sizeof(aci_version));

  len = sizeof(aci_version);
  pos = begin + len;
  if (pos < offset)
    {
      len = 0;
      begin = pos;
    }
  if (pos > offset+length)
    goto out;

  /* lock list */
  rsbac_read_lock(&role_lock, &flags);

  /* copy list */
  for(i=0; i<RSBAC_RC_NR_ROLES; i++) 
    {
      memcpy(buffer+len,
             &(roles[i]),
             sizeof(roles[i]));
      len += sizeof(roles[i]);
      pos = begin + len;
      if (pos < offset)
        {
          len = 0;
          begin = pos;
        }
      if (pos > offset+length)
        break;
    }

  /* unlock list */
  rsbac_read_unlock(&role_lock, &flags);

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

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

  int             i;
  u_long          flags;
  u_int           aci_version = RSBAC_RC_TYPE_VERSION;

  union rsbac_target_id_t         rsbac_target_id;
  union rsbac_attribute_value_t   rsbac_attribute_value;

  if (!rsbac_is_initialized())
    return (-ENOSYS);

  if (rsbac_debug_aef_rc)
    printk(KERN_DEBUG "rc_type_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;
    }

  /* copy version */
  memcpy(buffer,
         &aci_version,
         sizeof(aci_version));

  len = sizeof(aci_version);
  pos = begin + len;
  if (pos < offset)
    {
      len = 0;
      begin = pos;
    }
  if (pos > offset+length)
    goto out;

  /* lock list */
  rsbac_read_lock(&type_lock, &flags);

  /* copy list */
  for(i=0; i<RSBAC_RC_NR_TYPES; i++) 
    {
      memcpy(buffer+len,
             &(types[i]),
             sizeof(types[i]));
      len += sizeof(types[i]);
      pos = begin + len;
      if (pos < offset)
        {
          len = 0;
          begin = pos;
        }
      if (pos > offset+length)
        break;
    }

  /* unlock list */
  rsbac_read_unlock(&type_lock, &flags);

out:
  *start = buffer + (offset - begin);
  len -= (offset - begin);
  
  if (len > length)
    len = length;
  return len;
}
#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 of all RC data structures. After this call, all RC data   */
/* is kept in memory for performance reasons, but is written to disk on     */
/* every change.    */

/* There can be no access to aci data structures before init.               */

struct rsbac_rc_role_entry_t role0 __initdata = RSBAC_RC_GENERAL_ROLE_ENTRY;
struct rsbac_rc_role_entry_t role1 __initdata = RSBAC_RC_ROLE_ADMIN_ROLE_ENTRY;
struct rsbac_rc_role_entry_t role2 __initdata = RSBAC_RC_SYSTEM_ADMIN_ROLE_ENTRY;
struct rsbac_rc_type_entry_t type0 __initdata = RSBAC_RC_GENERAL_TYPE_ENTRY;
struct rsbac_rc_type_entry_t type1 __initdata = RSBAC_RC_SECURITY_TYPE_ENTRY;
struct rsbac_rc_type_entry_t type2 __initdata = RSBAC_RC_SYSTEM_TYPE_ENTRY;

inline int __init rsbac_init_rc(void)
  {
    int  err = 0;
    int i;
    int  tmperr = 0;
    u_long   flags, tflags;
    struct proc_dir_entry * tmp_entry_p;

    if (rsbac_is_initialized())
      {
        printk(KERN_WARNING "rsbac_init_rc(): RSBAC already initialized\n");
        return(-RSBAC_EREINIT);
      }

    /* init data structures */
    printk(KERN_INFO "rsbac_init_rc(): Initializing RSBAC: RC subsystem\n");

    /* spinlocks initialized on declaration */
    /* clear role and type arrays */
    rsbac_write_lock(&role_lock, &flags);
    for(i=0; i<RSBAC_RC_NR_ROLES; i++)
      memset(&(roles[i]), 0, sizeof(struct rsbac_rc_role_entry_t) );
    rsbac_write_lock(&type_lock, &tflags);
    for(i=0; i<RSBAC_RC_NR_TYPES; i++)
      memset(&(types[i]), 0, sizeof(struct rsbac_rc_type_entry_t) );
    /* set default values */
    memcpy(&roles[0], &role0, sizeof(struct rsbac_rc_role_entry_t));
    memcpy(&roles[1], &role1, sizeof(struct rsbac_rc_role_entry_t));
    for(i=0; i<RSBAC_RC_NR_TYPES; i++)
      {
        roles[1].type_comp_fd[i] |= RSBAC_RC_SPECIAL_RIGHTS_VECTOR;
        roles[1].type_comp_dev[i] |= RSBAC_RC_SPECIAL_RIGHTS_VECTOR;
        roles[1].type_comp_process[i] |= RSBAC_RC_SPECIAL_RIGHTS_VECTOR;
        roles[1].type_comp_ipc[i] |= RSBAC_RC_SPECIAL_RIGHTS_VECTOR;
        roles[1].type_comp_scd[i] |= RSBAC_RC_SPECIAL_RIGHTS_VECTOR;
      }
    memcpy(&roles[2], &role2, sizeof(struct rsbac_rc_role_entry_t));
    memcpy(&types[0], &type0, sizeof(struct rsbac_rc_type_entry_t));
    memcpy(&types[1], &type1, sizeof(struct rsbac_rc_type_entry_t));
    memcpy(&types[2], &type2, sizeof(struct rsbac_rc_type_entry_t));
    rsbac_write_unlock(&type_lock, &tflags);
    /* read all data */
    if (rsbac_debug_ds_rc)
      printk(KERN_DEBUG "rsbac_init_rc(): Reading data\n");

    /* read role array from root device */      
    if (rsbac_debug_ds_rc)
      printk(KERN_DEBUG "rsbac_init_rc(): reading roles\n");
    if ((tmperr = read_roles()))
      {
        char * tmp = kmalloc(RSBAC_MAXNAMELEN, GFP_KERNEL);

        if(tmp)
          {
            printk(KERN_WARNING
                   "rsbac_init_rc(): roles could not be sufficiently read, error %s, default role entries might be used!\n",
                   get_error_name(tmp, tmperr));
            kfree(tmp);
          }
        err |= tmperr;
      };
    rsbac_write_unlock(&role_lock, &flags);

    /* read type array from root device */      
    if (rsbac_debug_ds_rc)
      printk(KERN_DEBUG "rsbac_init_rc(): reading types\n");
    rsbac_write_lock(&type_lock, &tflags);
    if ((tmperr = read_types()))
      {
        char * tmp = kmalloc(RSBAC_MAXNAMELEN, GFP_KERNEL);

        if(tmp)
          {
            printk(KERN_WARNING
                   "rsbac_init_rc(): types could not be sufficiently read, error %s, default type entries might be used!\n",
                   get_error_name(tmp, tmperr));
            kfree(tmp);
          }
        err |= tmperr;
      };
    rsbac_write_unlock(&type_lock, &tflags);

    #if defined(CONFIG_RSBAC_PROC) && defined(CONFIG_PROC_FS)
    tmp_entry_p = create_proc_entry("stats_rc",
                                    S_IFREG | S_IRUGO,
                                    proc_rsbac_root_p);
    if(tmp_entry_p)
      {
        tmp_entry_p->get_info = stats_rc_proc_info;
      }

    tmp_entry_p = create_proc_entry("rc_role",
                                    S_IFREG | S_IRUGO,
                                    proc_rsbac_backup_p);
    if(tmp_entry_p)
      {
        tmp_entry_p->get_info = rc_role_proc_info;
        tmp_entry_p->size =
          sizeof(int) + RSBAC_RC_NR_ROLES * sizeof(struct rsbac_rc_role_entry_t);
      }
    tmp_entry_p = create_proc_entry("rc_type",
                                    S_IFREG | S_IRUGO,
                                    proc_rsbac_backup_p);
    if(tmp_entry_p)
      {
        tmp_entry_p->get_info = rc_type_proc_info;
        tmp_entry_p->size =
          sizeof(int) + RSBAC_RC_NR_TYPES * sizeof(struct rsbac_rc_type_entry_t);
      }
    #endif

    if (rsbac_debug_ds_rc)
      printk(KERN_DEBUG "rsbac_init_rc(): Ready.\n");
    return(err);
  };

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

int rsbac_stats_rc(void)
  {
    int   i;
    int   count=0;
    int   fd_count=0;
    int   dev_count=0;
    int   ipc_count=0;
    int   proc_count=0;

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

    if(rsbac_rc_roles_dirty)
        printk(KERN_INFO "Role array is dirty\n");
    else
        printk(KERN_INFO "Role array is clean\n");

    for (i=0; i<RSBAC_RC_NR_ROLES; i++)
      if(roles[i].name[0] != (char) 0)
        count++;
    printk(KERN_INFO "Role entry size is %i, %i of %i entries used, mem usage is %i\n",
           sizeof(struct rsbac_rc_role_entry_t),
           count,
           RSBAC_RC_NR_ROLES,
           RSBAC_RC_NR_ROLES * sizeof(struct rsbac_rc_role_entry_t));

    if(rsbac_rc_types_dirty)
        printk(KERN_INFO "Type array is dirty\n");
    else
        printk(KERN_INFO "Type array is clean\n");

    printk(KERN_INFO "Type entry size is %i, %i entries, mem usage is %i\n",
           sizeof(struct rsbac_rc_type_entry_t),
           RSBAC_RC_NR_TYPES,
           RSBAC_RC_NR_TYPES * sizeof(struct rsbac_rc_type_entry_t));
    for (i=0; i<RSBAC_RC_NR_TYPES; i++)
      {
        if(types[i].fd_name[0] != (char) 0)
          fd_count++;
        if(types[i].dev_name[0] != (char) 0)
          dev_count++;
        if(types[i].ipc_name[0] != (char) 0)
          ipc_count++;
        if(types[i].process_name[0] != (char) 0)
          proc_count++;
      }
    printk(KERN_INFO "Used type entries: fd: %i, dev: %i, ipc: %i, process: %i\n",
           fd_count,
           dev_count,
           ipc_count,
           proc_count);

    return(0);
  };

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

#if defined(CONFIG_RSBAC_AUTO_WRITE)
int rsbac_write_rc(boolean need_lock)
  {
    u_int  count = 0;
  
    if (!rsbac_is_initialized)
      {
        printk(KERN_WARNING "rsbac_write_rc(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }

    if(need_lock)
      lock_kernel();

    /* Role array */
    if(rsbac_rc_roles_dirty)
      {
        rsbac_rc_roles_dirty = FALSE;
        if(!write_roles())
          {
            count++;
            if(rsbac_debug_ds_rc)
              printk(KERN_DEBUG "rsbac_write_rc(): roles written\n");
          }
        else
          rsbac_rc_roles_dirty = TRUE;
      }

    /* Type array */
    if(rsbac_rc_types_dirty)
      {
        rsbac_rc_types_dirty = FALSE;
        if(!write_types())
          {
            count++;
            if(rsbac_debug_ds_rc)
              printk(KERN_DEBUG "rsbac_write_rc(): types written\n");
          }
        else
          rsbac_rc_types_dirty = TRUE;
      }

/*    if(rsbac_debug_ds_rc)
      printk(KERN_DEBUG "rsbac_write_rc(): %u lists written\n",count); */

    if(need_lock)
      unlock_kernel();

    return(count);
  };
#endif /* CONFIG_RSBAC_AUTO_WRITE */

/************************************************* */
/*               Access functions                  */
/************************************************* */

/* Invalid parameter combinations return an error. */

/* Only copy_role user write rw-spinlock to protect the targets during copy */

/* All roles are always there, so instead of creation, we supply a copy for */
/* initialization. There is always the well-defined role general to copy    */
int rsbac_rc_copy_role (rsbac_rc_role_id_t from_role,
                        rsbac_rc_role_id_t to_role)
  {
    u_long flags;

    if (!rsbac_is_initialized())
      {
        printk(KERN_WARNING "rsbac_rc_copy_role(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    if (in_interrupt())
      {
        printk(KERN_WARNING "rsbac_rc_copy_role(): called from interrupt!\n");
      }
    if(   (from_role > RSBAC_RC_MAX_ROLE)
       || (to_role > RSBAC_RC_MAX_ROLE)
       || (to_role == from_role)
      )
      return(-RSBAC_EINVALIDTARGET);
    
    /* lock list */
    rsbac_write_lock(&role_lock, &flags);
    /* copy */
    memcpy(&roles[to_role], &roles[from_role], sizeof(struct rsbac_rc_role_entry_t));
    /* unlock list */
    rsbac_write_unlock(&role_lock, &flags);

    return(0);
  };


/* Getting values */
int rsbac_rc_get_item (enum  rsbac_rc_target_t       target,
                       union rsbac_rc_target_id_t    tid,
                       enum  rsbac_rc_item_t         item,
                       union rsbac_rc_item_value_t * value_p)
  {
    u_long flags;
    int err=0;

    if (!rsbac_is_initialized())
      {
        printk(KERN_WARNING "rsbac_rc_get_item(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    if (in_interrupt())
      {
        printk(KERN_WARNING "rsbac_rc_get_item(): called from interrupt!\n");
      }
    switch (target)
      {
        case RT_ROLE:
          if(tid.role > RSBAC_RC_MAX_ROLE)
            return(-RSBAC_EINVALIDTARGET);
          if (rsbac_debug_ds_rc)
            printk(KERN_DEBUG
                     "rsbac_rc_get_item(): getting role item value\n");
          /* lock list */
          rsbac_read_lock(&role_lock, &flags);
            switch (item)
              {
                case RI_role_comp:
                  value_p->role_comp = roles[tid.role].role_comp;
                  break;
                case RI_admin_roles:
                  value_p->admin_roles = roles[tid.role].admin_roles;
                  break;
                case RI_assign_roles:
                  value_p->assign_roles = roles[tid.role].assign_roles;
                  break;
                case RI_type_comp_fd:
                  memcpy(value_p->type_comp, roles[tid.role].type_comp_fd,
                         sizeof(value_p->type_comp) );
                  break;
                case RI_type_comp_dev:
                  memcpy(value_p->type_comp, roles[tid.role].type_comp_dev,
                         sizeof(value_p->type_comp) );
                  break;
                case RI_type_comp_process:
                  memcpy(value_p->type_comp, roles[tid.role].type_comp_process,
                         sizeof(value_p->type_comp) );
                  break;
                case RI_type_comp_ipc:
                  memcpy(value_p->type_comp, roles[tid.role].type_comp_ipc,
                         sizeof(value_p->type_comp) );
                  break;
                case RI_type_comp_scd:
                  memcpy(value_p->type_comp, roles[tid.role].type_comp_scd,
                         sizeof(value_p->type_comp) );
                  break;
                case RI_admin_type:
                  value_p->admin_type = roles[tid.role].admin_type;
                  break;
                case RI_name:
                  strncpy(value_p->name, roles[tid.role].name, RSBAC_RC_NAME_LEN - 1);
                  value_p->name[RSBAC_RC_NAME_LEN - 1] = (char) 0;
                  break;
                case RI_def_fd_create_type:
                  value_p->type_id = roles[tid.role].def_fd_create_type;
                  break;
                case RI_def_process_create_type:
                  value_p->type_id = roles[tid.role].def_process_create_type;
                  break;
                case RI_def_process_chown_type:
                  value_p->type_id = roles[tid.role].def_process_chown_type;
                  break;
                case RI_def_process_execute_type:
                  value_p->type_id = roles[tid.role].def_process_execute_type;
                  break;
                case RI_def_ipc_create_type:
                  value_p->type_id = roles[tid.role].def_ipc_create_type;
                  break;
                default:
                  err = -RSBAC_EINVALIDATTR;
              }
            /* unlock list */
            rsbac_read_unlock(&role_lock, &flags);
            /* return */
            return(err);
            break;

        case RT_TYPE:
          if(tid.type > RSBAC_RC_MAX_TYPE)
            return(-RSBAC_EINVALIDTARGET);
          if (rsbac_debug_ds_rc)
            printk(KERN_DEBUG
                     "rsbac_rc_get_item(): getting type item value\n");
            /* lock list */
            rsbac_read_lock(&type_lock, &flags);
            switch (item)
              {
                case RI_type_fd_name:
                  strncpy(value_p->name, types[tid.type].fd_name, RSBAC_RC_NAME_LEN - 1);
                  value_p->name[RSBAC_RC_NAME_LEN - 1] = (char) 0;
                  break;
                case RI_type_dev_name:
                  strncpy(value_p->name, types[tid.type].dev_name, RSBAC_RC_NAME_LEN - 1);
                  value_p->name[RSBAC_RC_NAME_LEN - 1] = (char) 0;
                  break;
                case RI_type_ipc_name:
                  strncpy(value_p->name, types[tid.type].ipc_name, RSBAC_RC_NAME_LEN - 1);
                  value_p->name[RSBAC_RC_NAME_LEN - 1] = (char) 0;
                  break;
                case RI_type_process_name:
                  strncpy(value_p->name, types[tid.type].process_name, RSBAC_RC_NAME_LEN - 1);
                  value_p->name[RSBAC_RC_NAME_LEN - 1] = (char) 0;
                  break;
                case RI_type_fd_need_secdel:
                  value_p->need_secdel = types[tid.type].fd_need_secdel;
                  break;
                case RI_type_scd_name:
                  {
                    char * tmp;

                    tmp = kmalloc(RSBAC_MAXNAMELEN, GFP_KERNEL);
                    if(!tmp)
                      err = -RSBAC_ENOMEM;
                    else
                      {
                        get_rc_scd_type_name(tmp,tid.type);
                        strncpy(value_p->name, tmp, RSBAC_RC_NAME_LEN - 1);
                        value_p->name[RSBAC_RC_NAME_LEN - 1] = (char) 0;
                        kfree(tmp);
                      }
                    break;
                  }
                default:
                  err = -RSBAC_EINVALIDATTR;
              }
            /* unlock list */
            rsbac_read_unlock(&type_lock, &flags);
            /* and return */
            return(err);
            break;

        /* switch target: no valid target */
        default:  
          err = -RSBAC_EINVALIDTARGET;
      }
    return err;
  };      /* end of rsbac_rc_get_item() */


/* Setting values */
int rsbac_rc_set_item (enum  rsbac_rc_target_t       target,
                       union rsbac_rc_target_id_t    tid,
                       enum  rsbac_rc_item_t         item,
                       union rsbac_rc_item_value_t   value)
  {
    u_long flags;
    int err=0;

    if (!rsbac_is_initialized())
      {
        printk(KERN_WARNING "rsbac_rc_set_item(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    if (in_interrupt())
      {
        printk(KERN_WARNING "rsbac_rc_set_item(): called from interrupt!\n");
      }
    switch (target)
      {
        case RT_ROLE:
          if(tid.role > RSBAC_RC_MAX_ROLE)
            return(-RSBAC_EINVALIDTARGET);
          if (rsbac_debug_ds_rc)
            printk(KERN_DEBUG
                     "rsbac_rc_set_item(): Setting role item value\n");
            /* lock list */
            rsbac_write_lock(&role_lock, &flags);
            switch (item)
              {
                case RI_role_comp:
                  roles[tid.role].role_comp = value.role_comp;
                  break;
                case RI_admin_roles:
                  roles[tid.role].admin_roles = value.admin_roles;
                  break;
                case RI_assign_roles:
                  roles[tid.role].assign_roles = value.assign_roles;
                  break;
                case RI_type_comp_fd:
                  memcpy(roles[tid.role].type_comp_fd,
                         value.type_comp,
                         sizeof(value.type_comp) );
                  break;
                case RI_type_comp_dev:
                  memcpy(roles[tid.role].type_comp_dev,
                         value.type_comp,
                         sizeof(value.type_comp) );
                  break;
                case RI_type_comp_process:
                  memcpy(roles[tid.role].type_comp_process,
                         value.type_comp,
                         sizeof(value.type_comp) );
                  break;
                case RI_type_comp_ipc:
                  memcpy(roles[tid.role].type_comp_ipc,
                         value.type_comp,
                         sizeof(value.type_comp) );
                  break;
                case RI_type_comp_scd:
                  memcpy(roles[tid.role].type_comp_scd,
                         value.type_comp,
                         sizeof(value.type_comp) );
                  break;
                case RI_admin_type:
                  roles[tid.role].admin_type = value.admin_type;
                  break;
                case RI_name:
                  strncpy(roles[tid.role].name, value.name, RSBAC_RC_NAME_LEN - 1);
                  break;
                case RI_def_fd_create_type:
                  if(value.type_id > RC_type_max_value)
                    err = -RSBAC_EINVALIDVALUE;
                  else
                    roles[tid.role].def_fd_create_type = value.type_id;
                  break;
                case RI_def_process_create_type:
                  if(value.type_id > RC_type_max_value)
                    err = -RSBAC_EINVALIDVALUE;
                  else
                    roles[tid.role].def_process_create_type = value.type_id;
                  break;
                case RI_def_process_chown_type:
                  if(value.type_id > RC_type_max_value)
                    err = -RSBAC_EINVALIDVALUE;
                  else
                    roles[tid.role].def_process_chown_type = value.type_id;
                  break;
                case RI_def_process_execute_type:
                  if(value.type_id > RC_type_max_value)
                    err = -RSBAC_EINVALIDVALUE;
                  else
                    roles[tid.role].def_process_execute_type = value.type_id;
                  break;
                case RI_def_ipc_create_type:
                  if(value.type_id > RC_type_max_value)
                    err = -RSBAC_EINVALIDVALUE;
                  else
                    roles[tid.role].def_ipc_create_type = value.type_id;
                  break;

                default:
                  err = -RSBAC_EINVALIDATTR;
              }
            /* unlock list */
            rsbac_write_unlock(&role_lock, &flags);
            /* save roles */
            if(!err)
              rsbac_rc_roles_dirty = TRUE;
            /* and return */
            return(err);
            break;

        case RT_TYPE:
          if(tid.type > RSBAC_RC_MAX_TYPE)
            return(-RSBAC_EINVALIDTARGET);
          if (rsbac_debug_ds_rc)
            printk(KERN_DEBUG
                     "rsbac_rc_set_item(): Setting type item value\n");
            /* lock list */
            rsbac_write_lock(&type_lock, &flags);
            switch (item)
              {
                case RI_type_fd_name:
                  strncpy(types[tid.type].fd_name, value.name, RSBAC_RC_NAME_LEN - 1);
                  break;
                case RI_type_dev_name:
                  strncpy(types[tid.type].dev_name, value.name, RSBAC_RC_NAME_LEN - 1);
                  break;
                case RI_type_ipc_name:
                  strncpy(types[tid.type].ipc_name, value.name, RSBAC_RC_NAME_LEN - 1);
                  break;
                case RI_type_process_name:
                  strncpy(types[tid.type].process_name, value.name, RSBAC_RC_NAME_LEN - 1);
                  break;
                case RI_type_fd_need_secdel:
                  types[tid.type].fd_need_secdel = value.need_secdel;
                  break;
                default:
                  err = -RSBAC_EINVALIDATTR;
              }
            /* unlock list */
            rsbac_write_unlock(&type_lock, &flags);
            /* save types */
            if(!err)
              rsbac_rc_types_dirty = TRUE;
            /* and return */
            return(err);
            break;

        /* switch target: no valid target */
        default:  
          err = -RSBAC_EINVALIDTARGET;
      }
    return(err);
  };      /* end of rsbac_rc_set_item() */

/* end of rc_data_structures.c */
