/************************************************* */
/* Rule Set Based Access Control                   */
/* Implementation of ACL data structures           */
/* Author and (c) 1999,2000: Amon Ott              */
/*                                                 */
/* Last modified: 17/Oct/2000                      */
/************************************************* */

#include <linux/types.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/malloc.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/ext2_fs.h>
#include <asm/uaccess.h>
#include <rsbac/types.h>
#include <rsbac/aci_data_structures.h>
#include <rsbac/acl_data_structures.h>
#include <rsbac/error.h>
#include <rsbac/helpers.h>
#include <rsbac/adf.h>
#include <rsbac/aci.h>
#include <rsbac/acl.h>
#include <rsbac/proc_fs.h>
#include <rsbac/getname.h>
#include <rsbac/acl_getname.h>
#include <linux/string.h>
#include <linux/smp_lock.h>

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

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

static struct rsbac_acl_device_list_head_t      device_list_head;
static struct rsbac_acl_dev_list_head_t         dev_list_head;
static struct rsbac_acl_scd_list_head_t         scd_list_head;

static struct rsbac_acl_group_list_head_t       group_list_head;
static struct rsbac_acl_gm_list_head_t          gm_list_head;

static struct rsbac_acl_fd_list_item_t          default_fd_acl;
static        rwlock_t                          default_fd_lock;
static        boolean                           default_fd_dirty;
static struct rsbac_acl_dev_list_item_t         default_dev_acl;
static        rwlock_t                          default_dev_lock;
static        boolean                           default_dev_dirty;
static struct rsbac_acl_ipc_list_item_t         default_ipc_acl;
static        rwlock_t                          default_ipc_lock;
static        boolean                           default_ipc_dirty;
static struct rsbac_acl_scd_list_item_t         default_scd_acl;
static        rwlock_t                          default_scd_lock;
static        boolean                           default_scd_dirty;
static struct rsbac_acl_u_list_item_t           default_u_acl;
static        rwlock_t                          default_u_lock;
static        boolean                           default_u_dirty;
static struct rsbac_acl_p_list_item_t           default_p_acl;
static        rwlock_t                          default_p_lock;
static        boolean                           default_p_dirty;

static        rsbac_acl_rights_vector_t         default_fd_rights = 0;
static        rsbac_acl_rights_vector_t         default_dev_rights = 0;
static        rsbac_acl_rights_vector_t         default_ipc_rights = 0;
static        rsbac_acl_rights_vector_t         default_scd_rights = 0;
static        rsbac_acl_rights_vector_t         default_u_rights = 0;
static        rsbac_acl_rights_vector_t         default_p_rights = 0;

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

boolean writable(struct super_block * sb_p);

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

static struct rsbac_acl_sublist_item_t * 
         add_fd_subitem(struct rsbac_acl_fd_list_item_t * set,
                        struct rsbac_acl_entry_t entry);

static struct rsbac_acl_sublist_item_t * 
         add_dev_subitem(struct rsbac_acl_dev_list_item_t * set,
                         struct rsbac_acl_entry_t entry);

static struct rsbac_acl_sublist_item_t * 
         add_ipc_subitem(struct rsbac_acl_ipc_list_item_t * set,
                         struct rsbac_acl_entry_t entry);

static struct rsbac_acl_sublist_item_t * 
         add_scd_subitem(struct rsbac_acl_scd_list_item_t * set,
                         struct rsbac_acl_entry_t entry);

static struct rsbac_acl_sublist_item_t * 
         add_u_subitem(struct rsbac_acl_u_list_item_t * set,
                       struct rsbac_acl_entry_t entry);

static struct rsbac_acl_sublist_item_t * 
         add_p_subitem(struct rsbac_acl_p_list_item_t * set,
                       struct rsbac_acl_entry_t entry);

static struct rsbac_acl_gm_sublist_item_t * 
         add_gm_subitem(struct rsbac_acl_gm_list_item_t * set,
                        rsbac_acl_group_id_t group);

static struct rsbac_acl_fd_list_item_t *
         do_add_fd_item(struct rsbac_acl_fd_list_head_t * head_p,
                               rsbac_inode_nr_t           id,
                               rsbac_acl_rights_vector_t  mask);

static struct rsbac_acl_fd_list_item_t *
         add_fd_item(struct rsbac_acl_device_list_item_t * device_p,
                            rsbac_inode_nr_t               id,
                            rsbac_acl_rights_vector_t      mask);

static struct rsbac_acl_dev_list_item_t * 
         add_dev_item(struct rsbac_dev_t        id,
                      rsbac_acl_rights_vector_t mask);

static struct rsbac_acl_scd_list_item_t * 
         add_scd_item(enum rsbac_scd_type_t     id,
                      rsbac_acl_rights_vector_t mask);

static struct rsbac_acl_group_list_item_t * 
         add_group_item(struct rsbac_acl_group_entry_t entry);

static struct rsbac_acl_gm_list_item_t * 
         add_gm_item(rsbac_uid_t user);

static struct rsbac_acl_fd_list_item_t *
         do_lookup_fd_item(struct rsbac_acl_fd_list_head_t * head_p,
                                  rsbac_inode_nr_t           id);

static struct rsbac_acl_fd_list_item_t *
  lookup_fd_item(struct rsbac_acl_device_list_item_t * device_p,
                        rsbac_inode_nr_t               id);

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

static inline int fd_hash(rsbac_inode_nr_t inode)
  {
    return(inode % RSBAC_ACL_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 function is only called from rsbac_init_acl(), the write       */
/* function is called on every change from all functions that modify data.  */

/* acl_read_fd_lists() */
/* reading an fd ACL list from filesystem on device */

static int acl_read_fd_lists(struct rsbac_acl_device_list_item_t * device_p,
                               kdev_t kdev)
  {
    struct file                     file;
    char                          * name;
    int                             err = 0;
    char                            number[10];
    u_int                           i;
    u_int                           file_no;
    int                             tmperr;
    u_long                          read_count = 0;
    struct rsbac_acl_fd_list_item_t * item;
    u_long                          id;
    rsbac_acl_rights_vector_t       mask;
    struct rsbac_acl_entry_t        entry;
    struct rsbac_acl_fd_list_head_t * head_p;
    u_int                           len;
    rsbac_version_t                 aci_version;
    boolean                         wrong_list;
    mm_segment_t                    oldfs;
    u_long                          flags;

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

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

        /* construct name from base name + number */
        strcpy(name, RSBAC_ACL_FD_FILENAME);
        strcat(name, inttostr(number,file_no) );

        /* open file */
        if ((tmperr = rsbac_read_open (name,
                                       &file,
                                       kdev) ))
          {
            if(!err || (tmperr != RSBAC_ENOTFOUND))
              err=tmperr;
            continue;
          }

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

        /* 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);
        tmperr = file.f_op->read(&file,
                                 (char *) &aci_version,
                                 sizeof(aci_version),
                                 &file.f_pos);
        /* error? */
        if (tmperr < sizeof(aci_version))
          {
            printk(KERN_WARNING
                   "acl_read_fd_lists(): read error from file!\n");
            err = -RSBAC_EREADFAILED;
            goto end_read;
          }
        /* if wrong aci version, set no_write and skip */
        if (aci_version != RSBAC_ACL_VERSION)
          {
            printk(KERN_WARNING
                   "acl_read_fd_lists(): wrong version %u on device %02u:%02u, file %u, expected %u - skipping file and setting no_write for DEV list!\n",
               aci_version,
               MAJOR(kdev),
               MINOR(kdev),
               file_no,
               RSBAC_ACL_VERSION);
            device_p->no_write = TRUE;
            err = -RSBAC_EREADFAILED;
            goto end_read;
          }
        do
          {
            item = NULL;
            /* read id and mask */
            if (   ((tmperr = file.f_op->read(&file,
                                         (char *) &id,
                                         sizeof(id),
                                         &file.f_pos)) > 0)
                && ((tmperr = file.f_op->read(&file,
                                         (char *) &mask,
                                         sizeof(mask),
                                         &file.f_pos)) > 0)
               )
              { /* OK, id and mask successfully read. Now check. */
                if(fd_hash(id) == file_no)
                  { /* item is for this list */
                    if(do_lookup_fd_item(head_p, id))
                      { /* item id exists -> complain and skip */
                        printk(KERN_WARNING
                               "acl_read_fd_lists(): ACL for id %lu on list %i, device %02u:%02u exists -> ignored - run rsbac_check!\n",
                               id, file_no, MAJOR(kdev), MINOR(kdev));
                        /* set dirty for this lists */
                        head_p->dirty = TRUE;
                      }
                    else
                      {
                        item = do_add_fd_item(head_p, id, mask);
                      }
                  }
                else
                  { /* item is for other list, probably error or NR_LISTS changed */
                    if(lookup_fd_item(device_p, id))
                      { /* item id exists in other list -> complain and skip */
                        printk(KERN_WARNING
                               "acl_read_fd_lists(): ACL for id %lu on list %i, device %02u:%02u exists on correct list %i -> ignored - run rsbac_check!\n",
                               id, file_no, MAJOR(kdev), MINOR(kdev), fd_hash(id));
                      }
                    else
                      { /* wrong list -> move (only, if enabled) and tell */
                        if(rsbac_change_nr)
                          {
                            item = add_fd_item(device_p, id, mask);
                            /* set dirty for other list */
                            device_p->head_table[fd_hash(id)].dirty = TRUE;
                          }
                        else
                          {
                            printk(KERN_WARNING
                                   "acl_read_fd_lists(): ACL 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",
                                   id, file_no, MAJOR(kdev), MINOR(kdev), fd_hash(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); */
/*                item = do_add_fd_item(head_p,id, mask); */
                read_count++;
                tmperr = file.f_op->read(&file,
                                         (char *) &len,
                                         sizeof(len),
                                         &file.f_pos);
                /* if successful, read and add len subitems */
                if (tmperr > 0)
                  {
                    for(i=0;i<len;i++)
                      {
                        tmperr = file.f_op->read(&file,
                                                 (char *) &entry,
                                                 sizeof(entry),
                                                 &file.f_pos);
                        if(tmperr > 0)
                          {
                            if (item && !add_fd_subitem(item,
                                                        entry))
                              {
                                printk(KERN_WARNING
                                       "acl_read_fd_lists(): could not add sub-item!\n");
                                i = len;
                                tmperr = -1;
                              }
                          }
                        else
                          {
                            i = len;
                            tmperr = -1;
                          }
                      }
                  }
              }
          }
        while (tmperr > 0); /* end of do */

        if (tmperr < 0)
          {
            printk(KERN_WARNING "acl_read_fd_lists(): read error from file, item %lu!\n",
                   read_count);
            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);

        if (rsbac_debug_ds_acl)
          printk(KERN_DEBUG "acl_read_fd_lists(): %lu entries read from list %i.\n",
                 read_count, file_no);
        /* 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
                       "acl_read_fd_lists(): ACL was moved from list %i, device %02u:%02u, to other list - rsbac_change_nr was set!\n",
                       file_no, MAJOR(kdev), MINOR(kdev));
              }
            else
              {
                printk(KERN_WARNING
                       "acl_read_fd_lists(): ignored other list's ACL on list %i, device %02u:%02u - rsbac_change_nr was not set!\n",
                       file_no, MAJOR(kdev), MINOR(kdev));
              }
          }
      } /* end of 'for all files' */
    kfree(name);
    return(err);
  }; /* end of acl_read_fd_lists() */

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

#ifndef CONFIG_RSBAC_NO_WRITE
static int acl_write_fd_list(       int                            head_nr,
                             struct rsbac_acl_fd_list_head_t     * head_p,
                             struct rsbac_acl_device_list_item_t * device_p)
  {
    struct file                     file;
    char                          * name;
    int                             err = 0;
    char                            number[10];
    struct rsbac_acl_fd_list_item_t * current_p = NULL;
    struct rsbac_acl_sublist_item_t * sub_item_p;
    mm_segment_t oldfs;
    u_long          write_count = 0, written = 0, bytes, buflen = 0;
    int             tmperr = 0;
    u_long          flags;
    rsbac_version_t aci_version = RSBAC_ACL_VERSION;
    char          * buffer = NULL;
    boolean         vmalloc_used = FALSE;

    if(!device_p || !head_p)
      return(-RSBAC_EINVALIDPOINTER);
    /* test no_write */
    if(device_p->no_write)
      return(-RSBAC_ENOTWRITABLE);
    name = kmalloc(RSBAC_MAXNAMELEN, GFP_KERNEL);
    if(!name)
      return -RSBAC_ENOMEM;

    /* protect this list */
    rsbac_read_lock(&head_p->lock, &flags);
    /* calculate buflen */
    buflen = sizeof(aci_version)
           + (head_p->count *
              (  sizeof(current_p->id)
               + sizeof(current_p->mask)
               + sizeof(current_p->sublist_length)
              )
             );
    current_p = head_p->head;
    while (current_p) 
      {
        buflen += current_p->sublist_length * sizeof(sub_item_p->entry);
        current_p = current_p->next;
      }
    /* 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
                 "acl_write_fd_list(): vmalloc used!\n");
      }
    if(!buffer)
      {
        /* unprotect this list */
        rsbac_read_unlock(&head_p->lock, &flags);
        kfree(name);
        return(-RSBAC_ENOMEM);
      }
    /* 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->id,
	       sizeof(current_p->id));
        write_count += sizeof(current_p->id);
        memcpy(buffer+write_count,
               (char *) &current_p->mask,
	       sizeof(current_p->mask));
        write_count += sizeof(current_p->mask);
        memcpy(buffer+write_count,
               (char *) &current_p->sublist_length,
               sizeof(current_p->sublist_length));
        write_count += sizeof(current_p->sublist_length);
        sub_item_p = current_p->sublist_head;
        while (sub_item_p)
          {
            memcpy(buffer+write_count,
                   (char *) &(sub_item_p->entry),
                   sizeof(sub_item_p->entry));
            write_count += sizeof(sub_item_p->entry);
            sub_item_p = sub_item_p->next;
          }
        current_p = current_p->next;
      }
    /* unprotect this list */
    rsbac_read_unlock(&head_p->lock, &flags);

    if (write_count != buflen)
      {
        printk(KERN_WARNING
               "acl_write_fd_list(): length mismatch on file %s on device %02u:%02u, buflen is %lu, write_count is %lu!\n",
               name,
               MAJOR(device_p->id),
               MINOR(device_p->id),
               buflen,
               write_count);
        err |= -RSBAC_EWRITEFAILED;
      }

    /* get rsbac write-to-disk semaphore */
    down(&rsbac_write_sem);
    
    /* construct name from base name + head number */
    strcpy(name, RSBAC_ACL_FD_FILENAME);
    strcat(name, inttostr(number,head_nr) );

    /* 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. */
    /* 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
               "acl_write_fd_list(): write error %i on file %s on device %02u:%02u!\n",
               tmperr,
               name,
               MAJOR(device_p->id),
               MINOR(device_p->id));
        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 "acl_write_fd_list(): %u main lists written to device %02u:%02u, %lu bytes\n",
             head_p->count,
             MAJOR(device_p->id),
             MINOR(device_p->id),
             write_count);
    /* 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);
    kfree(name);
    return(err);
  }; /* end of acl_write_fd_list() */
#endif /* ifndef CONFIG_RSBAC_NO_WRITE */

/* acl_read_default_fd_acl() */
/* reading the default fd ACL list from root device */

static int acl_read_default_fd_acl(void)
  {
    struct file                     file;
    int                             err = 0;
    u_int                           i;
    int                             tmperr;
    struct rsbac_acl_entry_t        entry;
    u_int                           len;
    rsbac_version_t                 aci_version;
    mm_segment_t oldfs;

    /* open file */
    if ((err = rsbac_read_open (RSBAC_ACL_DEF_FD_FILENAME,
                                &file,
                                ROOT_DEV) ))
      {
        return(err);
      }

    /* OK, now we can start reading */
    /* 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
               "acl_read_default_fd_acl(): read error from file!\n");
        err = -RSBAC_EREADFAILED;
        goto end_read;
      }
    /* if wrong aci version, set no_write and skip */
    if (aci_version != RSBAC_ACL_VERSION)
      {
        printk(KERN_WARNING
               "acl_read_default_fd_acl(): wrong version %u, expected %u - skipping file!\n",
               aci_version,
               RSBAC_ACL_VERSION);
        err = -RSBAC_EREADFAILED;
        goto end_read;
      }
    tmperr = file.f_op->read(&file,
                             (char *) &len,
                             sizeof(len),
                             &file.f_pos);
    /* if successful, read and add len subitems */
    if (tmperr > 0)
      {
        for(i=0;i<len;i++)
          {
            tmperr = file.f_op->read(&file,
                                     (char *) &entry,
                                     sizeof(entry),
                                     &file.f_pos);
            if(tmperr > 0)
              {
                if (!add_fd_subitem(&default_fd_acl,
                                    entry))
                  {
                    printk(KERN_WARNING
                           "acl_read_default_fd_acl(): could not add sub-item!\n");
                    i = len;
                    tmperr = -1;
                  }
              }
            else
              {
                i = len;
                tmperr = -1;
              }
          }
      }

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

    if (rsbac_debug_ds_acl)
      printk(KERN_DEBUG "acl_read_default_fd_acl(): %u entries read.\n",
             len);
    /* We do not need this file any more */
    rsbac_read_close(&file);
    return(err);
  }; /* end of acl_read_fd_lists() */

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

#ifndef CONFIG_RSBAC_NO_WRITE
static int acl_write_default_fd_acl(void)
  {
    struct file                     file;
    int                             err = 0;
    struct rsbac_acl_sublist_item_t * sub_item_p;
    mm_segment_t oldfs;
    u_long          write_count = 0, written = 0, bytes, buflen = 0;
    int             tmperr = 0;
    u_long          flags;
    rsbac_version_t aci_version = RSBAC_ACL_VERSION;
    char          * buffer = NULL;
    boolean         vmalloc_used = FALSE;

    /* protect this list */
    rsbac_read_lock(&default_fd_lock, &flags);
    /* calculate buflen */
    buflen =   sizeof(aci_version)
             + sizeof(u_int) /* sublist_len */
             + default_fd_acl.sublist_length * sizeof(sub_item_p->entry);
    /* 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
                 "acl_write_default_fd_acl(): vmalloc used!\n");
      }
    if(!buffer)
      {
        /* unprotect this list */
        rsbac_read_unlock(&default_fd_lock, &flags);
        return(-RSBAC_ENOMEM);
      }
    /* copy version */
    memcpy(buffer,
           (char *) &aci_version,
           sizeof(aci_version));
    write_count = sizeof(aci_version);
    /* copy list */
    memcpy(buffer+write_count,
           (char *) &(default_fd_acl.sublist_length),
           sizeof(default_fd_acl.sublist_length));
    write_count += sizeof(default_fd_acl.sublist_length);
    sub_item_p = default_fd_acl.sublist_head;
    while (sub_item_p)
      {
        memcpy(buffer+write_count,
               (char *) &(sub_item_p->entry),
               sizeof(sub_item_p->entry));
        write_count += sizeof(sub_item_p->entry);
        sub_item_p = sub_item_p->next;
      }
    /* unprotect this list */
    rsbac_read_unlock(&default_fd_lock, &flags);

    /* get rsbac write-to-disk semaphore */
    down(&rsbac_write_sem);
    
    /* open file */
    if ((err = rsbac_write_open(RSBAC_ACL_DEF_FD_FILENAME,
                                &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
               "acl_write_default_fd_acl(): write error %i on file %s!\n",
               tmperr,
               RSBAC_ACL_DEF_FD_FILENAME);
        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 "acl_write_default_fd_acl(): %u list entries written to device %02u:%02u.\n",
             default_fd_acl.sublist_length,
             MAJOR(ROOT_DEV),
             MINOR(ROOT_DEV));
    /* 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 acl_write_default_fd_acl() */
#endif /* ifndef CONFIG_RSBAC_NO_WRITE */

/* acl_read_dev_list() */
/* read dev ACL list from filesystem on ROOT_DEV */

static int acl_read_dev_list(void)
  {
    struct file                     file;
    int                             err = 0;
    u_int                           i;
    int                             tmperr;
    u_long                          read_count = 0;
    struct rsbac_acl_dev_list_item_t * item;
    struct rsbac_dev_t              id;
    rsbac_acl_rights_vector_t       mask;
    struct rsbac_acl_entry_t        entry;
    u_int                           len;
    rsbac_version_t                 aci_version;
    mm_segment_t oldfs;

    read_count = 0;
    /* open file */
    if ((err = rsbac_read_open (RSBAC_ACL_DEV_FILENAME,
                                &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);
    tmperr = file.f_op->read(&file,
                             (char *) &aci_version,
                             sizeof(aci_version),
                             &file.f_pos);
    /* error? */
    if (tmperr < sizeof(aci_version))
      {
        printk(KERN_WARNING
               "acl_read_dev_list(): read error from file!\n");
        err = -RSBAC_EREADFAILED;
        goto end_read;
      }
    /* if wrong aci version, set no_write and skip */
    if (aci_version != RSBAC_ACL_VERSION)
      {
        printk(KERN_WARNING
               "acl_read_dev_list(): wrong version %u, expected %u - skipping file and setting no_write for DEV list!\n",
               aci_version,
               RSBAC_ACL_VERSION);
        dev_list_head.no_write = TRUE;
        err = -RSBAC_EREADFAILED;
        goto end_read;
      }
    do
      {
        item = NULL;
        /* read id and mask */
        if (   ((tmperr = file.f_op->read(&file,
                                     (char *) &id,
                                     sizeof(id),
                                     &file.f_pos)) > 0)
            && ((tmperr = file.f_op->read(&file,
                                     (char *) &mask,
                                     sizeof(mask),
                                     &file.f_pos)) > 0)
           )
          { /* OK, id and mask successfully read. Now check. */
            item = add_dev_item(id, mask);
            read_count++;
/*            if (rsbac_debug_ds) printk(KERN_DEBUG "read_dev_list(): read item %u\n",
                 id.id); */
            tmperr = file.f_op->read(&file,
                                     (char *) &len,
                                     sizeof(len),
                                     &file.f_pos);
            /* if successful, read and add len subitems */
            if (tmperr > 0)
              {
                for(i=0;i<len;i++)
                  {
                    tmperr = file.f_op->read(&file,
                                             (char *) &entry,
                                             sizeof(entry),
                                             &file.f_pos);
                    if(tmperr > 0)
                      {
                        if (item && !add_dev_subitem(item,
                                                     entry))
                          {
                            printk(KERN_WARNING
                                   "acl_read_dev_list(): could not add sub-item!\n");
                            i = len;
                            tmperr = -1;
                          }
                      }
                    else
                      {
                        i = len;
                        tmperr = -1;
                      }
                  }
              }
          }
      }
    while (tmperr > 0); /* end of do */

    if (tmperr < 0)
      {
        printk(KERN_WARNING "acl_read_dev_list(): read error from file, item %lu!\n",
               read_count);
        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_acl)
      printk(KERN_DEBUG "acl_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 acl_read_dev_list() */

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

#ifndef CONFIG_RSBAC_NO_WRITE
static int acl_write_dev_list(void)
  {
    struct file                     file;
    int                             err = 0;
    struct rsbac_acl_dev_list_item_t * current_p = NULL;
    struct rsbac_acl_sublist_item_t * sub_item_p;
    mm_segment_t oldfs;
    u_long          write_count = 0, written = 0, bytes, buflen = 0;
    int             tmperr = 0;
    u_long          flags;
    rsbac_version_t aci_version = RSBAC_ACL_VERSION;
    char          * buffer = NULL;
    boolean         vmalloc_used = FALSE;

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

    /* protect this list */
    rsbac_read_lock(&dev_list_head.lock, &flags);
    /* calculate buflen */
    buflen = sizeof(aci_version)
           + (dev_list_head.count *
              (  sizeof(current_p->id)
               + sizeof(current_p->mask)
               + sizeof(current_p->sublist_length)
              )
             );
    current_p = dev_list_head.head;
    while (current_p) 
      {
        buflen += current_p->sublist_length * sizeof(sub_item_p->entry);
        current_p = current_p->next;
      }
    /* 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
                 "acl_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->id,
	       sizeof(current_p->id));
        write_count += sizeof(current_p->id);
        memcpy(buffer+write_count,
               (char *) &current_p->mask,
	       sizeof(current_p->mask));
        write_count += sizeof(current_p->mask);
        memcpy(buffer+write_count,
               (char *) &current_p->sublist_length,
               sizeof(current_p->sublist_length));
        write_count += sizeof(current_p->sublist_length);
        sub_item_p = current_p->sublist_head;
        while (sub_item_p)
          {
            memcpy(buffer+write_count,
                   (char *) &(sub_item_p->entry),
                   sizeof(sub_item_p->entry));
            write_count += sizeof(sub_item_p->entry);
            sub_item_p = sub_item_p->next;
          }
        current_p = current_p->next;
      }
    /* unprotect this list */
    rsbac_read_unlock(&dev_list_head.lock, &flags);

    if (write_count != buflen)
      {
        printk(KERN_WARNING
               "acl_write_dev_list(): length mismatch on file %s, buflen is %lu, write_count is %lu!\n",
               RSBAC_ACL_DEV_FILENAME,
               buflen,
               write_count);
        err |= -RSBAC_EWRITEFAILED;
      }

    /* get rsbac write-to-disk semaphore */
    down(&rsbac_write_sem);
    
    /* open file */
    if ((err = rsbac_write_open(RSBAC_ACL_DEV_FILENAME,
                                &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
               "acl_write_dev_list(): write error %i on file %s!\n",
               tmperr,
               RSBAC_ACL_DEV_FILENAME);
        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 "acl_write_dev_list(): %u main lists written, %lu bytes\n",
             dev_list_head.count,
             write_count);
    /* 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 acl_write_dev_list() */
#endif /* ifndef CONFIG_RSBAC_NO_WRITE */

/* acl_read_default_dev_acl() */
/* reading the default device ACL list from root device */

static int acl_read_default_dev_acl(void)
  {
    struct file                     file;
    int                             err = 0;
    u_int                           i;
    int                             tmperr;
    struct rsbac_acl_entry_t        entry;
    u_int                           len;
    rsbac_version_t                 aci_version;
    mm_segment_t oldfs;

    /* open file */
    if ((err = rsbac_read_open (RSBAC_ACL_DEF_DEV_FILENAME,
                                &file,
                                ROOT_DEV) ))
      {
        return(err);
      }

    /* OK, now we can start reading */
    /* 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
               "acl_read_default_dev_acl(): read error from file!\n");
        err = -RSBAC_EREADFAILED;
        goto end_read;
      }
    /* if wrong aci version, set no_write and skip */
    if (aci_version != RSBAC_ACL_VERSION)
      {
        printk(KERN_WARNING
               "acl_read_default_dev_acl(): wrong version %u, expected %u - skipping file!\n",
               aci_version,
               RSBAC_ACL_VERSION);
        err = -RSBAC_EREADFAILED;
        goto end_read;
      }
    tmperr = file.f_op->read(&file,
                             (char *) &len,
                             sizeof(len),
                             &file.f_pos);
    /* if successful, read and add len subitems */
    if (tmperr > 0)
      {
        for(i=0;i<len;i++)
          {
            tmperr = file.f_op->read(&file,
                                     (char *) &entry,
                                     sizeof(entry),
                                     &file.f_pos);
            if(tmperr > 0)
              {
                if (!add_dev_subitem(&default_dev_acl,
                                     entry))
                  {
                    printk(KERN_WARNING
                           "acl_read_default_dev_acl(): could not add sub-item!\n");
                    i = len;
                    tmperr = -1;
                  }
              }
            else
              {
                i = len;
                tmperr = -1;
              }
          }
      }

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

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

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

#ifndef CONFIG_RSBAC_NO_WRITE
static int acl_write_default_dev_acl(void)
  {
    struct file                     file;
    int                             err = 0;
    struct rsbac_acl_sublist_item_t * sub_item_p;
    mm_segment_t oldfs;
    u_long          write_count = 0, written = 0, bytes, buflen = 0;
    int             tmperr = 0;
    u_long          flags;
    rsbac_version_t aci_version = RSBAC_ACL_VERSION;
    char          * buffer = NULL;
    boolean         vmalloc_used = FALSE;

    /* protect this list */
    rsbac_read_lock(&default_dev_lock, &flags);
    /* calculate buflen */
    buflen =   sizeof(aci_version)
             + sizeof(u_int) /* sublist_len */
             + default_dev_acl.sublist_length * sizeof(sub_item_p->entry);
    /* 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
                 "acl_write_default_dev_acl(): vmalloc used!\n");
      }
    if(!buffer)
      {
        /* unprotect this list */
        rsbac_read_unlock(&default_dev_lock, &flags);
        return(-RSBAC_ENOMEM);
      }
    /* copy version */
    memcpy(buffer,
           (char *) &aci_version,
           sizeof(aci_version));
    write_count = sizeof(aci_version);
    /* copy list */
    memcpy(buffer+write_count,
           (char *) &(default_dev_acl.sublist_length),
           sizeof(default_dev_acl.sublist_length));
    write_count += sizeof(default_dev_acl.sublist_length);
    sub_item_p = default_dev_acl.sublist_head;
    while (sub_item_p)
      {
        memcpy(buffer+write_count,
               (char *) &(sub_item_p->entry),
               sizeof(sub_item_p->entry));
        write_count += sizeof(sub_item_p->entry);
        sub_item_p = sub_item_p->next;
      }
    /* unprotect this list */
    rsbac_read_unlock(&default_dev_lock, &flags);

    /* get rsbac write-to-disk semaphore */
    down(&rsbac_write_sem);
    
    /* open file */
    if ((err = rsbac_write_open(RSBAC_ACL_DEF_DEV_FILENAME,
                                &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
               "acl_write_default_dev_acl(): write error %i on file %s!\n",
               tmperr,
               RSBAC_ACL_DEF_DEV_FILENAME);
        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 "acl_write_default_dev_acl(): %u list entries written to device %02u:%02u.\n",
             default_dev_acl.sublist_length,
             MAJOR(ROOT_DEV),
             MINOR(ROOT_DEV));
    /* 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 acl_write_default_dev_acl() */
#endif /* ifndef CONFIG_RSBAC_NO_WRITE */

/* acl_read_scd_list() */
/* read SCD ACL list from filesystem on ROOT_DEV */

static int acl_read_scd_list(void)
  {
    struct file                     file;
    int                             err = 0;
    u_int                           i;
    int                             tmperr;
    u_long                          read_count = 0;
    struct rsbac_acl_scd_list_item_t * item;
    __u8                            id; /* enum rsbac_scd_type_t */
    rsbac_acl_rights_vector_t       mask;
    struct rsbac_acl_entry_t        entry;
    u_int                           len;
    rsbac_version_t                 aci_version;
    mm_segment_t oldfs;

    read_count = 0;
    /* open file */
    if ((err = rsbac_read_open (RSBAC_ACL_SCD_FILENAME,
                                &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);
    tmperr = file.f_op->read(&file,
                             (char *) &aci_version,
                             sizeof(aci_version),
                             &file.f_pos);
    /* error? */
    if (tmperr < sizeof(aci_version))
      {
        printk(KERN_WARNING
               "acl_read_scd_list(): read error from file!\n");
        err = -RSBAC_EREADFAILED;
        goto end_read;
      }
    /* if wrong aci version, set no_write and skip */
    if (aci_version != RSBAC_ACL_VERSION)
      {
        printk(KERN_WARNING
               "acl_read_scd_list(): wrong version %u, expected %u - skipping file and setting no_write for SCD list!\n",
               aci_version,
               RSBAC_ACL_VERSION);
        scd_list_head.no_write = TRUE;
        err = -RSBAC_EREADFAILED;
        goto end_read;
      }
    do
      {
        item = NULL;
        /* read id and mask */
        if (   ((tmperr = file.f_op->read(&file,
                                     (char *) &id,
                                     sizeof(id),
                                     &file.f_pos)) > 0)
            && ((tmperr = file.f_op->read(&file,
                                     (char *) &mask,
                                     sizeof(mask),
                                     &file.f_pos)) > 0)
           )
          { /* OK, id and mask successfully read. Now check. */
            item = add_scd_item(id, mask);
            read_count++;
/*            if (rsbac_debug_ds) printk(KERN_DEBUG "read_scd_list(): read item %u\n",
                 id.id); */
            tmperr = file.f_op->read(&file,
                                     (char *) &len,
                                     sizeof(len),
                                     &file.f_pos);
            /* if successful, read and add len subitems */
            if (tmperr > 0)
              {
                for(i=0;i<len;i++)
                  {
                    tmperr = file.f_op->read(&file,
                                             (char *) &entry,
                                             sizeof(entry),
                                             &file.f_pos);
                    if(tmperr > 0)
                      {
                        if (item && !add_scd_subitem(item,
                                                     entry))
                          {
                            printk(KERN_WARNING
                                   "acl_read_scd_list(): could not add sub-item!\n");
                            i = len;
                            tmperr = -1;
                          }
                      }
                    else
                      {
                        i = len;
                        tmperr = -1;
                      }
                  }
              }
          }
      }
    while (tmperr > 0); /* end of do */

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

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

#ifndef CONFIG_RSBAC_NO_WRITE
static int acl_write_scd_list(void)
  {
    struct file                     file;
    int                             err = 0;
    struct rsbac_acl_scd_list_item_t * current_p = NULL;
    struct rsbac_acl_sublist_item_t * sub_item_p;
    mm_segment_t oldfs;
    u_long          write_count = 0, written = 0, bytes, buflen = 0;
    int             tmperr = 0;
    u_long          flags;
    rsbac_version_t aci_version = RSBAC_ACL_VERSION;
    char          * buffer = NULL;
    boolean         vmalloc_used = FALSE;

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

    /* protect this list */
    rsbac_read_lock(&scd_list_head.lock, &flags);
    /* calculate buflen */
    buflen = sizeof(aci_version)
           + (scd_list_head.count *
              (  sizeof(current_p->id)
               + sizeof(current_p->mask)
               + sizeof(current_p->sublist_length)
              )
             );
    current_p = scd_list_head.head;
    while (current_p) 
      {
        buflen += current_p->sublist_length * sizeof(sub_item_p->entry);
        current_p = current_p->next;
      }
    /* 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
                 "acl_write_scd_list(): vmalloc used!\n");
      }
    if(!buffer)
      {
        /* unprotect this list */
        rsbac_read_unlock(&scd_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 = scd_list_head.head;
    while (current_p) 
      {
        memcpy(buffer+write_count,
               (char *) &current_p->id,
	       sizeof(current_p->id));
        write_count += sizeof(current_p->id);
        memcpy(buffer+write_count,
               (char *) &current_p->mask,
	       sizeof(current_p->mask));
        write_count += sizeof(current_p->mask);
        memcpy(buffer+write_count,
               (char *) &current_p->sublist_length,
               sizeof(current_p->sublist_length));
        write_count += sizeof(current_p->sublist_length);
        sub_item_p = current_p->sublist_head;
        while (sub_item_p)
          {
            memcpy(buffer+write_count,
                   (char *) &(sub_item_p->entry),
                   sizeof(sub_item_p->entry));
            write_count += sizeof(sub_item_p->entry);
            sub_item_p = sub_item_p->next;
          }
        current_p = current_p->next;
      }
    /* unprotect this list */
    rsbac_read_unlock(&scd_list_head.lock, &flags);

    if (write_count != buflen)
      {
        printk(KERN_WARNING
               "acl_write_scd_list(): length mismatch on file %s, buflen is %lu, write_count is %lu!\n",
               RSBAC_ACL_SCD_FILENAME,
               buflen,
               write_count);
        err |= -RSBAC_EWRITEFAILED;
      }

    /* get rsbac write-to-disk semaphore */
    down(&rsbac_write_sem);
    
    /* open file */
    if ((err = rsbac_write_open(RSBAC_ACL_SCD_FILENAME,
                                &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
               "acl_write_scd_list(): write error %i on file %s!\n",
               tmperr,
               RSBAC_ACL_SCD_FILENAME);
        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 "acl_write_scd_list(): %u main lists written, %lu bytes\n",
             scd_list_head.count,
             write_count);
    /* 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 acl_write_scd_list() */
#endif /* ifndef CONFIG_RSBAC_NO_WRITE */

/* acl_read_default_ipc_acl() */
/* reading the default device ACL list from root device */

static int acl_read_default_ipc_acl(void)
  {
    struct file                     file;
    int                             err = 0;
    u_int                           i;
    int                             tmperr;
    struct rsbac_acl_entry_t        entry;
    u_int                           len;
    rsbac_version_t                 aci_version;
    mm_segment_t oldfs;

    /* open file */
    if ((err = rsbac_read_open (RSBAC_ACL_DEF_IPC_FILENAME,
                                &file,
                                ROOT_DEV) ))
      {
        return(err);
      }

    /* OK, now we can start reading */
    /* 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
               "acl_read_default_ipc_acl(): read error from file!\n");
        err = -RSBAC_EREADFAILED;
        goto end_read;
      }
    /* if wrong aci version, set no_write and skip */
    if (aci_version != RSBAC_ACL_VERSION)
      {
        printk(KERN_WARNING
               "acl_read_default_ipc_acl(): wrong version %u, expected %u - skipping file!\n",
               aci_version,
               RSBAC_ACL_VERSION);
        err = -RSBAC_EREADFAILED;
        goto end_read;
      }
    tmperr = file.f_op->read(&file,
                             (char *) &len,
                             sizeof(len),
                             &file.f_pos);
    /* if successful, read and add len subitems */
    if (tmperr > 0)
      {
        for(i=0;i<len;i++)
          {
            tmperr = file.f_op->read(&file,
                                     (char *) &entry,
                                     sizeof(entry),
                                     &file.f_pos);
            if(tmperr > 0)
              {
                if (!add_ipc_subitem(&default_ipc_acl,
                                     entry))
                  {
                    printk(KERN_WARNING
                           "acl_read_default_ipc_acl(): could not add sub-item!\n");
                    i = len;
                    tmperr = -1;
                  }
              }
            else
              {
                i = len;
                tmperr = -1;
              }
          }
      }

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

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

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

#ifndef CONFIG_RSBAC_NO_WRITE
static int acl_write_default_ipc_acl(void)
  {
    struct file                     file;
    int                             err = 0;
    struct rsbac_acl_sublist_item_t * sub_item_p;
    mm_segment_t oldfs;
    u_long          write_count = 0, written = 0, bytes, buflen = 0;
    int             tmperr = 0;
    u_long          flags;
    rsbac_version_t aci_version = RSBAC_ACL_VERSION;
    char          * buffer = NULL;
    boolean         vmalloc_used = FALSE;

    /* protect this list */
    rsbac_read_lock(&default_ipc_lock, &flags);
    /* calculate buflen */
    buflen =   sizeof(aci_version)
             + sizeof(u_int) /* sublist_len */
             + default_ipc_acl.sublist_length * sizeof(sub_item_p->entry);
    /* 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
                 "acl_write_default_ipc_acl(): vmalloc used!\n");
      }
    if(!buffer)
      {
        /* unprotect this list */
        rsbac_read_unlock(&default_ipc_lock, &flags);
        return(-RSBAC_ENOMEM);
      }
    /* copy version */
    memcpy(buffer,
           (char *) &aci_version,
           sizeof(aci_version));
    write_count = sizeof(aci_version);
    /* copy list */
    memcpy(buffer+write_count,
           (char *) &(default_ipc_acl.sublist_length),
           sizeof(default_ipc_acl.sublist_length));
    write_count += sizeof(default_ipc_acl.sublist_length);
    sub_item_p = default_ipc_acl.sublist_head;
    while (sub_item_p)
      {
        memcpy(buffer+write_count,
               (char *) &(sub_item_p->entry),
               sizeof(sub_item_p->entry));
        write_count += sizeof(sub_item_p->entry);
        sub_item_p = sub_item_p->next;
      }
    /* unprotect this list */
    rsbac_read_unlock(&default_ipc_lock, &flags);

    /* get rsbac write-to-disk semaphore */
    down(&rsbac_write_sem);
    
    /* open file */
    if ((err = rsbac_write_open(RSBAC_ACL_DEF_IPC_FILENAME,
                                &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
               "acl_write_default_ipc_acl(): write error %i on file %s!\n",
               tmperr,
               RSBAC_ACL_DEF_IPC_FILENAME);
        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 "acl_write_default_ipc_acl(): %u list entries written to device %02u:%02u.\n",
             default_ipc_acl.sublist_length,
             MAJOR(ROOT_DEV),
             MINOR(ROOT_DEV));
    /* 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 acl_write_default_ipc_acl() */
#endif /* ifndef CONFIG_RSBAC_NO_WRITE */

/* acl_read_default_scd_acl() */
/* reading the default device ACL list from root device */

static int acl_read_default_scd_acl(void)
  {
    struct file                     file;
    int                             err = 0;
    u_int                           i;
    int                             tmperr;
    struct rsbac_acl_entry_t        entry;
    u_int                           len;
    rsbac_version_t                 aci_version;
    mm_segment_t oldfs;

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

    /* OK, now we can start reading */
    /* 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
               "acl_read_default_scd_acl(): read error from file!\n");
        err = -RSBAC_EREADFAILED;
        goto end_read;
      }
    /* if wrong aci version, set no_write and skip */
    if (aci_version != RSBAC_ACL_VERSION)
      {
        printk(KERN_WARNING
               "acl_read_default_scd_acl(): wrong version %u, expected %u - skipping file!\n",
               aci_version,
               RSBAC_ACL_VERSION);
        err = -RSBAC_EREADFAILED;
        goto end_read;
      }
    tmperr = file.f_op->read(&file,
                             (char *) &len,
                             sizeof(len),
                             &file.f_pos);
    /* if successful, read and add len subitems */
    if (tmperr > 0)
      {
        for(i=0;i<len;i++)
          {
            tmperr = file.f_op->read(&file,
                                     (char *) &entry,
                                     sizeof(entry),
                                     &file.f_pos);
            if(tmperr > 0)
              {
                if (!add_scd_subitem(&default_scd_acl,
                                     entry))
                  {
                    printk(KERN_WARNING
                           "acl_read_default_scd_acl(): could not add sub-item!\n");
                    i = len;
                    tmperr = -1;
                  }
              }
            else
              {
                i = len;
                tmperr = -1;
              }
          }
      }

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

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

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

#ifndef CONFIG_RSBAC_NO_WRITE
static int acl_write_default_scd_acl(void)
  {
    struct file                     file;
    int                             err = 0;
    struct rsbac_acl_sublist_item_t * sub_item_p;
    mm_segment_t oldfs;
    u_long          write_count = 0, written = 0, bytes, buflen = 0;
    int             tmperr = 0;
    u_long          flags;
    rsbac_version_t aci_version = RSBAC_ACL_VERSION;
    char          * buffer = NULL;
    boolean         vmalloc_used = FALSE;

    /* protect this list */
    rsbac_read_lock(&default_scd_lock, &flags);
    /* calculate buflen */
    buflen =   sizeof(aci_version)
             + sizeof(u_int) /* sublist_len */
             + default_scd_acl.sublist_length * sizeof(sub_item_p->entry);
    /* 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
                 "acl_write_default_scd_acl(): vmalloc used!\n");
      }
    if(!buffer)
      {
        /* unprotect this list */
        rsbac_read_unlock(&default_scd_lock, &flags);
        return(-RSBAC_ENOMEM);
      }
    /* copy version */
    memcpy(buffer,
           (char *) &aci_version,
           sizeof(aci_version));
    write_count = sizeof(aci_version);
    /* copy list */
    memcpy(buffer+write_count,
           (char *) &(default_scd_acl.sublist_length),
           sizeof(default_scd_acl.sublist_length));
    write_count += sizeof(default_scd_acl.sublist_length);
    sub_item_p = default_scd_acl.sublist_head;
    while (sub_item_p)
      {
        memcpy(buffer+write_count,
               (char *) &(sub_item_p->entry),
               sizeof(sub_item_p->entry));
        write_count += sizeof(sub_item_p->entry);
        sub_item_p = sub_item_p->next;
      }
    /* unprotect this list */
    rsbac_read_unlock(&default_scd_lock, &flags);

    /* get rsbac write-to-disk semaphore */
    down(&rsbac_write_sem);
    
    /* open file */
    if ((err = rsbac_write_open(RSBAC_ACL_DEF_SCD_FILENAME,
                                &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
               "acl_write_default_scd_acl(): write error %i on file %s!\n",
               tmperr,
               RSBAC_ACL_DEF_SCD_FILENAME);
        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 "acl_write_default_scd_acl(): %u list entries written to device %02u:%02u.\n",
             default_scd_acl.sublist_length,
             MAJOR(ROOT_DEV),
             MINOR(ROOT_DEV));
    /* 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 acl_write_default_scd_acl() */
#endif /* ifndef CONFIG_RSBAC_NO_WRITE */

/* acl_read_default_u_acl() */
/* reading the default user ACL list from root device */

static int acl_read_default_u_acl(void)
  {
    struct file                     file;
    int                             err = 0;
    u_int                           i;
    int                             tmperr;
    struct rsbac_acl_entry_t        entry;
    u_int                           len;
    rsbac_version_t                 aci_version;
    mm_segment_t oldfs;

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

    /* OK, now we can start reading */
    /* 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
               "acl_read_default_u_acl(): read error from file!\n");
        err = -RSBAC_EREADFAILED;
        goto end_read;
      }
    /* if wrong aci version, set no_write and skip */
    if (aci_version != RSBAC_ACL_VERSION)
      {
        printk(KERN_WARNING
               "acl_read_default_u_acl(): wrong version %u, expected %u - skipping file!\n",
               aci_version,
               RSBAC_ACL_VERSION);
        err = -RSBAC_EREADFAILED;
        goto end_read;
      }
    tmperr = file.f_op->read(&file,
                             (char *) &len,
                             sizeof(len),
                             &file.f_pos);
    /* if successful, read and add len subitems */
    if (tmperr > 0)
      {
        for(i=0;i<len;i++)
          {
            tmperr = file.f_op->read(&file,
                                     (char *) &entry,
                                     sizeof(entry),
                                     &file.f_pos);
            if(tmperr > 0)
              {
                if (!add_u_subitem(&default_u_acl,
                                    entry))
                  {
                    printk(KERN_WARNING
                           "acl_read_default_u_acl(): could not add sub-item!\n");
                    i = len;
                    tmperr = -1;
                  }
              }
            else
              {
                i = len;
                tmperr = -1;
              }
          }
      }

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

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

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

#ifndef CONFIG_RSBAC_NO_WRITE
static int acl_write_default_u_acl(void)
  {
    struct file                     file;
    int                             err = 0;
    struct rsbac_acl_sublist_item_t * sub_item_p;
    mm_segment_t oldfs;
    u_long          write_count = 0, written = 0, bytes, buflen = 0;
    int             tmperr = 0;
    u_long          flags;
    rsbac_version_t aci_version = RSBAC_ACL_VERSION;
    char          * buffer = NULL;
    boolean         vmalloc_used = FALSE;

    /* protect this list */
    rsbac_read_lock(&default_u_lock, &flags);
    /* calculate buflen */
    buflen =   sizeof(aci_version)
             + sizeof(u_int) /* sublist_len */
             + default_u_acl.sublist_length * sizeof(sub_item_p->entry);
    /* 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
                 "acl_write_default_u_acl(): vmalloc used!\n");
      }
    if(!buffer)
      {
        /* unprotect this list */
        rsbac_read_unlock(&default_u_lock, &flags);
        return(-RSBAC_ENOMEM);
      }
    /* copy version */
    memcpy(buffer,
           (char *) &aci_version,
           sizeof(aci_version));
    write_count = sizeof(aci_version);
    /* copy list */
    memcpy(buffer+write_count,
           (char *) &(default_u_acl.sublist_length),
           sizeof(default_u_acl.sublist_length));
    write_count += sizeof(default_u_acl.sublist_length);
    sub_item_p = default_u_acl.sublist_head;
    while (sub_item_p)
      {
        memcpy(buffer+write_count,
               (char *) &(sub_item_p->entry),
               sizeof(sub_item_p->entry));
        write_count += sizeof(sub_item_p->entry);
        sub_item_p = sub_item_p->next;
      }
    /* unprotect this list */
    rsbac_read_unlock(&default_u_lock, &flags);

    /* get rsbac write-to-disk semaphore */
    down(&rsbac_write_sem);
    
    /* open file */
    if ((err = rsbac_write_open(RSBAC_ACL_DEF_U_FILENAME,
                                &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
               "acl_write_default_u_acl(): write error %i on file %s!\n",
               tmperr,
               RSBAC_ACL_DEF_U_FILENAME);
        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 "acl_write_default_u_acl(): %u list entries written to device %02u:%02u.\n",
             default_u_acl.sublist_length,
             MAJOR(ROOT_DEV),
             MINOR(ROOT_DEV));
    /* 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 acl_write_default_u_acl() */
#endif /* ifndef CONFIG_RSBAC_NO_WRITE */

/* acl_read_default_p_acl() */
/* reading the default process ACL list from root device */

static int acl_read_default_p_acl(void)
  {
    struct file                     file;
    int                             err = 0;
    u_int                           i;
    int                             tmperr;
    struct rsbac_acl_entry_t        entry;
    u_int                           len;
    rsbac_version_t                 aci_version;
    mm_segment_t oldfs;

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

    /* OK, now we can start reading */
    /* 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
               "acl_read_default_p_acl(): read error from file!\n");
        err = -RSBAC_EREADFAILED;
        goto end_read;
      }
    /* if wrong aci version, set no_write and skip */
    if (aci_version != RSBAC_ACL_VERSION)
      {
        printk(KERN_WARNING
               "acl_read_default_p_acl(): wrong version %u, expected %u - skipping file!\n",
               aci_version,
               RSBAC_ACL_VERSION);
        err = -RSBAC_EREADFAILED;
        goto end_read;
      }
    tmperr = file.f_op->read(&file,
                             (char *) &len,
                             sizeof(len),
                             &file.f_pos);
    /* if successful, read and add len subitems */
    if (tmperr > 0)
      {
        for(i=0;i<len;i++)
          {
            tmperr = file.f_op->read(&file,
                                     (char *) &entry,
                                     sizeof(entry),
                                     &file.f_pos);
            if(tmperr > 0)
              {
                if (!add_p_subitem(&default_p_acl,
                                    entry))
                  {
                    printk(KERN_WARNING
                           "acl_read_default_p_acl(): could not add sub-item!\n");
                    i = len;
                    tmperr = -1;
                  }
              }
            else
              {
                i = len;
                tmperr = -1;
              }
          }
      }

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

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

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

#ifndef CONFIG_RSBAC_NO_WRITE
static int acl_write_default_p_acl(void)
  {
    struct file                     file;
    int                             err = 0;
    struct rsbac_acl_sublist_item_t * sub_item_p;
    mm_segment_t oldfs;
    u_long          write_count = 0, written = 0, bytes, buflen = 0;
    int             tmperr = 0;
    u_long          flags;
    rsbac_version_t aci_version = RSBAC_ACL_VERSION;
    char          * buffer = NULL;
    boolean         vmalloc_used = FALSE;

    /* protect this list */
    rsbac_read_lock(&default_p_lock, &flags);
    /* calculate buflen */
    buflen =   sizeof(aci_version)
             + sizeof(u_int) /* sublist_len */
             + default_p_acl.sublist_length * sizeof(sub_item_p->entry);
    /* 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
                 "acl_write_default_p_acl(): vmalloc used!\n");
      }
    if(!buffer)
      {
        /* unprotect this list */
        rsbac_read_unlock(&default_p_lock, &flags);
        return(-RSBAC_ENOMEM);
      }
    /* copy version */
    memcpy(buffer,
           (char *) &aci_version,
           sizeof(aci_version));
    write_count = sizeof(aci_version);
    /* copy list */
    memcpy(buffer+write_count,
           (char *) &(default_p_acl.sublist_length),
           sizeof(default_p_acl.sublist_length));
    write_count += sizeof(default_p_acl.sublist_length);
    sub_item_p = default_p_acl.sublist_head;
    while (sub_item_p)
      {
        memcpy(buffer+write_count,
               (char *) &(sub_item_p->entry),
               sizeof(sub_item_p->entry));
        write_count += sizeof(sub_item_p->entry);
        sub_item_p = sub_item_p->next;
      }
    /* unprotect this list */
    rsbac_read_unlock(&default_p_lock, &flags);

    /* get rsbac write-to-disk semaphore */
    down(&rsbac_write_sem);
    
    /* open file */
    if ((err = rsbac_write_open(RSBAC_ACL_DEF_P_FILENAME,
                                &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
               "acl_write_default_p_acl(): write error %i on file %s!\n",
               tmperr,
               RSBAC_ACL_DEF_P_FILENAME);
        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 "acl_write_default_p_acl(): %u list entries written to device %02u:%02u.\n",
             default_p_acl.sublist_length,
             MAJOR(ROOT_DEV),
             MINOR(ROOT_DEV));
    /* 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 acl_write_default_p_acl() */
#endif /* ifndef CONFIG_RSBAC_NO_WRITE */

/* acl_read_gm_list() */
/* read group membership list from filesystem on ROOT_DEV */

static int acl_read_gm_list(void)
  {
    struct file                     file;
    int                             err = 0;
    u_int                           i;
    int                             tmperr;
    u_long                          read_count = 0;
    struct rsbac_acl_gm_list_item_t * item;
    rsbac_uid_t                     user;
    rsbac_old_uid_t                 old_user;
    rsbac_acl_group_id_t            group;
    u_int                           len;
    rsbac_version_t                 aci_version;
    mm_segment_t oldfs;

    read_count = 0;
    /* open file */
    if ((err = rsbac_read_open (RSBAC_ACL_GM_FILENAME,
                                &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);
    tmperr = file.f_op->read(&file,
                             (char *) &aci_version,
                             sizeof(aci_version),
                             &file.f_pos);
    /* error? */
    if (tmperr < sizeof(aci_version))
      {
        printk(KERN_WARNING
               "acl_read_gm_list(): 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_ACL_GM_VERSION:
          break;
        case RSBAC_ACL_OLD_GM_VERSION:
          printk(KERN_WARNING
                 "acl_read_gm_list(): old version %u, new is %u - upconverting group membership list!\n",
                 aci_version,
                 RSBAC_ACL_GM_VERSION);
          break;
        default:
          printk(KERN_WARNING
                 "acl_read_gm_list(): wrong version %u, expected %u - skipping file and setting no_write for GM list!\n",
                 aci_version,
                 RSBAC_ACL_GM_VERSION);
          gm_list_head.no_write = TRUE;
          err = -RSBAC_EREADFAILED;
          goto end_read;
      }
    do
      {
        item = NULL;
        /* read id and mask */
        switch(aci_version)
          {
            case RSBAC_ACL_GM_VERSION:
              tmperr = file.f_op->read(&file,
                                       (char *) &user,
                                       sizeof(user),
                                       &file.f_pos);
              break;
            case RSBAC_ACL_OLD_GM_VERSION:
              tmperr = file.f_op->read(&file,
                                       (char *) &old_user,
                                       sizeof(old_user),
                                       &file.f_pos);
              user = old_user;
              break;
            default:
         }
        if (tmperr > 0)
          { /* OK, user id successfully read. Now add. */
            item = add_gm_item(user);
            read_count++;
/*            if (rsbac_debug_ds) printk(KERN_DEBUG "read_gm_list(): read item %u\n",
                 user); */
            tmperr = file.f_op->read(&file,
                                     (char *) &len,
                                     sizeof(len),
                                     &file.f_pos);
            /* if successful, read and add len subitems */
            if (tmperr > 0)
              {
                for(i=0;i<len;i++)
                  {
                    tmperr = file.f_op->read(&file,
                                             (char *) &group,
                                             sizeof(group),
                                             &file.f_pos);
                    if(tmperr > 0)
                      {
                        if (item && !add_gm_subitem(item,
                                                    group))
                          {
                            printk(KERN_WARNING
                                   "acl_read_gm_list(): could not add sub-item!\n");
                            i = len;
                            tmperr = -1;
                          }
                      }
                    else
                      {
                        i = len;
                        tmperr = -1;
                      }
                  }
              }
          }
      }
    while (tmperr > 0); /* end of do */

    if (tmperr < 0)
      {
        printk(KERN_WARNING "acl_read_gm_list(): read error from file, item %lu!\n",
               read_count);
        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_acl)
      printk(KERN_DEBUG "acl_read_gm_list(): %lu entries read.\n",
             read_count);
    /* We do not need this file any more */
    rsbac_read_close(&file);

    return(err);
  }; /* end of acl_read_gm_list() */

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

#ifndef CONFIG_RSBAC_NO_WRITE
static int acl_write_gm_list(void)
  {
    struct file                     file;
    int                             err = 0;
    struct rsbac_acl_gm_list_item_t * current_p = NULL;
    struct rsbac_acl_gm_sublist_item_t * sub_item_p;
    mm_segment_t oldfs;
    u_long          write_count = 0, written = 0, bytes, buflen = 0;
    int             tmperr = 0;
    u_long          flags;
    rsbac_version_t aci_version = RSBAC_ACL_GM_VERSION;
    char          * buffer = NULL;
    boolean         vmalloc_used = FALSE;

    /* test no_write */
    if(gm_list_head.no_write)
      return(-RSBAC_ENOTWRITABLE);
    /* protect this list */
    rsbac_read_lock(&gm_list_head.lock, &flags);
    /* calculate buflen */
    buflen = sizeof(aci_version)
           + (gm_list_head.count *
              (  sizeof(current_p->user)
               + sizeof(current_p->sublist_length)
              )
             );
    current_p = gm_list_head.head;
    while (current_p) 
      {
        buflen += current_p->sublist_length * sizeof(sub_item_p->group);
        current_p = current_p->next;
      }
    /* 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
                 "acl_write_gm_list(): vmalloc used!\n");
      }
    if(!buffer)
      {
        /* unprotect this list */
        rsbac_read_unlock(&gm_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 = gm_list_head.head;
    while (current_p) 
      {
        memcpy(buffer+write_count,
               (char *) &current_p->user,
	       sizeof(current_p->user));
        write_count += sizeof(current_p->user);
        memcpy(buffer+write_count,
               (char *) &current_p->sublist_length,
               sizeof(current_p->sublist_length));
        write_count += sizeof(current_p->sublist_length);
        sub_item_p = current_p->sublist_head;
        while (sub_item_p)
          {
            memcpy(buffer+write_count,
                   (char *) &(sub_item_p->group),
                   sizeof(sub_item_p->group));
            write_count += sizeof(sub_item_p->group);
            sub_item_p = sub_item_p->next;
          }
        current_p = current_p->next;
      }
    /* unprotect this list */
    rsbac_read_unlock(&gm_list_head.lock, &flags);

    if (write_count != buflen)
      {
        printk(KERN_WARNING
               "acl_write_gm_list(): length mismatch on file %s, buflen is %lu, write_count is %lu!\n",
               RSBAC_ACL_GM_FILENAME,
               buflen,
               write_count);
        err |= -RSBAC_EWRITEFAILED;
      }

    /* get rsbac write-to-disk semaphore */
    down(&rsbac_write_sem);
    
    /* open file */
    if ((err = rsbac_write_open(RSBAC_ACL_GM_FILENAME,
                                &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
               "acl_write_gm_list(): write error %i on file %s!\n",
               tmperr,
               RSBAC_ACL_GM_FILENAME);
        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 "acl_write_gm_list(): %u main lists written, %lu bytes\n",
             gm_list_head.count,
             write_count);
    /* 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 acl_write_gm_list() */
#endif /* ifndef CONFIG_RSBAC_NO_WRITE */

/* acl_read_group_list() */
/* reading the group list from root device */

void convert_old_group_entry(struct rsbac_acl_old_group_entry_t * old_entry,
                             struct rsbac_acl_group_entry_t * new_entry)
  {
    new_entry->id = old_entry->id;
    new_entry->owner = old_entry->owner;
    new_entry->type = old_entry->type;
    memcpy(new_entry->name, old_entry->name, RSBAC_ACL_GROUP_NAMELEN);
  }

static int acl_read_group_list(void)
  {
    struct file                     file;
    int                             err = 0;
    u_int                           i;
    int                             tmperr;
    struct rsbac_acl_group_entry_t  entry;
    struct rsbac_acl_old_group_entry_t  old_entry;
    u_int                           len;
    rsbac_version_t                 aci_version;
    mm_segment_t oldfs;

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

    /* OK, now we can start reading */
    /* 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
               "acl_read_group_list(): 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_ACL_GROUP_VERSION:
          break;
        case RSBAC_ACL_OLD_GROUP_VERSION:
          printk(KERN_WARNING
                 "acl_read_group_list(): old version %u, new is %u - upconverting!\n",
                 aci_version,
                 RSBAC_ACL_GROUP_VERSION);
          break;
        default:
          printk(KERN_WARNING
                 "acl_read_group_list(): wrong version %u, expected %u - skipping file and setting no_write!\n",
                 aci_version,
                 RSBAC_ACL_GROUP_VERSION);
          group_list_head.no_write = TRUE;
          err = -RSBAC_EREADFAILED;
          goto end_read;
      }
    tmperr = file.f_op->read(&file,
                             (char *) &group_list_head.last_new,
                             sizeof(group_list_head.last_new),
                             &file.f_pos);
    /* error? */
    if (tmperr < sizeof(group_list_head.last_new))
      {
        printk(KERN_WARNING
               "acl_read_group_list(): read error from file!\n");
        err = -RSBAC_EREADFAILED;
        goto end_read;
      }
    tmperr = file.f_op->read(&file,
                             (char *) &len,
                             sizeof(len),
                             &file.f_pos);
    /* if successful, read and add len items */
    if (tmperr > 0)
      {
        for(i=0;i<len;i++)
          {
            switch(aci_version)
              {
                case RSBAC_ACL_GROUP_VERSION:
                  tmperr = file.f_op->read(&file,
                                           (char *) &entry,
                                           sizeof(entry),
                                           &file.f_pos);
                  break;
                case RSBAC_ACL_OLD_GROUP_VERSION:
                  tmperr = file.f_op->read(&file,
                                           (char *) &old_entry,
                                           sizeof(old_entry),
                                           &file.f_pos);
                  convert_old_group_entry(&old_entry, &entry);
                  break;
                default:
                  break;
              }
            if(tmperr > 0)
              {
                if (!add_group_item(entry))
                  {
                    printk(KERN_WARNING
                           "acl_read_group_list(): could not add item!\n");
                    i = len;
                    tmperr = -1;
                  }
              }
            else
              {
                i = len;
                tmperr = -1;
              }
          }
      }

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

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

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

#ifndef CONFIG_RSBAC_NO_WRITE
static int acl_write_group_list(void)
  {
    struct file                     file;
    int                             err = 0;
    struct rsbac_acl_group_list_item_t * item_p;
    mm_segment_t oldfs;
    u_long          write_count = 0, written = 0, bytes, buflen = 0;
    int             tmperr = 0;
    u_long          flags;
    rsbac_version_t aci_version = RSBAC_ACL_GROUP_VERSION;
    char          * buffer = NULL;
    boolean         vmalloc_used = FALSE;

    /* test no_write */
    if(group_list_head.no_write)
      return(-RSBAC_ENOTWRITABLE);
    /* protect this list */
    rsbac_read_lock(&group_list_head.lock, &flags);
    /* calculate buflen */
    buflen =   sizeof(aci_version)
             + sizeof(u_int) /* list_len */
             + group_list_head.count * sizeof(item_p->entry);
    /* 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
                 "acl_write_group_list(): vmalloc used!\n");
      }
    if(!buffer)
      {
        /* unprotect this list */
        rsbac_read_unlock(&group_list_head.lock, &flags);
        return(-RSBAC_ENOMEM);
      }
    /* copy version */
    memcpy(buffer,
           (char *) &aci_version,
           sizeof(aci_version));
    write_count = sizeof(aci_version);
    /* copy last_new */
    memcpy(buffer+write_count,
           (char *) &group_list_head.last_new,
           sizeof(group_list_head.last_new));
    write_count += sizeof(group_list_head.last_new);
    /* copy list */
    memcpy(buffer+write_count,
           (char *) &(group_list_head.count),
           sizeof(group_list_head.count));
    write_count += sizeof(group_list_head.count);
    item_p = group_list_head.head;
    while (item_p)
      {
        memcpy(buffer+write_count,
               (char *) &(item_p->entry),
               sizeof(item_p->entry));
        write_count += sizeof(item_p->entry);
        item_p = item_p->next;
      }
    /* unprotect this list */
    rsbac_read_unlock(&group_list_head.lock, &flags);

    /* get rsbac write-to-disk semaphore */
    down(&rsbac_write_sem);
    
    /* open file */
    if ((err = rsbac_write_open(RSBAC_ACL_GROUP_FILENAME,
                                &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
               "acl_write_group_list(): write error %i on file %s!\n",
               tmperr,
               RSBAC_ACL_GROUP_FILENAME);
        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 "acl_write_group_list(): %u list entries written to device %02u:%02u.\n",
             group_list_head.count,
             MAJOR(ROOT_DEV),
             MINOR(ROOT_DEV));
    /* 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 acl_write_group_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.                                           */

/* first the device item lookup */
static struct rsbac_acl_device_list_item_t * acl_lookup_device(kdev_t kdev)
    {
      struct rsbac_acl_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 "acl_lookup_device(): Invalid device %02u:%02u\n",
                   MAJOR(kdev),MINOR(kdev));
        }
      /* it is the current item -> return it */
        return (curr);
    };

/* lookup_fd_subitem() */
/* lookup file/dir set sub item, return pointer */
static struct rsbac_acl_sublist_item_t *
  lookup_fd_subitem(struct rsbac_acl_fd_list_item_t * set,
                    enum   rsbac_acl_subject_type_t   subj_type,
                           rsbac_acl_subject_id_t     subj_id)
    {
      struct rsbac_acl_sublist_item_t  * curr;

      if(!set)
        return(NULL);      
      curr = set->sublist_head;
      while (   curr
             && (   (curr->entry.subj_type != subj_type)
                 || (curr->entry.subj_id != subj_id)
                )
            )
        curr = curr->next;
      return (curr);
    };

/* do_lookup_fd_item() */
/* lookup file set item from single list */
static struct rsbac_acl_fd_list_item_t *
  do_lookup_fd_item(struct rsbac_acl_fd_list_head_t  * head_p,
                           rsbac_inode_nr_t            id)
    {
      struct rsbac_acl_fd_list_item_t  * curr;

      if(!head_p)
        return NULL;
      curr = head_p->curr;
      /* is the current item the one we look for? yes -> return, else search */
      if (curr && (curr->id == id) )
        return (curr);
      else
        curr = head_p->head;
        while (curr && (curr->id != id) )
          curr = curr->next;

        if (curr)
          head_p->curr=curr;
        return (curr);
    };

/* lookup_fd_item() */
/* lookup file set item */
static struct rsbac_acl_fd_list_item_t *
  lookup_fd_item(struct rsbac_acl_device_list_item_t * device_p,
                        rsbac_inode_nr_t               id)
    {
      if(!device_p)
        return NULL;
      return do_lookup_fd_item(&device_p->head_table[fd_hash(id)], id);
    }

/* lookup_dev_subitem() */
/* lookup device set sub item, return pointer */
static struct rsbac_acl_sublist_item_t *
  lookup_dev_subitem (struct rsbac_acl_dev_list_item_t  * set,
                      enum   rsbac_acl_subject_type_t     subj_type,
                             rsbac_acl_subject_id_t       subj_id)
    {
      struct rsbac_acl_sublist_item_t  * curr;

      if(!set)
        return(NULL);      
      curr = set->sublist_head;
      while (   curr
             && (   (curr->entry.subj_type != subj_type)
                 || (curr->entry.subj_id != subj_id)
                )
            )
        curr = curr->next;
      return (curr);
    };

/* lookup_dev_item() */
/* lookup device set item */
static struct rsbac_acl_dev_list_item_t *
  lookup_dev_item (struct rsbac_dev_t id)
    {
      struct rsbac_acl_dev_list_item_t  * curr;

      curr = dev_list_head.curr;
      /* is the current item the one we look for? yes -> return, else search */
      if (curr && (curr->id.type == id.type) && (curr->id.id == id.id) )
        return (curr);
      else
        curr = dev_list_head.head;
        while (curr && ((curr->id.type != id.type) || (curr->id.id != id.id)) )
          curr = curr->next;

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


/* lookup_ipc_subitem() */
/* lookup device set sub item, return pointer */
static struct rsbac_acl_sublist_item_t *
  lookup_ipc_subitem (struct rsbac_acl_ipc_list_item_t  * set,
                      enum   rsbac_acl_subject_type_t     subj_type,
                             rsbac_acl_subject_id_t       subj_id)
    {
      struct rsbac_acl_sublist_item_t  * curr;

      if(!set)
        return(NULL);      
      curr = set->sublist_head;
      while (   curr
             && (   (curr->entry.subj_type != subj_type)
                 || (curr->entry.subj_id != subj_id)
                )
            )
        curr = curr->next;
      return (curr);
    };

/* lookup_scd_subitem() */
/* lookup device set sub item, return pointer */
static struct rsbac_acl_sublist_item_t *
  lookup_scd_subitem (struct rsbac_acl_scd_list_item_t  * set,
                      enum   rsbac_acl_subject_type_t     subj_type,
                             rsbac_acl_subject_id_t       subj_id)
    {
      struct rsbac_acl_sublist_item_t  * curr;

      if(!set)
        return(NULL);      
      curr = set->sublist_head;
      while (   curr
             && (   (curr->entry.subj_type != subj_type)
                 || (curr->entry.subj_id != subj_id)
                )
            )
        curr = curr->next;
      return (curr);
    };

/* lookup_scd_item() */
/* lookup SCD set item */
static struct rsbac_acl_scd_list_item_t *
  lookup_scd_item (enum rsbac_scd_type_t id)
    {
      struct rsbac_acl_scd_list_item_t  * curr;

      curr = scd_list_head.curr;
      /* is the current item the one we look for? yes -> return, else search */
      if (curr && (curr->id == id))
        return (curr);
      else
        curr = scd_list_head.head;
        while (curr && (curr->id != id))
          curr = curr->next;

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


/* lookup_u_subitem() */
/* lookup process set sub item, return pointer */
static struct rsbac_acl_sublist_item_t *
  lookup_u_subitem (struct rsbac_acl_u_list_item_t  * set,
                    enum   rsbac_acl_subject_type_t   subj_type,
                           rsbac_acl_subject_id_t     subj_id)
    {
      struct rsbac_acl_sublist_item_t  * curr;

      if(!set)
        return(NULL);      
      curr = set->sublist_head;
      while (   curr
             && (   (curr->entry.subj_type != subj_type)
                 || (curr->entry.subj_id != subj_id)
                )
            )
        curr = curr->next;
      return (curr);
    };

/* lookup_p_subitem() */
/* lookup process set sub item, return pointer */
static struct rsbac_acl_sublist_item_t *
  lookup_p_subitem (struct rsbac_acl_p_list_item_t  * set,
                    enum   rsbac_acl_subject_type_t   subj_type,
                           rsbac_acl_subject_id_t     subj_id)
    {
      struct rsbac_acl_sublist_item_t  * curr;

      if(!set)
        return(NULL);      
      curr = set->sublist_head;
      while (   curr
             && (   (curr->entry.subj_type != subj_type)
                 || (curr->entry.subj_id != subj_id)
                )
            )
        curr = curr->next;
      return (curr);
    };

/* lookup_gm_subitem() */
/* lookup group member set sub item, return pointer */
static struct rsbac_acl_gm_sublist_item_t *
  lookup_gm_subitem (struct rsbac_acl_gm_list_item_t  * set,
                            rsbac_acl_group_id_t       group)
    {
      struct rsbac_acl_gm_sublist_item_t  * curr;

      if(!set)
        return(NULL);      
      curr = set->sublist_head;
      while (   curr
             && (curr->group != group)
            )
        curr = curr->next;
      return (curr);
    };

/* lookup_gm_item() */
/* lookup group member list item */
static struct rsbac_acl_gm_list_item_t *
  lookup_gm_item (rsbac_uid_t user)
    {
      struct rsbac_acl_gm_list_item_t  * curr;

      curr = gm_list_head.curr;
      /* is the current item the one we look for? yes -> return, else search */
      if (curr && (curr->user == user) )
        return (curr);
      else
        curr = gm_list_head.head;
        while (curr && (curr->user != user) )
          curr = curr->next;

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

/* lookup_group_item() */
/* lookup group list item */
static struct rsbac_acl_group_list_item_t *
  lookup_group_item (rsbac_acl_group_id_t group)
    {
      struct rsbac_acl_group_list_item_t  * curr;

      curr = group_list_head.curr;
      /* is the current item the one we look for? yes -> return, else search */
      if (curr && (curr->entry.id == group) )
        return (curr);
      else
        curr = group_list_head.head;
        while (curr && (curr->entry.id != group) )
          curr = curr->next;

        if (curr)
          group_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! */

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

      /* allocate memory for new device, return NULL, if failed */
      if ( !(new_item_p = (struct rsbac_acl_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;

      /* init file/dir sublists */
      for(i=0; i<RSBAC_ACL_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);
    };

static struct rsbac_acl_sublist_item_t * 
         add_fd_subitem(struct rsbac_acl_fd_list_item_t * set,
                        struct rsbac_acl_entry_t          entry)
    {
      struct rsbac_acl_sublist_item_t * new_item_p = NULL;

      if(!set)
        return(NULL);      
      if ( !(new_item_p = (struct rsbac_acl_sublist_item_t *)
                 kmalloc(sizeof(*new_item_p),GFP_KERNEL)) )
        return(NULL);
      new_item_p->entry = entry;
      
      if (!set->sublist_head)
        {
          set->sublist_head=new_item_p;
          set->sublist_tail=new_item_p;
          set->sublist_length=1;
          new_item_p->prev=NULL;
          new_item_p->next=NULL;          
        }  
      else
        {
          new_item_p->prev=set->sublist_tail;
          new_item_p->next=NULL;
          set->sublist_tail->next=new_item_p;
          set->sublist_tail=new_item_p;
          set->sublist_length++;
        };
      return(new_item_p);
    };

static struct rsbac_acl_fd_list_item_t * 
         do_add_fd_item(struct rsbac_acl_fd_list_head_t * head_p,
                               rsbac_inode_nr_t           id,
                               rsbac_acl_rights_vector_t  mask)
    {
      struct rsbac_acl_fd_list_item_t * new_item_p = NULL;

      if(!head_p)
        return NULL;
      if ( !(new_item_p = (struct rsbac_acl_fd_list_item_t *)
                 kmalloc(sizeof(*new_item_p),GFP_KERNEL)) )
        return(NULL);
      new_item_p->id = id;
      new_item_p->mask = mask;
      new_item_p->sublist_length = 0;
      new_item_p->sublist_head = NULL;
      new_item_p->sublist_tail = NULL;
      
      if (!head_p->head)
        {
          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
        {
          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);
    };

static struct rsbac_acl_fd_list_item_t * 
         add_fd_item(struct rsbac_acl_device_list_item_t * device_p,
                            rsbac_inode_nr_t               id,
                            rsbac_acl_rights_vector_t      mask)
    {
      if(!device_p)
        return NULL;
      return do_add_fd_item(&device_p->head_table[fd_hash(id)], id, mask);
    }

static struct rsbac_acl_sublist_item_t * 
         add_dev_subitem (struct rsbac_acl_dev_list_item_t  * set,
                          struct rsbac_acl_entry_t            entry)
    {
      struct rsbac_acl_sublist_item_t * new_item_p = NULL;

      if(!set)
        return(NULL);      
      if ( !(new_item_p = (struct rsbac_acl_sublist_item_t *)
                 kmalloc(sizeof(*new_item_p),GFP_KERNEL)) )
        return(NULL);
      new_item_p->entry = entry;
      
      if (!set->sublist_head)
        {
          set->sublist_head=new_item_p;
          set->sublist_tail=new_item_p;
          set->sublist_length=1;
          new_item_p->prev=NULL;
          new_item_p->next=NULL;          
        }  
      else
        {
          new_item_p->prev=set->sublist_tail;
          new_item_p->next=NULL;
          set->sublist_tail->next=new_item_p;
          set->sublist_tail=new_item_p;
          set->sublist_length++;
        };
      return(new_item_p);
    };

static struct rsbac_acl_dev_list_item_t * 
         add_dev_item(struct rsbac_dev_t        id,
                      rsbac_acl_rights_vector_t mask)
    {
      struct rsbac_acl_dev_list_item_t * new_item_p = NULL;

      if ( !(new_item_p = (struct rsbac_acl_dev_list_item_t *)
                 kmalloc(sizeof(*new_item_p),GFP_KERNEL)) )
        return(NULL);
      new_item_p->id = id;
      new_item_p->mask = mask;
      new_item_p->sublist_length = 0;
      new_item_p->sublist_head = NULL;
      new_item_p->sublist_tail = NULL;
      
      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_acl_sublist_item_t * 
         add_ipc_subitem (struct rsbac_acl_ipc_list_item_t  * set,
                          struct rsbac_acl_entry_t            entry)
    {
      struct rsbac_acl_sublist_item_t * new_item_p = NULL;

      if(!set)
        return(NULL);      
      if ( !(new_item_p = (struct rsbac_acl_sublist_item_t *)
                 kmalloc(sizeof(*new_item_p),GFP_KERNEL)) )
        return(NULL);
      new_item_p->entry = entry;
      
      if (!set->sublist_head)
        {
          set->sublist_head=new_item_p;
          set->sublist_tail=new_item_p;
          set->sublist_length=1;
          new_item_p->prev=NULL;
          new_item_p->next=NULL;          
        }  
      else
        {
          new_item_p->prev=set->sublist_tail;
          new_item_p->next=NULL;
          set->sublist_tail->next=new_item_p;
          set->sublist_tail=new_item_p;
          set->sublist_length++;
        };
      return(new_item_p);
    };

static struct rsbac_acl_sublist_item_t * 
         add_scd_subitem (struct rsbac_acl_scd_list_item_t  * set,
                          struct rsbac_acl_entry_t            entry)
    {
      struct rsbac_acl_sublist_item_t * new_item_p = NULL;

      if(!set)
        return(NULL);      
      if ( !(new_item_p = (struct rsbac_acl_sublist_item_t *)
                 kmalloc(sizeof(*new_item_p),GFP_KERNEL)) )
        return(NULL);
      new_item_p->entry = entry;
      
      if (!set->sublist_head)
        {
          set->sublist_head=new_item_p;
          set->sublist_tail=new_item_p;
          set->sublist_length=1;
          new_item_p->prev=NULL;
          new_item_p->next=NULL;          
        }  
      else
        {
          new_item_p->prev=set->sublist_tail;
          new_item_p->next=NULL;
          set->sublist_tail->next=new_item_p;
          set->sublist_tail=new_item_p;
          set->sublist_length++;
        };
      return(new_item_p);
    };

static struct rsbac_acl_scd_list_item_t * 
         add_scd_item(enum rsbac_scd_type_t     id,
                      rsbac_acl_rights_vector_t mask)
    {
      struct rsbac_acl_scd_list_item_t * new_item_p = NULL;

      if ( !(new_item_p = (struct rsbac_acl_scd_list_item_t *)
                 kmalloc(sizeof(*new_item_p),GFP_KERNEL)) )
        return(NULL);
      new_item_p->id = id;
      new_item_p->mask = mask;
      new_item_p->sublist_length = 0;
      new_item_p->sublist_head = NULL;
      new_item_p->sublist_tail = NULL;
      
      if (!scd_list_head.head)
        {
          scd_list_head.head=new_item_p;
          scd_list_head.tail=new_item_p;
          scd_list_head.curr=new_item_p;
          scd_list_head.count = 1;
          new_item_p->prev=NULL;
          new_item_p->next=NULL;          
        }  
      else
        {
          new_item_p->prev=scd_list_head.tail;
          new_item_p->next=NULL;
          scd_list_head.tail->next=new_item_p;
          scd_list_head.tail=new_item_p;
          scd_list_head.curr=new_item_p;
          scd_list_head.count++;
        };
      return(new_item_p);
    };

static struct rsbac_acl_sublist_item_t * 
         add_u_subitem (struct rsbac_acl_u_list_item_t  * set,
                        struct rsbac_acl_entry_t          entry)
    {
      struct rsbac_acl_sublist_item_t * new_item_p = NULL;

      if(!set)
        return(NULL);      
      if ( !(new_item_p = (struct rsbac_acl_sublist_item_t *)
                 kmalloc(sizeof(*new_item_p),GFP_KERNEL)) )
        return(NULL);
      new_item_p->entry = entry;
      
      if (!set->sublist_head)
        {
          set->sublist_head=new_item_p;
          set->sublist_tail=new_item_p;
          set->sublist_length=1;
          new_item_p->prev=NULL;
          new_item_p->next=NULL;          
        }  
      else
        {
          new_item_p->prev=set->sublist_tail;
          new_item_p->next=NULL;
          set->sublist_tail->next=new_item_p;
          set->sublist_tail=new_item_p;
          set->sublist_length++;
        };
      return(new_item_p);
    };

static struct rsbac_acl_sublist_item_t * 
         add_p_subitem (struct rsbac_acl_p_list_item_t  * set,
                        struct rsbac_acl_entry_t          entry)
    {
      struct rsbac_acl_sublist_item_t * new_item_p = NULL;

      if(!set)
        return(NULL);      
      if ( !(new_item_p = (struct rsbac_acl_sublist_item_t *)
                 kmalloc(sizeof(*new_item_p),GFP_KERNEL)) )
        return(NULL);
      new_item_p->entry = entry;
      
      if (!set->sublist_head)
        {
          set->sublist_head=new_item_p;
          set->sublist_tail=new_item_p;
          set->sublist_length=1;
          new_item_p->prev=NULL;
          new_item_p->next=NULL;          
        }  
      else
        {
          new_item_p->prev=set->sublist_tail;
          new_item_p->next=NULL;
          set->sublist_tail->next=new_item_p;
          set->sublist_tail=new_item_p;
          set->sublist_length++;
        };
      return(new_item_p);
    };

static struct rsbac_acl_gm_sublist_item_t * 
         add_gm_subitem (struct rsbac_acl_gm_list_item_t  * set,
                                rsbac_acl_group_id_t group)
    {
      struct rsbac_acl_gm_sublist_item_t * new_item_p = NULL;

      if(!set)
        return(NULL);      
      if ( !(new_item_p = (struct rsbac_acl_gm_sublist_item_t *)
                 kmalloc(sizeof(*new_item_p),GFP_KERNEL)) )
        return(NULL);
      new_item_p->group = group;
      
      if (!set->sublist_head)
        {
          set->sublist_head=new_item_p;
          set->sublist_tail=new_item_p;
          set->sublist_length=1;
          new_item_p->prev=NULL;
          new_item_p->next=NULL;          
        }  
      else
        {
          new_item_p->prev=set->sublist_tail;
          new_item_p->next=NULL;
          set->sublist_tail->next=new_item_p;
          set->sublist_tail=new_item_p;
          set->sublist_length++;
        };
      return(new_item_p);
    };

static struct rsbac_acl_gm_list_item_t * 
         add_gm_item(rsbac_uid_t user)
    {
      struct rsbac_acl_gm_list_item_t * new_item_p = NULL;

      if ( !(new_item_p = (struct rsbac_acl_gm_list_item_t *)
                 kmalloc(sizeof(*new_item_p),GFP_KERNEL)) )
        return(NULL);
      new_item_p->user = user;
      new_item_p->sublist_length = 0;
      new_item_p->sublist_head = NULL;
      new_item_p->sublist_tail = NULL;
      
      if (!gm_list_head.head)
        {
          gm_list_head.head=new_item_p;
          gm_list_head.tail=new_item_p;
          gm_list_head.curr=new_item_p;
          gm_list_head.count = 1;
          new_item_p->prev=NULL;
          new_item_p->next=NULL;          
        }  
      else
        {
          new_item_p->prev=gm_list_head.tail;
          new_item_p->next=NULL;
          gm_list_head.tail->next=new_item_p;
          gm_list_head.tail=new_item_p;
          gm_list_head.curr=new_item_p;
          gm_list_head.count++;
        };
      return(new_item_p);
    };

static struct rsbac_acl_group_list_item_t * 
         add_group_item(struct rsbac_acl_group_entry_t entry)
    {
      struct rsbac_acl_group_list_item_t * new_item_p = NULL;

      if ( !(new_item_p = (struct rsbac_acl_group_list_item_t *)
                 kmalloc(sizeof(*new_item_p),GFP_KERNEL)) )
        return(NULL);
      new_item_p->entry = entry;
      
      if (!group_list_head.head)
        {
          group_list_head.head=new_item_p;
          group_list_head.tail=new_item_p;
          group_list_head.curr=new_item_p;
          group_list_head.count = 1;
          new_item_p->prev=NULL;
          new_item_p->next=NULL;          
        }  
      else
        {
          new_item_p->prev=group_list_head.tail;
          new_item_p->next=NULL;
          group_list_head.tail->next=new_item_p;
          group_list_head.tail=new_item_p;
          group_list_head.curr=new_item_p;
          group_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_fd_subitem(struct rsbac_acl_fd_list_item_t * set,
                              enum   rsbac_acl_subject_type_t   subj_type,
                                     rsbac_acl_subject_id_t     subj_id)
    {
      struct rsbac_acl_sublist_item_t * item_p;
    
      /* first we must locate the item. */
      if (set && (item_p = lookup_fd_subitem(set, subj_type, subj_id)) )
        { /* ok, item was found */
          if ( (set->sublist_head == item_p) )
             { /* item is head */
               if ( (set->sublist_tail == item_p) )
                 { /* item is head and tail = only item -> list will be empty*/
                   set->sublist_head = NULL;
                   set->sublist_tail = NULL;
                   set->sublist_length = 0;
                 }
               else
                 { /* item is head, but not tail -> next item becomes head */
                   item_p->next->prev = NULL;
                   set->sublist_head = item_p->next;
                   set->sublist_length--;
                 };
             }
          else
             { /* item is not head */
               if ( (set->sublist_tail == item_p) )
                 { /*item is not head, but tail -> previous item becomes tail*/
                   item_p->prev->next = NULL;
                   set->sublist_tail = item_p->prev;
                   set->sublist_length--;
                 }
               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;
                   set->sublist_length--;
                 };
             };
             
          /* now we can remove the item from memory */
          kfree(item_p);    
        };  /* end of if: item was found */

    }; /* end of remove_fd_subitem() */

#if 0
static void
  remove_all_fd_subitems(struct rsbac_acl_fd_list_item_t * set_p)
    {
      struct rsbac_acl_sublist_item_t * sub_item_p;
      struct rsbac_acl_sublist_item_t * next_sub_item_p;
    
      /* first we must locate the item. */
      if (set_p)
        { /* ok, valid set pointer */
          /* remove all subitems from memory */
          sub_item_p = set_p->sublist_head;
          while(sub_item_p)
            {
              next_sub_item_p = sub_item_p->next;
              kfree(sub_item_p);
              sub_item_p = next_sub_item_p;
            }
          /* adjust start pointers */
          set_p->sublist_head = NULL;
          set_p->sublist_tail = NULL;
          set_p->sublist_length = 0;
        };  /* end of if: item was found */

    }; /* end of remove_all_fd_subitems() */
#endif /* 0 */

static void do_remove_fd_item(struct rsbac_acl_fd_list_head_t * head_p,
                                     rsbac_inode_nr_t           id)
    {
      struct rsbac_acl_fd_list_item_t * item_p;
      struct rsbac_acl_sublist_item_t * sub_item_p;
      struct rsbac_acl_sublist_item_t * next_sub_item_p;
    
      /* first we must locate the item. */
      if ( (item_p = do_lookup_fd_item(head_p,id)) )
        { /* 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;
                 }
               else
                 { /* item is head, but not tail -> next item becomes head */
                   item_p->next->prev = NULL;
                   head_p->head = 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;
                 }
               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 */
          head_p->curr=NULL;
          /* adjust count */
          head_p->count--;
          /* now we can remove the item from memory, but first the sub-items */
          sub_item_p = item_p->sublist_head;
          while(sub_item_p)
            {
              next_sub_item_p = sub_item_p->next;
              kfree(sub_item_p);
              sub_item_p = next_sub_item_p;
            }
          kfree(item_p);    
        };  /* end of if: item was found */

    }; /* end of remove_fd_item() */


static void remove_fd_item(struct rsbac_acl_device_list_item_t * device_p,
                                  rsbac_inode_nr_t id)
    {
      if(device_p)
        do_remove_fd_item(&device_p->head_table[fd_hash(id)], id);
    }

static void remove_device_item(kdev_t kdev)
    {
      struct rsbac_acl_device_list_item_t    * item_p;
      struct rsbac_acl_fd_list_item_t        * fd_item_p;
      struct rsbac_acl_fd_list_item_t        * fd_item_p2;
             int                               i;
    
      /* first we must locate the item. */
      if ( (item_p = acl_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_ACL_NR_FD_LISTS; i++)
            {
              fd_item_p = item_p->head_table[i].head;
              while (fd_item_p)
                {
                  fd_item_p2 = fd_item_p->next;
                  item_p->head_table[i].curr = fd_item_p;
                  remove_fd_item(item_p, fd_item_p->id);
                  fd_item_p = fd_item_p2;
                }
            }
          /* OK, lets remove the device item itself */
          kfree(item_p);
        };  /* end of if: item was found */

    }; /* end of remove_device_item() */

static void remove_dev_subitem (struct rsbac_acl_dev_list_item_t * set,
                                enum   rsbac_acl_subject_type_t   subj_type,
                                       rsbac_acl_subject_id_t     subj_id)
    {
      struct rsbac_acl_sublist_item_t * item_p;
    
      /* first we must locate the item. */
      if (set && (item_p = lookup_dev_subitem(set, subj_type, subj_id)) )
        { /* ok, item was found */
          if ( (set->sublist_head == item_p) )
             { /* item is head */
               if ( (set->sublist_tail == item_p) )
                 { /* item is head and tail = only item -> list will be empty*/
                   set->sublist_head = NULL;
                   set->sublist_tail = NULL;
                   set->sublist_length = 0;
                 }
               else
                 { /* item is head, but not tail -> next item becomes head */
                   item_p->next->prev = NULL;
                   set->sublist_head = item_p->next;
                   set->sublist_length--;
                 };
             }
          else
             { /* item is not head */
               if ( (set->sublist_tail == item_p) )
                 { /*item is not head, but tail -> previous item becomes tail*/
                   item_p->prev->next = NULL;
                   set->sublist_tail = item_p->prev;
                   set->sublist_length--;
                 }
               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;
                   set->sublist_length--;
                 };
             };
             
          /* now we can remove the item from memory */
          kfree(item_p);    
        };  /* end of if: item was found */

    }; /* end of remove_dev_subitem() */

static void remove_dev_item (struct rsbac_dev_t id)
    {
      struct rsbac_acl_dev_list_item_t * item_p;
      struct rsbac_acl_sublist_item_t  * sub_item_p;
      struct rsbac_acl_sublist_item_t  * next_sub_item_p;
    
      /* first we must locate the item. */
      if ( (item_p = lookup_dev_item(id)) )
        { /* 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 count */
          dev_list_head.count--;
          /* now we can remove the item from memory, but first the sub-items */
          sub_item_p = item_p->sublist_head;
          while(sub_item_p)
            {
              next_sub_item_p = sub_item_p->next;
              kfree(sub_item_p);
              sub_item_p = next_sub_item_p;
            }
          kfree(item_p);    
        };  /* end of if: item was found */

    }; /* end of remove_dev_item() */

static void remove_ipc_subitem (struct rsbac_acl_ipc_list_item_t * set,
                                enum   rsbac_acl_subject_type_t   subj_type,
                                       rsbac_acl_subject_id_t     subj_id)
    {
      struct rsbac_acl_sublist_item_t * item_p;
    
      /* first we must locate the item. */
      if (set && (item_p = lookup_ipc_subitem(set, subj_type, subj_id)) )
        { /* ok, item was found */
          if ( (set->sublist_head == item_p) )
             { /* item is head */
               if ( (set->sublist_tail == item_p) )
                 { /* item is head and tail = only item -> list will be empty*/
                   set->sublist_head = NULL;
                   set->sublist_tail = NULL;
                   set->sublist_length = 0;
                 }
               else
                 { /* item is head, but not tail -> next item becomes head */
                   item_p->next->prev = NULL;
                   set->sublist_head = item_p->next;
                   set->sublist_length--;
                 };
             }
          else
             { /* item is not head */
               if ( (set->sublist_tail == item_p) )
                 { /*item is not head, but tail -> previous item becomes tail*/
                   item_p->prev->next = NULL;
                   set->sublist_tail = item_p->prev;
                   set->sublist_length--;
                 }
               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;
                   set->sublist_length--;
                 };
             };
             
          /* now we can remove the item from memory */
          kfree(item_p);    
        };  /* end of if: item was found */

    }; /* end of remove_ipc_subitem() */

static void remove_scd_subitem (struct rsbac_acl_scd_list_item_t * set,
                                enum   rsbac_acl_subject_type_t   subj_type,
                                       rsbac_acl_subject_id_t     subj_id)
    {
      struct rsbac_acl_sublist_item_t * item_p;
    
      /* first we must locate the item. */
      if (set && (item_p = lookup_scd_subitem(set, subj_type, subj_id)) )
        { /* ok, item was found */
          if ( (set->sublist_head == item_p) )
             { /* item is head */
               if ( (set->sublist_tail == item_p) )
                 { /* item is head and tail = only item -> list will be empty*/
                   set->sublist_head = NULL;
                   set->sublist_tail = NULL;
                   set->sublist_length = 0;
                 }
               else
                 { /* item is head, but not tail -> next item becomes head */
                   item_p->next->prev = NULL;
                   set->sublist_head = item_p->next;
                   set->sublist_length--;
                 };
             }
          else
             { /* item is not head */
               if ( (set->sublist_tail == item_p) )
                 { /*item is not head, but tail -> previous item becomes tail*/
                   item_p->prev->next = NULL;
                   set->sublist_tail = item_p->prev;
                   set->sublist_length--;
                 }
               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;
                   set->sublist_length--;
                 };
             };
             
          /* now we can remove the item from memory */
          kfree(item_p);    
        };  /* end of if: item was found */

    }; /* end of remove_scd_subitem() */

static void remove_scd_item (enum rsbac_scd_type_t id)
    {
      struct rsbac_acl_scd_list_item_t * item_p;
      struct rsbac_acl_sublist_item_t  * sub_item_p;
      struct rsbac_acl_sublist_item_t  * next_sub_item_p;
    
      /* first we must locate the item. */
      if ( (item_p = lookup_scd_item(id)) )
        { /* ok, item was found */
          if ( (scd_list_head.head == item_p) )
             { /* item is head */
               if ( (scd_list_head.tail == item_p) )
                 { /* item is head and tail = only item -> list will be empty*/
                   scd_list_head.head = NULL;
                   scd_list_head.tail = NULL;
                 }
               else
                 { /* item is head, but not tail -> next item becomes head */
                   item_p->next->prev = NULL;
                   scd_list_head.head = item_p->next;
                 };
             }
          else
             { /* item is not head */
               if ( (scd_list_head.tail == item_p) )
                 { /*item is not head, but tail -> previous item becomes tail*/
                   item_p->prev->next = NULL;
                   scd_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 */
          scd_list_head.curr=NULL;
          /* adjust count */
          scd_list_head.count--;
          /* now we can remove the item from memory, but first the sub-items */
          sub_item_p = item_p->sublist_head;
          while(sub_item_p)
            {
              next_sub_item_p = sub_item_p->next;
              kfree(sub_item_p);
              sub_item_p = next_sub_item_p;
            }
          kfree(item_p);    
        };  /* end of if: item was found */

    }; /* end of remove_scd_item() */

static void remove_u_subitem (struct rsbac_acl_u_list_item_t * set,
                              enum   rsbac_acl_subject_type_t   subj_type,
                                     rsbac_acl_subject_id_t     subj_id)
    {
      struct rsbac_acl_sublist_item_t * item_p;
    
      /* first we must locate the item. */
      if (set && (item_p = lookup_u_subitem(set, subj_type, subj_id)) )
        { /* ok, item was found */
          if ( (set->sublist_head == item_p) )
             { /* item is head */
               if ( (set->sublist_tail == item_p) )
                 { /* item is head and tail = only item -> list will be empty*/
                   set->sublist_head = NULL;
                   set->sublist_tail = NULL;
                   set->sublist_length = 0;
                 }
               else
                 { /* item is head, but not tail -> next item becomes head */
                   item_p->next->prev = NULL;
                   set->sublist_head = item_p->next;
                   set->sublist_length--;
                 };
             }
          else
             { /* item is not head */
               if ( (set->sublist_tail == item_p) )
                 { /*item is not head, but tail -> previous item becomes tail*/
                   item_p->prev->next = NULL;
                   set->sublist_tail = item_p->prev;
                   set->sublist_length--;
                 }
               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;
                   set->sublist_length--;
                 };
             };
             
          /* now we can remove the item from memory */
          kfree(item_p);    
        };  /* end of if: item was found */

    }; /* end of remove_u_subitem() */

static void remove_p_subitem (struct rsbac_acl_p_list_item_t * set,
                              enum   rsbac_acl_subject_type_t   subj_type,
                                     rsbac_acl_subject_id_t     subj_id)
    {
      struct rsbac_acl_sublist_item_t * item_p;
    
      /* first we must locate the item. */
      if (set && (item_p = lookup_p_subitem(set, subj_type, subj_id)) )
        { /* ok, item was found */
          if ( (set->sublist_head == item_p) )
             { /* item is head */
               if ( (set->sublist_tail == item_p) )
                 { /* item is head and tail = only item -> list will be empty*/
                   set->sublist_head = NULL;
                   set->sublist_tail = NULL;
                   set->sublist_length = 0;
                 }
               else
                 { /* item is head, but not tail -> next item becomes head */
                   item_p->next->prev = NULL;
                   set->sublist_head = item_p->next;
                   set->sublist_length--;
                 };
             }
          else
             { /* item is not head */
               if ( (set->sublist_tail == item_p) )
                 { /*item is not head, but tail -> previous item becomes tail*/
                   item_p->prev->next = NULL;
                   set->sublist_tail = item_p->prev;
                   set->sublist_length--;
                 }
               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;
                   set->sublist_length--;
                 };
             };
             
          /* now we can remove the item from memory */
          kfree(item_p);    
        };  /* end of if: item was found */

    }; /* end of remove_p_subitem() */

static void remove_gm_subitem (struct rsbac_acl_gm_list_item_t * set,
                                      rsbac_acl_group_id_t       group)
    {
      struct rsbac_acl_gm_sublist_item_t * item_p;
    
      /* first we must locate the item. */
      if (set && (item_p = lookup_gm_subitem(set, group)) )
        { /* ok, item was found */
          if ( (set->sublist_head == item_p) )
             { /* item is head */
               if ( (set->sublist_tail == item_p) )
                 { /* item is head and tail = only item -> list will be empty */
                   set->sublist_head = NULL;
                   set->sublist_tail = NULL;
                   set->sublist_length = 0;
                 }
               else
                 { /* item is head, but not tail -> next item becomes head */
                   item_p->next->prev = NULL;
                   set->sublist_head = item_p->next;
                   set->sublist_length--;
                 };
             }
          else
             { /* item is not head */
               if ( (set->sublist_tail == item_p) )
                 { /*item is not head, but tail -> previous item becomes tail*/
                   item_p->prev->next = NULL;
                   set->sublist_tail = item_p->prev;
                   set->sublist_length--;
                 }
               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;
                   set->sublist_length--;
                 };
             };
             
          /* now we can remove the item from memory */
          kfree(item_p);    
        };  /* end of if: item was found */

    }; /* end of remove_gm_subitem() */

static void remove_gm_item (rsbac_uid_t user)
    {
      struct rsbac_acl_gm_list_item_t * item_p;
      struct rsbac_acl_gm_sublist_item_t  * sub_item_p;
      struct rsbac_acl_gm_sublist_item_t  * next_sub_item_p;

      /* first we must locate the item. */
      if ( (item_p = lookup_gm_item(user)) )
        { /* ok, item was found */
          if ( (gm_list_head.head == item_p) )
             { /* item is head */
               if ( (gm_list_head.tail == item_p) )
                 { /* item is head and tail = only item -> list will be empty*/
                   gm_list_head.head = NULL;
                   gm_list_head.tail = NULL;
                 }
               else
                 { /* item is head, but not tail -> next item becomes head */
                   item_p->next->prev = NULL;
                   gm_list_head.head = item_p->next;
                 };
             }
          else
             { /* item is not head */
               if ( (gm_list_head.tail == item_p) )
                 { /*item is not head, but tail -> previous item becomes tail*/
                   item_p->prev->next = NULL;
                   gm_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 */
          gm_list_head.curr=NULL;
          /* adjust count */
          gm_list_head.count--;
          /* now we can remove the item from memory, but first the sub-items */
          sub_item_p = item_p->sublist_head;
          while(sub_item_p)
            {
              next_sub_item_p = sub_item_p->next;
              kfree(sub_item_p);
              sub_item_p = next_sub_item_p;
            }
          kfree(item_p);    
        };  /* end of if: item was found */

    }; /* end of remove_gm_item() */

static void remove_group_item (rsbac_acl_group_id_t group)
    {
      struct rsbac_acl_group_list_item_t * item_p;

      /* first we must locate the item. */
      if ( (item_p = lookup_group_item(group)) )
        { /* ok, item was found */
          if ( (group_list_head.head == item_p) )
             { /* item is head */
               if ( (group_list_head.tail == item_p) )
                 { /* item is head and tail = only item -> list will be empty*/
                   group_list_head.head = NULL;
                   group_list_head.tail = NULL;
                 }
               else
                 { /* item is head, but not tail -> next item becomes head */
                   item_p->next->prev = NULL;
                   group_list_head.head = item_p->next;
                 };
             }
          else
             { /* item is not head */
               if ( (group_list_head.tail == item_p) )
                 { /*item is not head, but tail -> previous item becomes tail*/
                   item_p->prev->next = NULL;
                   group_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 */
          group_list_head.curr=NULL;
          /* adjust count */
          group_list_head.count--;
          /* now we can remove the item from memory */
          kfree(item_p);    
        };  /* end of if: item was found */

    }; /* end of remove_group_item() */


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

#if defined(CONFIG_RSBAC_PROC) && defined(CONFIG_PROC_FS)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
static int
stats_acl_proc_info(char *buffer, char **start, off_t offset, int length, int dummy)
#else
static int
stats_acl_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_acl_fd_list_item_t         * fd_item_p;
    struct rsbac_acl_dev_list_item_t        * dev_item_p;
    struct rsbac_acl_scd_list_item_t        * scd_item_p;
    struct rsbac_acl_gm_list_item_t         * gm_item_p;
    u_int                                     item_count = 0;
    u_int                                     member_count = 0;
    u_int                                     no_dirty = 0;
    u_int                                     i;
    u_long flags,dflags;
    struct rsbac_acl_device_list_item_t   * device_p;

    union rsbac_target_id_t       rsbac_target_id;
    union rsbac_attribute_value_t rsbac_attribute_value;

    if (!rsbac_is_initialized)
      {
        printk(KERN_WARNING "stats_acl_proc_info(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    if (rsbac_debug_aef_acl)
      printk(KERN_DEBUG "stats_acl_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, "ACL Status\n-----------\n");

    /* protect device list */
    rsbac_read_lock(&device_list_head.lock, &dflags);
    device_p = device_list_head.head;
    while(device_p)
      {
        /* reset counters */
        item_count = 0;
        member_count = 0;
        no_dirty = 0;
        for(i=0; i<RSBAC_ACL_NR_FD_LISTS; i++)
          {
            /* protect file list */
            rsbac_read_lock(&device_p->head_table[i].lock, &flags);
            fd_item_p = device_p->head_table[i].head;
            while (fd_item_p)
              {
                item_count++;
                member_count += fd_item_p->sublist_length;
                fd_item_p = fd_item_p->next;
              };
            if(device_p->head_table[i].dirty)
              no_dirty++;
            /* unprotect the list */
            rsbac_read_unlock(&device_p->head_table[i].lock, &flags);
          }
        len += sprintf(buffer + len, "device %02u:%02u has %u file ACLs, sum of %u members, %u lists dirty\n",
                       MAJOR(device_p->id),
                       MINOR(device_p->id),
                       item_count,member_count,
                       no_dirty);
        pos = begin + len;
        if (pos < offset)
          {
            len = 0;
            begin = pos;
          }
        if (pos > offset+length)
          {
            rsbac_read_unlock(&device_list_head.lock, &dflags);
            goto out;
          }
        device_p = device_p->next;
      }
    /* unprotect device list */
    rsbac_read_unlock(&device_list_head.lock, &dflags);

    /* protect dev list */
    item_count = 0;
    member_count = 0;
    rsbac_read_lock(&dev_list_head.lock, &flags);
    dev_item_p = dev_list_head.head;
    while (dev_item_p)
      {
        item_count++;
        member_count += dev_item_p->sublist_length;
        dev_item_p = dev_item_p->next;
      };
    /* unprotect the list */
    rsbac_read_unlock(&dev_list_head.lock, &flags);
    len += sprintf(buffer + len, "%u device ACL items, sum of %u members\n",
                   item_count,member_count);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    /* protect SCD list */
    item_count = 0;
    member_count = 0;
    rsbac_read_lock(&scd_list_head.lock, &flags);
    scd_item_p = scd_list_head.head;
    while (scd_item_p)
      {
        item_count++;
        member_count += scd_item_p->sublist_length;
        scd_item_p = scd_item_p->next;
      };
    /* unprotect the list */
    rsbac_read_unlock(&scd_list_head.lock, &flags);
    len += sprintf(buffer + len, "%u device ACL items, sum of %u members\n",
                   item_count,member_count);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    len += sprintf(buffer + len, "%u groups, last new is %u\n",
                                 group_list_head.count, group_list_head.last_new);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    /* protect gm list */
    item_count = 0;
    member_count = 0;
    rsbac_read_lock(&gm_list_head.lock, &flags);
    gm_item_p = gm_list_head.head;
    while (gm_item_p)
      {
        item_count++;
        member_count += gm_item_p->sublist_length;
        gm_item_p = gm_item_p->next;
      };
    /* unprotect the list */
    rsbac_read_unlock(&gm_list_head.lock, &flags);
    len += sprintf(buffer + len, "%u group member items, sum of %u group memberships\n",
                   item_count,member_count);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

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
acl_acllist_proc_info(char *buffer, char **start, off_t offset, int length, int dummy)
#else
static int
acl_acllist_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_acl_fd_list_item_t  * fd_item_p;
    struct rsbac_acl_dev_list_item_t * dev_item_p;
    struct rsbac_acl_scd_list_item_t * scd_item_p;
    struct rsbac_acl_sublist_item_t  * sub_item_p;
    u_int                              no_dirty = 0;
    u_int                              i;
    char                               tmp1[80], tmp2[80];
    u_int                              count = 0;
    u_int                              member_count = 0;
    u_long flags,dflags;
    struct rsbac_acl_device_list_item_t   * device_p;

    union rsbac_target_id_t       rsbac_target_id;
    union rsbac_attribute_value_t rsbac_attribute_value;

    if (!rsbac_is_initialized)
      {
        printk(KERN_WARNING "acl_acllist_proc_info(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    if (rsbac_debug_aef_acl)
      printk(KERN_DEBUG "acl_acllist_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, "ACL Lists\n----------\n");

    /* protect default_fd list */
    rsbac_read_lock(&default_fd_lock, &flags);
    member_count = 0;
    count = 0;
    len += sprintf(buffer + len, "Default FD ACL:      %u members:",
                                 default_fd_acl.sublist_length);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    sub_item_p = default_fd_acl.sublist_head;
    while (sub_item_p)
      {
        member_count++;
        len += sprintf(buffer + len, " %s %u,",
                       get_acl_subject_type_name(tmp1,sub_item_p->entry.subj_type),
                       sub_item_p->entry.subj_id);
        pos = begin + len;
        if (pos < offset)
          {
            len = 0;
            begin = pos;
          }
        if (pos > offset+length)
          {
            rsbac_read_unlock(&default_fd_lock, &flags);
            goto out;
          }
        sub_item_p = sub_item_p->next;
      };
    /* unprotect the list */
    rsbac_read_unlock(&default_fd_lock, &flags);

    /* protect default_dev list */
    rsbac_read_lock(&default_dev_lock, &flags);
    member_count = 0;
    count = 0;
    len += sprintf(buffer + len, "\nDefault Device ACL:  %u members:",
                                 default_dev_acl.sublist_length);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    sub_item_p = default_dev_acl.sublist_head;
    while (sub_item_p)
      {
        member_count++;
        len += sprintf(buffer + len, " %s %u,",
                       get_acl_subject_type_name(tmp1,sub_item_p->entry.subj_type),
                       sub_item_p->entry.subj_id);
        pos = begin + len;
        if (pos < offset)
          {
            len = 0;
            begin = pos;
          }
        if (pos > offset+length)
          {
            rsbac_read_unlock(&default_dev_lock, &flags);
            goto out;
          }
        sub_item_p = sub_item_p->next;
      };
    /* unprotect the list */
    rsbac_read_unlock(&default_dev_lock, &flags);

    /* protect default_ipc_list */
    rsbac_read_lock(&default_ipc_lock, &flags);
    member_count = 0;
    count = 0;
    len += sprintf(buffer + len, "\nDefault IPC ACL:     %u members:",
                                 default_ipc_acl.sublist_length);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    sub_item_p = default_ipc_acl.sublist_head;
    while (sub_item_p)
      {
        member_count++;
        len += sprintf(buffer + len, " %s %u,",
                       get_acl_subject_type_name(tmp1,sub_item_p->entry.subj_type),
                       sub_item_p->entry.subj_id);
        pos = begin + len;
        if (pos < offset)
          {
            len = 0;
            begin = pos;
          }
        if (pos > offset+length)
          {
            rsbac_read_unlock(&default_ipc_lock, &flags);
            goto out;
          }
        sub_item_p = sub_item_p->next;
      };
    /* unprotect the list */
    rsbac_read_unlock(&default_ipc_lock, &flags);

    /* protect default_scd_list */
    rsbac_read_lock(&default_scd_lock, &flags);
    member_count = 0;
    count = 0;
    len += sprintf(buffer + len, "\nDefault SCD ACL:     %u members:",
                                 default_scd_acl.sublist_length);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    sub_item_p = default_scd_acl.sublist_head;
    while (sub_item_p)
      {
        member_count++;
        len += sprintf(buffer + len, " %s %u,",
                       get_acl_subject_type_name(tmp1,sub_item_p->entry.subj_type),
                       sub_item_p->entry.subj_id);
        pos = begin + len;
        if (pos < offset)
          {
            len = 0;
            begin = pos;
          }
        if (pos > offset+length)
          {
            rsbac_read_unlock(&default_scd_lock, &flags);
            goto out;
          }
        sub_item_p = sub_item_p->next;
      };
    /* unprotect the list */
    rsbac_read_unlock(&default_scd_lock, &flags);

    /* protect default_u_list */
    rsbac_read_lock(&default_u_lock, &flags);
    member_count = 0;
    count = 0;
    len += sprintf(buffer + len, "\nDefault User ACL:    %u members:",
                                 default_u_acl.sublist_length);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    sub_item_p = default_u_acl.sublist_head;
    while (sub_item_p)
      {
        member_count++;
        len += sprintf(buffer + len, " %s %u,",
                       get_acl_subject_type_name(tmp1,sub_item_p->entry.subj_type),
                       sub_item_p->entry.subj_id);
        pos = begin + len;
        if (pos < offset)
          {
            len = 0;
            begin = pos;
          }
        if (pos > offset+length)
          {
            rsbac_read_unlock(&default_u_lock, &flags);
            goto out;
          }
        sub_item_p = sub_item_p->next;
      };
    /* unprotect the list */
    rsbac_read_unlock(&default_u_lock, &flags);

    /* protect default_p list */
    rsbac_read_lock(&default_p_lock, &flags);
    member_count = 0;
    count = 0;
    len += sprintf(buffer + len, "\nDefault Process ACL: %u members:",
                                 default_p_acl.sublist_length);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    sub_item_p = default_p_acl.sublist_head;
    while (sub_item_p)
      {
        member_count++;
        len += sprintf(buffer + len, " %s %u,",
                       get_acl_subject_type_name(tmp1,sub_item_p->entry.subj_type),
                       sub_item_p->entry.subj_id);
        pos = begin + len;
        if (pos < offset)
          {
            len = 0;
            begin = pos;
          }
        if (pos > offset+length)
          {
            rsbac_read_unlock(&default_p_lock, &flags);
            goto out;
          }
        sub_item_p = sub_item_p->next;
      };
    /* unprotect the list */
    rsbac_read_unlock(&default_p_lock, &flags);


    len += sprintf(buffer + len, "\n\nFile/Dir ACLs:\n");
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    /* protect device list */
    rsbac_read_lock(&device_list_head.lock, &dflags);
    device_p = device_list_head.head;
    while(device_p)
      {
        /* reset counters */
        count = 0;
        member_count = 0;
        no_dirty = 0;
        len += sprintf(buffer + len, "\nDevice %02u:%02u\n inode  count   mask+members",
                       MAJOR(device_p->id),
                       MINOR(device_p->id));
        pos = begin + len;
        if (pos < offset)
          {
            len = 0;
            begin = pos;
          }
        if (pos > offset+length)
          {
            rsbac_read_unlock(&device_list_head.lock, &dflags);
            goto out;
          }
        for(i=0; i<RSBAC_ACL_NR_FD_LISTS; i++)
          {
            /* protect file list */
            rsbac_read_lock(&device_p->head_table[i].lock, &flags);
            fd_item_p = device_p->head_table[i].head;
            while (fd_item_p)
              {
                count++;
                len += sprintf(buffer + len, "\n%6lu\t  %u\t",
                               fd_item_p->id,
                               fd_item_p->sublist_length);
                pos = begin + len;
                if (pos < offset)
                  {
                    len = 0;
                    begin = pos;
                  }
                if (pos > offset+length)
                  {
                    rsbac_read_unlock(&device_p->head_table[i].lock, &flags);
                    rsbac_read_unlock(&device_list_head.lock, &dflags);
                    goto out;
                  }
                len += sprintf(buffer + len, "%s\n\t\t",
                               u64tostracl(tmp1, fd_item_p->mask));
                pos = begin + len;
                if (pos < offset)
                  {
                    len = 0;
                    begin = pos;
                  }
                if (pos > offset+length)
                  {
                    rsbac_read_unlock(&device_p->head_table[i].lock, &flags);
                    rsbac_read_unlock(&device_list_head.lock, &dflags);
                    goto out;
                  }
                sub_item_p = fd_item_p->sublist_head;
                while (sub_item_p)
                  {
                    member_count++;
                    len += sprintf(buffer + len, "%s %u, ",
                                   get_acl_subject_type_name(tmp1,sub_item_p->entry.subj_type),
                                   sub_item_p->entry.subj_id);
                    pos = begin + len;
                    if (pos < offset)
                      {
                        len = 0;
                        begin = pos;
                      }
                    if (pos > offset+length)
                      {
                        rsbac_read_unlock(&device_p->head_table[i].lock, &flags);
                        rsbac_read_unlock(&device_list_head.lock, &dflags);
                        goto out;
                      }
                    sub_item_p = sub_item_p->next;
                  };
                fd_item_p = fd_item_p->next;
              };
            /* unprotect the list */
            rsbac_read_unlock(&device_p->head_table[i].lock, &flags);
            if(device_p->head_table[i].dirty)
              no_dirty++;
          }
        len += sprintf(buffer + len, "\n%u file ACLs, sum of %u members, %u lists dirty\n",
                       count,member_count,
                       no_dirty);
        pos = begin + len;
        if (pos < offset)
          {
            len = 0;
            begin = pos;
          }
        if (pos > offset+length)
          {
            rsbac_read_unlock(&device_list_head.lock, &dflags);
            goto out;
          }
        device_p = device_p->next;
      }
    /* unprotect device list */
    rsbac_read_unlock(&device_list_head.lock, &dflags);

    /* protect dev list */
    rsbac_read_lock(&dev_list_head.lock, &flags);
    dev_item_p = dev_list_head.head;
    member_count = 0;
    count = 0;
    len += sprintf(buffer + len, "\nDevice ACLs:\ntype+id  count  mask+members");
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    while (dev_item_p)
      {
        count++;
        len += sprintf(buffer + len, "\n%c%02u:%02u\t  %3u\t%s\n\t\t",
                       'B' + dev_item_p->id.type,
                       MAJOR(dev_item_p->id.id),
                       MINOR(dev_item_p->id.id),
                       dev_item_p->sublist_length,
                       u64tostracl(tmp1, dev_item_p->mask));
        pos = begin + len;
        if (pos < offset)
          {
            len = 0;
            begin = pos;
          }
        if (pos > offset+length)
          {
            rsbac_read_unlock(&dev_list_head.lock, &flags);
            goto out;
          }
        sub_item_p = dev_item_p->sublist_head;
        while (sub_item_p)
          {
            member_count++;
            len += sprintf(buffer + len, "%s %u, ",
                           get_acl_subject_type_name(tmp1,sub_item_p->entry.subj_type),
                           sub_item_p->entry.subj_id);
            pos = begin + len;
            if (pos < offset)
              {
                len = 0;
                begin = pos;
              }
            if (pos > offset+length)
              {
                rsbac_read_unlock(&dev_list_head.lock, &flags);
                goto out;
              }
            sub_item_p = sub_item_p->next;
          };
        dev_item_p = dev_item_p->next;
      };
    /* unprotect the list */
    rsbac_read_unlock(&dev_list_head.lock, &flags);
    len += sprintf(buffer + len, "\n\n%u device ACL items, sum of %u members\n",
                   count,member_count);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    /* protect scd list */
    rsbac_read_lock(&scd_list_head.lock, &flags);
    scd_item_p = scd_list_head.head;
    member_count = 0;
    count = 0;
    len += sprintf(buffer + len, "\nSCD ACLs:\nname             count  mask+members");
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    while (scd_item_p)
      {
        count++;
        len += sprintf(buffer + len, "\n%-16s  %3u\t%s\n\t\t\t",
                       get_acl_scd_type_name(tmp1, scd_item_p->id),
                       scd_item_p->sublist_length,
                       u64tostracl(tmp2, scd_item_p->mask));
        pos = begin + len;
        if (pos < offset)
          {
            len = 0;
            begin = pos;
          }
        if (pos > offset+length)
          {
            rsbac_read_unlock(&scd_list_head.lock, &flags);
            goto out;
          }
        sub_item_p = scd_item_p->sublist_head;
        while (sub_item_p)
          {
            member_count++;
            len += sprintf(buffer + len, "%s %u, ",
                           get_acl_subject_type_name(tmp1,sub_item_p->entry.subj_type),
                           sub_item_p->entry.subj_id);
            pos = begin + len;
            if (pos < offset)
              {
                len = 0;
                begin = pos;
              }
            if (pos > offset+length)
              {
                rsbac_read_unlock(&scd_list_head.lock, &flags);
                goto out;
              }
            sub_item_p = sub_item_p->next;
          };
        scd_item_p = scd_item_p->next;
      };
    /* unprotect the list */
    rsbac_read_unlock(&scd_list_head.lock, &flags);
    len += sprintf(buffer + len, "\n\n%u SCD ACL items, sum of %u members\n",
                   count,member_count);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

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
acl_grouplist_proc_info(char *buffer, char **start, off_t offset, int length, int dummy)
#else
static int
acl_grouplist_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_acl_gm_list_item_t  * gm_item_p;
    struct rsbac_acl_gm_sublist_item_t * sub_item_p;
    struct rsbac_acl_group_list_item_t  * group_item_p;
    char                               type;
    u_int                              count = 0;
    u_int                              member_count = 0;
    u_long flags;

    union rsbac_target_id_t       rsbac_target_id;
    union rsbac_attribute_value_t rsbac_attribute_value;

    if (!rsbac_is_initialized)
      {
        printk(KERN_WARNING "acl_grouplist_proc_info(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    if (rsbac_debug_aef_acl)
      printk(KERN_DEBUG "acl_grouplist_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, "ACL Groups\n----------\n");

    /* protect group list */
    rsbac_read_lock(&group_list_head.lock, &flags);
    member_count = 0;
    count = 0;
    len += sprintf(buffer + len, "Group list:  %u groups, last new is %u\nID\ttype name\t\towner\n",
                                 group_list_head.count, group_list_head.last_new);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      {
        rsbac_read_unlock(&group_list_head.lock, &flags);
        goto out;
      }

    group_item_p = group_list_head.head;
    while (group_item_p)
      {
        count++;
        if(group_item_p->entry.type == ACLG_GLOBAL)
          type = 'G';
        else
          type = 'P';
        len += sprintf(buffer + len, "%u\t%c    %-18s %u\n",
                       group_item_p->entry.id,
                       type,
                       group_item_p->entry.name,
                       group_item_p->entry.owner);
        pos = begin + len;
        if (pos < offset)
          {
            len = 0;
            begin = pos;
          }
        if (pos > offset+length)
          {
            rsbac_read_unlock(&group_list_head.lock, &flags);
            goto out;
          }
        group_item_p = group_item_p->next;
      };
    /* unprotect the list */
    rsbac_read_unlock(&group_list_head.lock, &flags);

    /* protect group member list */
    rsbac_read_lock(&gm_list_head.lock, &flags);
    gm_item_p = gm_list_head.head;
    member_count = 0;
    count = 0;
    len += sprintf(buffer + len, "\nGroup memberships:\nuser   count\tgroups");
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      {
        rsbac_read_unlock(&gm_list_head.lock, &flags);
        goto out;
      }

    while (gm_item_p)
      {
        count++;
        len += sprintf(buffer + len, "\n%u\t%u\t",
                       gm_item_p->user,
                       gm_item_p->sublist_length);
        pos = begin + len;
        if (pos < offset)
          {
            len = 0;
            begin = pos;
          }
        if (pos > offset+length)
          {
            rsbac_read_unlock(&gm_list_head.lock, &flags);
            goto out;
          }
        sub_item_p = gm_item_p->sublist_head;
        while (sub_item_p)
          {
            member_count++;
            len += sprintf(buffer + len, "%u ",
                           sub_item_p->group);
            pos = begin + len;
            if (pos < offset)
              {
                len = 0;
                begin = pos;
              }
            if (pos > offset+length)
              {
                rsbac_read_unlock(&gm_list_head.lock, &flags);
                goto out;
              }
            sub_item_p = sub_item_p->next;
          };
        gm_item_p = gm_item_p->next;
      };
    /* unprotect the list */
    rsbac_read_unlock(&gm_list_head.lock, &flags);
    len += sprintf(buffer + len, "\n\n%u user items, sum of %u memberships\n",
                   count,member_count);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

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
acl_grp_proc_info(char *buffer, char **start, off_t offset, int length, int dummy)
#else
static int
acl_grp_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_acl_group_list_item_t  * item_p;
  u_long          flags;
  u_int           aci_version = RSBAC_ACL_GROUP_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_acl)
    printk(KERN_DEBUG "acl_grp_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);

  /* protect this list */
    rsbac_read_lock(&group_list_head.lock, &flags);
  /* copy last_new */
  memcpy(buffer+len,
         &group_list_head.last_new,
         sizeof(group_list_head.last_new));
  len += sizeof(group_list_head.last_new);
  /* copy count */
  memcpy(buffer+len,
         (char *) &(group_list_head.count),
         sizeof(group_list_head.count));
  len += sizeof(group_list_head.count);

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

  item_p = group_list_head.head;
  while (item_p)
    {
      memcpy(buffer+len,
             (char *) &(item_p->entry),
             sizeof(item_p->entry));
      len += sizeof(item_p->entry);
      item_p = item_p->next;

      pos = begin + len;
      if (pos < offset)
        {
          len = 0;
          begin = pos;
        }
      if (pos > offset+length)
        break;
    }
out:
  /* unprotect this list */
  rsbac_read_unlock(&group_list_head.lock, &flags);

  *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
acl_gm_proc_info(char *buffer, char **start, off_t offset, int length, int dummy)
#else
static int
acl_gm_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_acl_gm_list_item_t    * item_p;
  struct rsbac_acl_gm_sublist_item_t * sub_item_p;
  u_long          flags;
  u_int           aci_version = RSBAC_ACL_GM_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_acl)
    printk(KERN_DEBUG "acl_grp_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;

  /* protect this list */
    rsbac_read_lock(&gm_list_head.lock, &flags);
  /* copy list */
  item_p = gm_list_head.head;
  while (item_p) 
    {
      memcpy(buffer+len,
             (char *) &item_p->user,
             sizeof(item_p->user));
      len += sizeof(item_p->user);
      memcpy(buffer+len,
             (char *) &item_p->sublist_length,
             sizeof(item_p->sublist_length));
      len += sizeof(item_p->sublist_length);
      pos = begin + len;
      if (pos < offset)
        {
          len = 0;
          begin = pos;
        }
      if (pos > offset+length)
        break;

      sub_item_p = item_p->sublist_head;
      while (sub_item_p)
        {
          memcpy(buffer+len,
                 (char *) &(sub_item_p->group),
                 sizeof(sub_item_p->group));
          len += sizeof(sub_item_p->group);
          sub_item_p = sub_item_p->next;
          pos = begin + len;
          if (pos < offset)
            {
              len = 0;
              begin = pos;
            }
          if (pos > offset+length)
            goto out_unlock;
        }
      item_p = item_p->next;
    }

out_unlock:
  /* unprotect this list */
  rsbac_read_unlock(&gm_list_head.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 ACL data structures. After this call, all ACL    */
/* data is kept in memory for performance reasons, but is written to disk   */
/* on every change. */

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

inline int __init rsbac_init_acl(void)
  {
    int  err = 0;
    struct rsbac_acl_device_list_item_t * device_p = NULL;
    char tmp[80];
    u_long flags;
    struct proc_dir_entry * tmp_entry_p;

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

    /* set rw-spinlocks to unlocked status and init data structures */
    printk(KERN_INFO "rsbac_init_acl(): Initializing RSBAC: ACL subsystem\n");

    /* Init lists */
    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.count    = 0;
    dev_list_head.no_write = FALSE;
    scd_list_head.lock=RW_LOCK_UNLOCKED;
    scd_list_head.head     = NULL;
    scd_list_head.tail     = NULL;
    scd_list_head.curr     = NULL;
    scd_list_head.count    = 0;
    scd_list_head.no_write = FALSE;
    gm_list_head.lock=RW_LOCK_UNLOCKED;
    gm_list_head.head      = NULL;
    gm_list_head.tail      = NULL;
    gm_list_head.curr      = NULL;
    gm_list_head.count     = 0;
    gm_list_head.dirty     = FALSE;
    gm_list_head.no_write  = FALSE;
    group_list_head.lock=RW_LOCK_UNLOCKED;
    group_list_head.head   = NULL;
    group_list_head.tail   = NULL;
    group_list_head.curr   = NULL;
    group_list_head.last_new = 0;
    group_list_head.count  = 0;
    group_list_head.dirty  = FALSE;
    group_list_head.no_write = FALSE;

    /* Init default lists */
    default_fd_acl.id              = 0;
    default_fd_acl.mask            = RSBAC_ACL_DEFAULT_MASK;
    default_fd_acl.sublist_length  = 0;
    default_fd_acl.sublist_head    = NULL;
    default_fd_acl.sublist_tail    = NULL;
    default_fd_acl.prev            = NULL;
    default_fd_acl.next            = NULL;
    default_fd_lock=RW_LOCK_UNLOCKED;
    default_fd_dirty               = FALSE;
    default_dev_acl.id.type        = D_none;
    default_dev_acl.id.id          = 0;
    default_dev_acl.mask           = RSBAC_ACL_DEFAULT_MASK;
    default_dev_acl.sublist_length = 0;
    default_dev_acl.sublist_head   = NULL;
    default_dev_acl.sublist_tail   = NULL;
    default_dev_acl.prev           = NULL;
    default_dev_acl.next           = NULL;
    default_dev_lock=RW_LOCK_UNLOCKED;
    default_dev_dirty              = FALSE;
    default_ipc_acl.id.type        = I_none;
    default_ipc_acl.id.id.id_nr    = 0;
    default_ipc_acl.mask           = RSBAC_ACL_DEFAULT_MASK;
    default_ipc_acl.sublist_length = 0;
    default_ipc_acl.sublist_head   = NULL;
    default_ipc_acl.sublist_tail   = NULL;
    default_ipc_acl.prev           = NULL;
    default_ipc_acl.next           = NULL;
    default_ipc_lock=RW_LOCK_UNLOCKED;
    default_ipc_dirty              = FALSE;
    default_scd_acl.id             = AST_none;
    default_scd_acl.mask           = RSBAC_ACL_DEFAULT_MASK;
    default_scd_acl.sublist_length = 0;
    default_scd_acl.sublist_head   = NULL;
    default_scd_acl.sublist_tail   = NULL;
    default_scd_acl.prev           = NULL;
    default_scd_acl.next           = NULL;
    default_scd_lock=RW_LOCK_UNLOCKED;
    default_scd_dirty              = FALSE;
    default_u_acl.id               = 0;
    default_u_acl.mask             = RSBAC_ACL_DEFAULT_MASK;
    default_u_acl.sublist_length   = 0;
    default_u_acl.sublist_head     = NULL;
    default_u_acl.sublist_tail     = NULL;
    default_u_acl.prev             = NULL;
    default_u_acl.next             = NULL;
    default_u_lock=RW_LOCK_UNLOCKED;
    default_u_dirty                = FALSE;
    default_p_acl.id               = 0;
    default_p_acl.mask             = RSBAC_ACL_DEFAULT_MASK;
    default_p_acl.sublist_length   = 0;
    default_p_acl.sublist_head     = NULL;
    default_p_acl.sublist_tail     = NULL;
    default_p_acl.prev             = NULL;
    default_p_acl.next             = NULL;
    default_p_lock=RW_LOCK_UNLOCKED;
    default_p_dirty                = FALSE;

    /* read all data */
    if (rsbac_debug_ds_acl)
      printk(KERN_DEBUG "rsbac_init_acl(): Reading data\n");
    /* wait for write access to device_list_head */
    rsbac_write_lock(&device_list_head.lock, &flags);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
    device_p = add_device_item(ROOT_DEV);
#else
    device_p = add_device_item(ROOT_DEV, NULL);
#endif
    /* device was added, allow access */
    rsbac_write_unlock(&device_list_head.lock, &flags);
    if (!device_p)
      {
        printk(KERN_CRIT "rsbac_init_acl(): Could not add device!\n");
        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 = acl_read_fd_lists(device_p,ROOT_DEV)))
      printk(KERN_WARNING
             "rsbac_init_acl(): File/Dir ACLs not fully read from dev %02u:%02u, err %s!\n",
             MAJOR(ROOT_DEV), MINOR(ROOT_DEV), get_error_name(tmp,err));
    /* allow access */
    rsbac_read_unlock(&device_list_head.lock, &flags);

    /* wait for write access to default_fd_acl */
    rsbac_write_lock(&default_fd_lock, &flags);
    if((err = acl_read_default_fd_acl()))
      {
        struct rsbac_acl_entry_t acman_entry = RSBAC_ACL_ACMAN_FD_ENTRY;
        struct rsbac_acl_entry_t sysadm_entry = RSBAC_ACL_SYSADM_FD_ENTRY;
        struct rsbac_acl_entry_t gen_entry = RSBAC_ACL_GENERAL_FD_ENTRY;

        printk(KERN_WARNING
               "rsbac_init_acl(): File/Dir default ACL not fully read from dev %02u:%02u, err %s, generating standard ACL!\n",
               MAJOR(ROOT_DEV), MINOR(ROOT_DEV), get_error_name(tmp,err));
        add_fd_subitem(&default_fd_acl, acman_entry);
        add_fd_subitem(&default_fd_acl, sysadm_entry);
        add_fd_subitem(&default_fd_acl, gen_entry);
      }
    /* free access to default_fd_acl */
    rsbac_write_unlock(&default_fd_lock, &flags);

    /* wait for write access to dev_list */
    rsbac_write_lock(&dev_list_head.lock, &flags);
    if((err = acl_read_dev_list()))
      printk(KERN_WARNING
             "rsbac_init_acl(): Device ACLs not fully read from dev %02u:%02u, err %s!\n",
             MAJOR(ROOT_DEV), MINOR(ROOT_DEV), get_error_name(tmp,err));
    /* free access to default_fd_acl */
    rsbac_write_unlock(&dev_list_head.lock, &flags);

    /* wait for write access to default_dev_acl */
    rsbac_write_lock(&default_dev_lock, &flags);
    if((err = acl_read_default_dev_acl()))
      {
        struct rsbac_acl_entry_t acman_entry = RSBAC_ACL_ACMAN_DEV_ENTRY;
        struct rsbac_acl_entry_t sysadm_entry = RSBAC_ACL_SYSADM_DEV_ENTRY;
        struct rsbac_acl_entry_t gen_entry = RSBAC_ACL_GENERAL_DEV_ENTRY;

        printk(KERN_WARNING
               "rsbac_init_acl(): Device default ACL not fully read from dev %02u:%02u, err %s, generating standard ACL!\n",
               MAJOR(ROOT_DEV), MINOR(ROOT_DEV), get_error_name(tmp,err));
        add_dev_subitem(&default_dev_acl, acman_entry);
        add_dev_subitem(&default_dev_acl, sysadm_entry);
        add_dev_subitem(&default_dev_acl, gen_entry);
      }
    /* free access to default_dev_acl */
    rsbac_write_unlock(&default_dev_lock, &flags);

    /* wait for write access to default_ipc_acl */
    rsbac_write_lock(&default_ipc_lock, &flags);
    if((err = acl_read_default_ipc_acl()))
      {
        struct rsbac_acl_entry_t acman_entry = RSBAC_ACL_ACMAN_IPC_ENTRY;
        struct rsbac_acl_entry_t sysadm_entry = RSBAC_ACL_SYSADM_IPC_ENTRY;
        struct rsbac_acl_entry_t gen_entry = RSBAC_ACL_GENERAL_IPC_ENTRY;

        printk(KERN_WARNING
               "rsbac_init_acl(): IPC default ACL not fully read from dev %02u:%02u, err %s, generating standard ACL!\n",
               MAJOR(ROOT_DEV), MINOR(ROOT_DEV), get_error_name(tmp,err));
        add_ipc_subitem(&default_ipc_acl, acman_entry);
        add_ipc_subitem(&default_ipc_acl, sysadm_entry);
        add_ipc_subitem(&default_ipc_acl, gen_entry);
      }
    /* free access to default_fd_acl */
    rsbac_write_unlock(&default_ipc_lock, &flags);

    /* wait for write access to scd_list */
    rsbac_write_lock(&scd_list_head.lock, &flags);
    if((err = acl_read_scd_list()))
      {
        struct rsbac_acl_entry_t gen_entry = RSBAC_ACL_GENERAL_SCD_ENTRY;
        #ifdef CONFIG_RSBAC_USER_MOD_IOPERM
        struct rsbac_acl_entry_t gen_ioports_entry = RSBAC_ACL_GENERAL_SCD_IOPORTS_ENTRY;
        #endif
        struct rsbac_acl_entry_t sysadm_entry = RSBAC_ACL_SYSADM_SCD_ENTRY;
        struct rsbac_acl_entry_t sysadm_other_entry = RSBAC_ACL_SYSADM_SCD_OTHER_ENTRY;
        struct rsbac_acl_entry_t acman_other_entry = RSBAC_ACL_ACMAN_SCD_OTHER_ENTRY;
        struct rsbac_acl_scd_list_item_t * scd_item_p;
        enum  rsbac_acl_scd_type_t scd;

        printk(KERN_WARNING
               "rsbac_init_acl(): SCD ACLs not fully read from dev %02u:%02u, err %s, adding standard entries!\n",
               MAJOR(ROOT_DEV), MINOR(ROOT_DEV), get_error_name(tmp,err));
        scd_item_p = lookup_scd_item(ST_rlimit);
        if(!scd_item_p)
          scd_item_p = add_scd_item(ST_rlimit, RSBAC_ACL_DEFAULT_MASK);
        if(scd_item_p && !lookup_scd_subitem(scd_item_p, ACLS_GROUP, RSBAC_ACL_GROUP_EVERYONE))
          add_scd_subitem(scd_item_p, gen_entry);
        for(scd=ST_time_strucs; scd<=ST_rsbac; scd++)
          {
            scd_item_p = lookup_scd_item(scd);
            if(!scd_item_p)
              scd_item_p = add_scd_item(scd, RSBAC_ACL_DEFAULT_MASK);
            if(scd_item_p && !lookup_scd_subitem(scd_item_p, ACLS_USER, RSBAC_SYSADM_UID))
              add_scd_subitem(scd_item_p, sysadm_entry);
          }
        #ifdef CONFIG_RSBAC_USER_MOD_IOPERM
        scd_item_p = lookup_scd_item(ST_ioports);
        if(!scd_item_p)
          scd_item_p = add_scd_item(ST_ioports, RSBAC_ACL_DEFAULT_MASK);
        if(scd_item_p && !lookup_scd_subitem(scd_item_p, ACLS_GROUP, RSBAC_ACL_GROUP_EVERYONE))
          add_scd_subitem(scd_item_p, gen_ioports_entry);
        #endif
        scd_item_p = lookup_scd_item(ST_other);
        if(!scd_item_p)
          scd_item_p = add_scd_item(ST_other, RSBAC_ACL_DEFAULT_MASK);
        if(scd_item_p && !lookup_scd_subitem(scd_item_p, ACLS_USER, RSBAC_SECOFF_UID))
          add_scd_subitem(scd_item_p, acman_other_entry);
        if(scd_item_p && !lookup_scd_subitem(scd_item_p, ACLS_USER, RSBAC_SYSADM_UID))
          add_scd_subitem(scd_item_p, sysadm_other_entry);
      }
    /* free access to scd_list */
    rsbac_write_unlock(&scd_list_head.lock, &flags);

    /* wait for write access to default_scd_acl */
    rsbac_write_lock(&default_scd_lock, &flags);
    if((err = acl_read_default_scd_acl()))
      {
        struct rsbac_acl_entry_t acman_entry = RSBAC_ACL_ACMAN_SCD_ENTRY;

        printk(KERN_WARNING
               "rsbac_init_acl(): SCD default ACL not fully read from dev %02u:%02u, err %s, generating standard ACL!\n",
               MAJOR(ROOT_DEV), MINOR(ROOT_DEV), get_error_name(tmp,err));
        add_scd_subitem(&default_scd_acl, acman_entry);
      }
    /* free access to default_scd_acl */
    rsbac_write_unlock(&default_scd_lock, &flags);

    /* wait for write access to default_u_acl */
    rsbac_write_lock(&default_u_lock, &flags);
    if((err = acl_read_default_u_acl()))
      {
        struct rsbac_acl_entry_t acman_entry = RSBAC_ACL_ACMAN_U_ENTRY;
        struct rsbac_acl_entry_t sysadm_entry = RSBAC_ACL_SYSADM_U_ENTRY;

        printk(KERN_WARNING
               "rsbac_init_acl(): User default ACL not fully read from dev %02u:%02u, err %s, generating standard ACL!\n",
               MAJOR(ROOT_DEV), MINOR(ROOT_DEV), get_error_name(tmp,err));
        add_u_subitem(&default_u_acl, acman_entry);
        add_u_subitem(&default_u_acl, sysadm_entry);
      }
    /* free access to default_fd_acl */
    rsbac_write_unlock(&default_u_lock, &flags);

    /* wait for write access to default_p_acl */
    rsbac_write_lock(&default_p_lock, &flags);
    if((err = acl_read_default_p_acl()))
      {
        struct rsbac_acl_entry_t acman_entry = RSBAC_ACL_ACMAN_P_ENTRY;
        struct rsbac_acl_entry_t sysadm_entry = RSBAC_ACL_SYSADM_P_ENTRY;
        struct rsbac_acl_entry_t gen_entry = RSBAC_ACL_GENERAL_P_ENTRY;

        printk(KERN_WARNING
               "rsbac_init_acl(): Process default ACL not fully read from dev %02u:%02u, err %s, generating standard ACL!\n",
               MAJOR(ROOT_DEV), MINOR(ROOT_DEV), get_error_name(tmp,err));
        add_p_subitem(&default_p_acl, acman_entry);
        add_p_subitem(&default_p_acl, sysadm_entry);
        add_p_subitem(&default_p_acl, gen_entry);
      }
    /* free access to default_fd_acl */
    rsbac_write_unlock(&default_p_lock, &flags);

    /* wait for write access to group list */
    rsbac_write_lock(&group_list_head.lock, &flags);
    if((err = acl_read_group_list()))
      {
        printk(KERN_WARNING
               "rsbac_init_acl(): Group list not fully read from dev %02u:%02u, err %s!\n",
               MAJOR(ROOT_DEV), MINOR(ROOT_DEV), get_error_name(tmp,err));
      }
    /* free access to group list */
    rsbac_write_unlock(&group_list_head.lock, &flags);

    /* wait for write access to group membership list */
    rsbac_write_lock(&gm_list_head.lock, &flags);
    if((err = acl_read_gm_list()))
      {
        printk(KERN_WARNING
               "rsbac_init_acl(): Group membership list not fully read from dev %02u:%02u, err %s!\n",
               MAJOR(ROOT_DEV), MINOR(ROOT_DEV), get_error_name(tmp,err));
      }
    /* free access to group membership list */
    rsbac_write_unlock(&gm_list_head.lock, &flags);

    #if defined(CONFIG_RSBAC_PROC) && defined(CONFIG_PROC_FS)
    tmp_entry_p = create_proc_entry("stats_acl",
                                    S_IFREG | S_IRUGO,
                                    proc_rsbac_root_p);
    if(tmp_entry_p)
      {
        tmp_entry_p->get_info = stats_acl_proc_info;
      }
    tmp_entry_p = create_proc_entry("acl_acllist",
                                    S_IFREG | S_IRUGO,
                                    proc_rsbac_root_p);
    if(tmp_entry_p)
      {
        tmp_entry_p->get_info = acl_acllist_proc_info;
      }
    tmp_entry_p = create_proc_entry("acl_grouplist",
                                    S_IFREG | S_IRUGO,
                                    proc_rsbac_root_p);
    if(tmp_entry_p)
      {
        tmp_entry_p->get_info = acl_grouplist_proc_info;
      }

    tmp_entry_p = create_proc_entry("acl_grp",
                                    S_IFREG | S_IRUGO,
                                    proc_rsbac_backup_p);
    if(tmp_entry_p)
      {
        tmp_entry_p->get_info = acl_grp_proc_info;
      }
    tmp_entry_p = create_proc_entry("acl_gm",
                                    S_IFREG | S_IRUGO,
                                    proc_rsbac_backup_p);
    if(tmp_entry_p)
      {
        tmp_entry_p->get_info = acl_gm_proc_info;
      }
    #endif

    if (rsbac_debug_ds_acl)
      printk(KERN_DEBUG "rsbac_init_acl(): Ready.\n");
    return(err);
  };

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
int rsbac_mount_acl(kdev_t kdev)
#else
int rsbac_mount_acl(kdev_t kdev, struct dentry * d_covers)
#endif
  {
    int err = 0;
    struct rsbac_acl_device_list_item_t * device_p;
    u_long flags;

    if (!rsbac_is_initialized())
      {
        printk(KERN_WARNING "rsbac_mount_acl(): RSBAC not initialized\n");
          return(-RSBAC_ENOTINITIALIZED);
      }
    if (rsbac_debug_ds_acl)
      printk(KERN_DEBUG "rsbac_mount_acl(): 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 */
        if((err = acl_read_fd_lists(device_p, kdev)))
          if (rsbac_debug_ds_acl)
            printk(KERN_WARNING
                   "rsbac_mount_acl(): File/Dir ACLs not fully read from dev %02u:%02u!\n",
                   MAJOR(kdev), MINOR(kdev));
        /* device was set, free access */
        rsbac_read_unlock(&device_list_head.lock, &flags);
      }
    else
      /* error: */
      err = -RSBAC_ECOULDNOTADDDEVICE;
    return(err);
  };
  
/* When umounting a device, its file/dir ACLs must be removed. */

int rsbac_umount_acl(kdev_t kdev)
  {
    u_long flags;

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

    if (rsbac_debug_ds)
      printk(KERN_DEBUG "rsbac_umount_acl(): umounting device %02u:%02u\n",
             MAJOR(kdev), MINOR(kdev));
    /* sync of attribute lists was done in rsbac_umount */
    /* 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_acl(void)
  {
    struct rsbac_acl_fd_list_item_t         * fd_item_p;
    struct rsbac_acl_dev_list_item_t        * dev_item_p;
    struct rsbac_acl_scd_list_item_t        * scd_item_p;
    struct rsbac_acl_gm_list_item_t         * gm_item_p;
    u_int                                     item_count = 0;
    u_int                                     member_count = 0;
    u_int  i;
    u_long flags,dflags;
    struct rsbac_acl_device_list_item_t     * device_p;
  
    union rsbac_target_id_t       rsbac_target_id;
    union rsbac_attribute_value_t rsbac_attribute_value;

    if (!rsbac_is_initialized)
      {
        printk(KERN_WARNING "rsbac_stats_acl(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    if (rsbac_debug_aef_acl)
      printk(KERN_DEBUG "rsbac_stats_acl(): 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;
      }

    printk(KERN_INFO "ACL Status\n-----------\n");

    /* protect device list */
    rsbac_read_lock(&device_list_head.lock, &dflags);
    device_p = device_list_head.head;
    while(device_p)
      {
        /* reset counters */
        item_count = 0;
        member_count = 0;
        for(i=0; i<RSBAC_ACL_NR_FD_LISTS; i++)
          {
            /* protect file list */
            rsbac_read_lock(&device_p->head_table[i].lock, &flags);
            fd_item_p = device_p->head_table[i].head;
            while (fd_item_p)
              {
                item_count++;
                member_count += fd_item_p->sublist_length;
                fd_item_p = fd_item_p->next;
              };
            /* unprotect the list */
            rsbac_read_unlock(&device_p->head_table[i].lock, &flags);
          }
        printk(KERN_INFO "device %02u:%02u has %u file ACLs, sum of %u members\n",
                         MAJOR(device_p->id),
                         MINOR(device_p->id),
                         item_count,member_count);
        device_p = device_p->next;
      }
    /* unprotect device list */
    rsbac_read_unlock(&device_list_head.lock, &dflags);

    /* protect dev list */
    rsbac_read_lock(&dev_list_head.lock, &flags);
    dev_item_p = dev_list_head.head;
    item_count = 0;
    member_count = 0;
    while (dev_item_p)
      {
        item_count++;
        member_count += dev_item_p->sublist_length;
        dev_item_p = dev_item_p->next;
      };
    /* unprotect the list */
    rsbac_read_unlock(&dev_list_head.lock, &flags);
    printk(KERN_INFO "%u Device ACLs, sum of %u members\n",
           item_count,member_count);

    /* protect scd list */
    rsbac_read_lock(&scd_list_head.lock, &flags);
    scd_item_p = scd_list_head.head;
    item_count = 0;
    member_count = 0;
    while (scd_item_p)
      {
        item_count++;
        member_count += scd_item_p->sublist_length;
        scd_item_p = scd_item_p->next;
      };
    /* unprotect the list */
    rsbac_read_unlock(&scd_list_head.lock, &flags);
    printk(KERN_INFO "%u SCD ACLs, sum of %u members\n",
           item_count,member_count);

    /* protect gm list */
    rsbac_read_lock(&gm_list_head.lock, &flags);
    gm_item_p = gm_list_head.head;
    item_count = 0;
    member_count = 0;
    while (gm_item_p)
      {
        item_count++;
        member_count += gm_item_p->sublist_length;
        gm_item_p = gm_item_p->next;
      };
    /* unprotect the list */
    rsbac_read_unlock(&gm_list_head.lock, &flags);
    printk(KERN_INFO "%u group membership user entries, sum of %u memberships\n",
           item_count,member_count);

    return(0);
  };

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

int rsbac_check_acl(int correct, int check_inode)
  {
    struct rsbac_acl_device_list_item_t * device_p;
    boolean                             no_write;
    u_long                              f_count = 0, f_sum = 0, tmp_count,
                                        r_count, u_count, b_count, no_member_count,
                                        sub_count;
    u_int                               f_dirty = 0, dirty = 0;
    int                                 list_no;
    u_long                              flags, dflags;
    struct super_block                * sb_p;
    struct inode                      * inode_p;
    struct rsbac_acl_fd_list_item_t   * fd_item_p, * fd_item_tmp_p;
    struct rsbac_acl_dev_list_item_t  * dev_item_p, * dev_item_tmp_p;
    struct rsbac_acl_scd_list_item_t  * scd_item_p, * scd_item_tmp_p;
    struct rsbac_acl_sublist_item_t   * subitem_p;
    struct rsbac_acl_group_list_item_t * group_item_p;
    struct rsbac_acl_gm_list_item_t   * gm_item_p;
    struct rsbac_acl_gm_sublist_item_t * gm_subitem_p;
    struct rsbac_acl_fd_list_head_t   * head_p;

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

    /* group list */
    rsbac_write_lock(&group_list_head.lock, &flags);
    tmp_count = 0;
    group_item_p = group_list_head.head;
    while(group_item_p)
      {
        tmp_count++;
        group_item_p = group_item_p->next;
      }
    if(tmp_count != group_list_head.count)
      {
        printk(KERN_WARNING
               "rsbac_check_acl(): correcting count mismatch for group list - was %u, counted %lu!\n",
               group_list_head.count, tmp_count);
        group_list_head.count = tmp_count;
      }
    rsbac_write_unlock(&group_list_head.lock, &flags);
    printk(KERN_INFO "rsbac_check_acl(): %u group items\n",
                 group_list_head.count);

    /* group membership list */
    rsbac_read_lock(&group_list_head.lock, &dflags);
    rsbac_write_lock(&gm_list_head.lock, &flags);
    tmp_count = 0;
    gm_item_p = gm_list_head.head;
    while(gm_item_p)
      {
        sub_count = 0;
        gm_subitem_p = gm_item_p->sublist_head;
        while(gm_subitem_p)
          {
            if(lookup_group_item(gm_subitem_p->group))
              {
                sub_count++;
                gm_subitem_p = gm_subitem_p->next;
              }
            else
              {
                rsbac_acl_group_id_t group;

                printk(KERN_WARNING
                       "rsbac_check_acl(): removing user %u membership in non-existent group %u!\n",
                       gm_item_p->user, gm_subitem_p->group);
                group = gm_subitem_p->group;
                gm_subitem_p = gm_subitem_p->next;
                remove_gm_subitem(gm_item_p, group);
                gm_list_head.dirty = TRUE;
              }
          }
        if(sub_count != gm_item_p->sublist_length)
          {
            printk(KERN_WARNING
                   "rsbac_check_acl(): correcting count mismatch for user group list - was %u, counted %lu!\n",
                   gm_item_p->sublist_length, sub_count);
            gm_item_p->sublist_length = sub_count;
          }
        if(!gm_item_p->sublist_length)
          { /* remove user item without sublist */
            rsbac_uid_t user = gm_item_p->user;

            gm_item_p = gm_item_p->next;
            remove_gm_item(user);
            gm_list_head.dirty = TRUE;
          }
        else
          {
            tmp_count++;
            gm_item_p = gm_item_p->next;
          }
      }
    if(tmp_count != gm_list_head.count)
      {
        printk(KERN_WARNING
               "rsbac_check_acl(): correcting count mismatch for group membership list - was %u, counted %lu!\n",
               gm_list_head.count, tmp_count);
        gm_list_head.count = tmp_count;
      }
    rsbac_write_unlock(&gm_list_head.lock, &flags);
    rsbac_read_unlock(&group_list_head.lock, &dflags);
    printk(KERN_INFO "rsbac_check_acl(): %u group membership items\n",
                 gm_list_head.count);

    /* wait for read access to device_list_head */
    rsbac_read_lock(&device_list_head.lock, &dflags);
    /* OK, go on */
/*    printk(KERN_INFO "rsbac_check_acl(): 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 */
        f_count = 0;
        r_count = 0;
        u_count = 0;
        b_count = 0;
        no_member_count = 0;
        f_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_acl(): 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_acl(): 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(list_no = 0; list_no < RSBAC_ACL_NR_FD_LISTS; list_no++)
          {
/*            printk(KERN_INFO "rsbac_check_acl(): list %u\n",
                   list_no); */
            tmp_count = 0;
            head_p = &device_p->head_table[list_no];
            rsbac_write_lock(&head_p->lock, &flags);
            fd_item_p = head_p->head;
            while(fd_item_p)
              {
                /* check for inode on disk */
                if(sb_p)
                  {
                    inode_p = iget(sb_p, fd_item_p->id);
                    if(is_bad_inode(inode_p))
                      { /* inode is bad -> remove */
                        b_count++;
                        if(correct)
                          {
                            printk(KERN_INFO
                                   "rsbac_check_acl(): fd_item for bad inode %lu on device %02u:%02u, list %u, removing!\n",
                                   fd_item_p->id, MAJOR(device_p->id), MINOR(device_p->id), list_no);
                            fd_item_tmp_p = fd_item_p;
                            fd_item_p = fd_item_p->next;
                            head_p->curr = fd_item_tmp_p;
                            remove_fd_item(device_p, fd_item_tmp_p->id);
                            head_p->dirty = TRUE;
                            continue;
                          }
                        else
                          {
                            printk(KERN_INFO
                                   "rsbac_check_acl(): fd_item for bad inode %lu on device %02u:%02u, list %u!\n",
                                   fd_item_p->id, MAJOR(device_p->id), MINOR(device_p->id), list_no);
                          }
                      } /* 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_acl(): fd_item for deleted inode %lu on device %02u:%02u, list %u, removing!\n",
                                           fd_item_p->id, MAJOR(device_p->id), MINOR(device_p->id), list_no);
                                    fd_item_tmp_p = fd_item_p;
                                    fd_item_p = fd_item_p->next;
                                    head_p->curr = fd_item_tmp_p;
                                    remove_fd_item(device_p, fd_item_tmp_p->id);
                                    head_p->dirty = TRUE;
                                    continue;
                                  }
                                else
                                  {
                                    printk(KERN_INFO
                                           "rsbac_check_acl(): fd_item for deleted inode %lu on device %02u:%02u, list %u!\n",
                                           fd_item_p->id, MAJOR(device_p->id), MINOR(device_p->id), list_no);
                                  }
                              }
                            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_acl(): fd_item for inode %lu with nlink <= 0 on device %02u:%02u, list %u, removing!\n",
                                               fd_item_p->id, MAJOR(device_p->id), MINOR(device_p->id), list_no);
                                        fd_item_tmp_p = fd_item_p;
                                        fd_item_p = fd_item_p->next;
                                        head_p->curr = fd_item_tmp_p;
                                        remove_fd_item(device_p, fd_item_tmp_p->id);
                                        head_p->dirty = TRUE;
                                        continue;
                                      }
                                    else
                                      {
                                        printk(KERN_INFO
                                               "rsbac_check_acl(): deleted inode %lu on device %02u:%02u, list %u, has no dtime!\n",
                                               fd_item_p->id, MAJOR(device_p->id), MINOR(device_p->id), list_no);
                                      }
                                  }
                              }
                          }
                      } /* end of is_good_inode */
                    iput(inode_p);
                  } /* end of sb_p */

                /* check for group existence */
                subitem_p = fd_item_p->sublist_head;
                while(subitem_p)
                  {
                    if(   (subitem_p->entry.subj_type == ACLS_GROUP)
                       && subitem_p->entry.subj_id
                       && !rsbac_acl_group_exist(subitem_p->entry.subj_id)
                      )
                      {
                        if(correct)
                          {
                            rsbac_acl_group_id_t group;

                            /* remove sub item and complain */
                            if(rsbac_debug_ds)
                              printk(KERN_DEBUG
                                     "rsbac_check_acl(): fd_item for inode %lu on device %02u:%02u, list %u, has invalid group %u in ACL -> removing entry!\n",
                                     fd_item_p->id, MAJOR(device_p->id), MINOR(device_p->id), list_no, subitem_p->entry.subj_id);
                            group = subitem_p->entry.subj_id;
                            subitem_p = subitem_p->next;
                            remove_fd_subitem(fd_item_p, ACLS_GROUP, group);
                          }
                        else
                          {
                            /* complain */
                            if(rsbac_debug_ds)
                              printk(KERN_DEBUG
                                     "rsbac_check_acl(): fd_item for inode %lu on device %02u:%02u, list %u, has invalid group %u in ACL!\n",
                                     fd_item_p->id, MAJOR(device_p->id), MINOR(device_p->id), list_no, subitem_p->entry.subj_id);
                            subitem_p = subitem_p->next;
                          }
                      }
                    #if defined(CONFIG_RSBAC_RC) || defined(CONFIG_RSBAC_RC_MAINT)
                    else
                    if(   (subitem_p->entry.subj_type == ACLS_ROLE)
                       && (subitem_p->entry.subj_id > RSBAC_RC_MAX_ROLE)
                      )
                      {
                        if(correct)
                          {
                            rsbac_rc_role_id_t role;

                            /* remove sub item and complain */
                            if(rsbac_debug_ds)
                              printk(KERN_DEBUG
                                     "rsbac_check_acl(): fd_item for inode %lu on device %02u:%02u, list %u, has invalid RC role %u in ACL -> removing entry!\n",
                                     fd_item_p->id, MAJOR(device_p->id), MINOR(device_p->id), list_no, subitem_p->entry.subj_id);
                            role = subitem_p->entry.subj_id;
                            subitem_p = subitem_p->next;
                            remove_fd_subitem(fd_item_p, ACLS_ROLE, role);
                          }
                        else
                          {
                            /* complain */
                            if(rsbac_debug_ds)
                              printk(KERN_DEBUG
                                     "rsbac_check_acl(): fd_item for inode %lu on device %02u:%02u, list %u, has invalid role %u in ACL!\n",
                                     fd_item_p->id, MAJOR(device_p->id), MINOR(device_p->id), list_no, subitem_p->entry.subj_id);
                            subitem_p = subitem_p->next;
                          }
                      }
                    #endif
                    else
                      subitem_p = subitem_p->next;
                  }

                /* if this item has neither subitems nor non-default mask, remove it */
                if(!fd_item_p->sublist_length && (fd_item_p->mask == RSBAC_ACL_DEFAULT_MASK))
                  {
                    no_member_count++;
                    if(correct)
                      {
                        if(rsbac_debug_ds)
                          printk(KERN_DEBUG
                                 "rsbac_check_acl(): fd_item for inode %lu on device %02u:%02u, list %u, has no set members, removing!\n",
                                 fd_item_p->id, MAJOR(device_p->id), MINOR(device_p->id), list_no);
                        fd_item_tmp_p = fd_item_p;
                        fd_item_p = fd_item_p->next;
                        remove_fd_item(device_p, fd_item_tmp_p->id);
                        head_p->dirty = TRUE;
                        continue;
                      }
                    else
                      {
                        if(rsbac_debug_ds)
                          printk(KERN_DEBUG
                                 "rsbac_check_acl(): fd_item for inode %lu on device %02u:%02u, list %u, has no set members!\n",
                                 fd_item_p->id, MAJOR(device_p->id), MINOR(device_p->id), list_no);
                      }
                  }
                tmp_count++;
                fd_item_p = fd_item_p->next;
              }
            if(tmp_count != head_p->count)
              {
                printk(KERN_WARNING
                       "rsbac_check_acl(): correcting count mismatch for device %02u:%02u, list %u - was %u, counted %lu!\n",
                       MAJOR(device_p->id), MINOR(device_p->id), list_no, head_p->count, tmp_count);
                head_p->count = tmp_count;
              }
            if(head_p->dirty)
              f_dirty++;
            rsbac_write_unlock(&head_p->lock, &flags);
            f_count += head_p->count;
          } /* end of for-fd-list-array */
        switch(correct)
          {
            case 2:  printk(KERN_INFO
                            "rsbac_check_acl(): Device %02u:%02u has %lu file/dir ACLs (%lu removed (%lu bad inodes, %lu dtimed inodes, %lu unlinked inodes, %lu had no members and default mask)) and %u dirty lists\n",
                            MAJOR(device_p->id), MINOR(device_p->id), f_count, b_count + r_count + u_count + no_member_count,
                            b_count, r_count, u_count, no_member_count, f_dirty);
                            break;
            case 1:  printk(KERN_INFO
                            "rsbac_check_acl(): Device %02u:%02u has %lu file/dir ACLs (%lu removed (%lu bad inodes, %lu dtimed inodes, %lu had no members and default mask), %lu unlinked inodes) and %u dirty lists\n",
                            MAJOR(device_p->id), MINOR(device_p->id), f_count, b_count + r_count + no_member_count,
                            b_count, r_count, no_member_count, u_count, f_dirty);
                            break;
            default: printk(KERN_INFO
                            "rsbac_check_acl(): Device %02u:%02u has %lu file/dir ACLs (%lu with bad inodes, %lu with dtimed inodes, %lu unlinked inodes, %lu without members and with default mask) and %u dirty lists\n",
                            MAJOR(device_p->id), MINOR(device_p->id), f_count,
                            b_count, r_count, u_count, no_member_count, f_dirty);
          }
        f_sum += f_count;
        dirty += f_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_acl(): Sum of %u Devices with %lu file/dir ACLs\n",
                 device_list_head.count, f_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;
    dev_item_p = dev_list_head.head;
    while(dev_item_p)
      {
        /* check for group existence */
        subitem_p = dev_item_p->sublist_head;
        while(subitem_p)
          {
            if(   (subitem_p->entry.subj_type == ACLS_GROUP)
               && subitem_p->entry.subj_id
               && !rsbac_acl_group_exist(subitem_p->entry.subj_id)
              )
              {
                if(correct)
                  {
                    rsbac_acl_group_id_t group;

                    /* remove sub item and complain */
                    if(rsbac_debug_ds)
                      printk(KERN_DEBUG
                             "rsbac_check_acl(): dev_item %c%02u:%02u, has invalid group %u in ACL -> removing entry!\n",
                             'B'+dev_item_p->id.type, MAJOR(dev_item_p->id.id), MINOR(dev_item_p->id.id), subitem_p->entry.subj_id);
                    group = subitem_p->entry.subj_id;
                    subitem_p = subitem_p->next;
                    remove_dev_subitem(dev_item_p, ACLS_GROUP, group);
                  }
                else
                  {
                    /* complain */
                    if(rsbac_debug_ds)
                      printk(KERN_DEBUG
                             "rsbac_check_acl(): dev_item %c%02u:%02u, has invalid group %u in ACL!\n",
                             'B'+dev_item_p->id.type,
                             MAJOR(dev_item_p->id.id), MINOR(dev_item_p->id.id), subitem_p->entry.subj_id);
                    subitem_p = subitem_p->next;
                  }
              }
            #if defined(CONFIG_RSBAC_RC) || defined(CONFIG_RSBAC_RC_MAINT)
            else
            if(   (subitem_p->entry.subj_type == ACLS_ROLE)
               && (subitem_p->entry.subj_id > RSBAC_RC_MAX_ROLE)
              )
              {
                if(correct)
                  {
                    rsbac_rc_role_id_t role;

                    /* remove sub item and complain */
                    if(rsbac_debug_ds)
                      printk(KERN_DEBUG
                             "rsbac_check_acl(): dev_item %c%02u:%02u, has invalid role %u in ACL -> removing entry!\n",
                             'B'+dev_item_p->id.type, MAJOR(dev_item_p->id.id), MINOR(dev_item_p->id.id), subitem_p->entry.subj_id);
                    role = subitem_p->entry.subj_id;
                    subitem_p = subitem_p->next;
                    remove_dev_subitem(dev_item_p, ACLS_ROLE, role);
                  }
                else
                  {
                    /* complain */
                    if(rsbac_debug_ds)
                      printk(KERN_DEBUG
                             "rsbac_check_acl(): dev_item %c%02u:%02u, has invalid role %u in ACL!\n",
                             'B'+dev_item_p->id.type,
                             MAJOR(dev_item_p->id.id), MINOR(dev_item_p->id.id), subitem_p->entry.subj_id);
                    subitem_p = subitem_p->next;
                  }
              }
            #endif
            else
              subitem_p = subitem_p->next;
          }
        /* if this item has neither subitems nor non-default mask, remove it */
        if(!dev_item_p->sublist_length && (dev_item_p->mask == RSBAC_ACL_DEFAULT_MASK))
          {
            no_member_count++;
            if(correct)
              {
                if(rsbac_debug_ds)
                  printk(KERN_DEBUG
                         "rsbac_check_acl(): dev_item %c%02u:%02u, has no set members, removing!\n",
                         'B'+dev_item_p->id.type,
                         MAJOR(dev_item_p->id.id), MINOR(dev_item_p->id.id));
                dev_item_tmp_p = dev_item_p;
                dev_item_p = dev_item_p->next;
                remove_dev_item(dev_item_tmp_p->id);
                dev_list_head.dirty = TRUE;
                continue;
              }
            else
              {
                if(rsbac_debug_ds)
                  printk(KERN_DEBUG
                         "rsbac_check_acl(): dev_item %c%02u:%02u, has no set members!\n",
                         'B'+dev_item_p->id.type,
                         MAJOR(dev_item_p->id.id), MINOR(dev_item_p->id.id));
                tmp_count++;
              }
          }
        else
          tmp_count++;
        dev_item_p = dev_item_p->next;
      }
    if(tmp_count != dev_list_head.count)
      {
        printk(KERN_WARNING
               "rsbac_check_acl(): correcting count mismatch for device 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);
    printk(KERN_INFO "rsbac_check_acl(): %u device items\n",
                 dev_list_head.count);

    /* SCD list */
    rsbac_write_lock(&scd_list_head.lock, &flags);
    tmp_count = 0;
    scd_item_p = scd_list_head.head;
    while(scd_item_p)
      {
        /* check for group existence */
        subitem_p = scd_item_p->sublist_head;
        while(subitem_p)
          {
            if(   (subitem_p->entry.subj_type == ACLS_GROUP)
               && subitem_p->entry.subj_id
               && !rsbac_acl_group_exist(subitem_p->entry.subj_id)
              )
              {
                if(correct)
                  {
                    rsbac_acl_group_id_t group;

                    /* remove sub item and complain */
                    if(rsbac_debug_ds)
                      printk(KERN_DEBUG
                             "rsbac_check_acl(): scd_item %u has invalid group %u in ACL -> removing entry!\n",
                             scd_item_p->id, subitem_p->entry.subj_id);
                    group = subitem_p->entry.subj_id;
                    subitem_p = subitem_p->next;
                    remove_scd_subitem(scd_item_p, ACLS_GROUP, group);
                  }
                else
                  {
                    /* complain */
                    if(rsbac_debug_ds)
                      printk(KERN_DEBUG
                             "rsbac_check_acl(): scd_item %u has invalid group %u in ACL!\n",
                             scd_item_p->id, subitem_p->entry.subj_id);
                    subitem_p = subitem_p->next;
                  }
              }
            #if defined(CONFIG_RSBAC_RC) || defined(CONFIG_RSBAC_RC_MAINT)
            else
            if(   (subitem_p->entry.subj_type == ACLS_ROLE)
               && (subitem_p->entry.subj_id > RSBAC_RC_MAX_ROLE)
              )
              {
                if(correct)
                  {
                    rsbac_rc_role_id_t role;

                    /* remove sub item and complain */
                    if(rsbac_debug_ds)
                      printk(KERN_DEBUG
                             "rsbac_check_acl(): scd_item %u has invalid role %u in ACL -> removing entry!\n",
                             scd_item_p->id, subitem_p->entry.subj_id);
                    role = subitem_p->entry.subj_id;
                    subitem_p = subitem_p->next;
                    remove_scd_subitem(scd_item_p, ACLS_ROLE, role);
                  }
                else
                  {
                    /* complain */
                    if(rsbac_debug_ds)
                      printk(KERN_DEBUG
                             "rsbac_check_acl(): scd_item %u has invalid role %u in ACL!\n",
                             scd_item_p->id, subitem_p->entry.subj_id);
                    subitem_p = subitem_p->next;
                  }
              }
            #endif
            else
              subitem_p = subitem_p->next;
          }
        /* if this item has neither subitems nor non-default mask, remove it */
        if(!scd_item_p->sublist_length && (scd_item_p->mask == RSBAC_ACL_DEFAULT_MASK))
          {
            no_member_count++;
            if(correct)
              {
                if(rsbac_debug_ds)
                  printk(KERN_DEBUG
                         "rsbac_check_acl(): scd_item %u, has no set members, removing!\n",
                         scd_item_p->id);
                scd_item_tmp_p = scd_item_p;
                scd_item_p = scd_item_p->next;
                remove_scd_item(scd_item_tmp_p->id);
                scd_list_head.dirty = TRUE;
                continue;
              }
            else
              {
                if(rsbac_debug_ds)
                  printk(KERN_DEBUG
                         "rsbac_check_acl(): scd_item %u has no set members!\n",
                         scd_item_p->id);
                tmp_count++;
              }
          }
        else
          tmp_count++;
        scd_item_p = scd_item_p->next;
      }
    if(tmp_count != scd_list_head.count)
      {
        printk(KERN_WARNING
               "rsbac_check_acl(): correcting count mismatch for SCD list - was %u, counted %lu!\n",
               scd_list_head.count, tmp_count);
        scd_list_head.count = tmp_count;
      }
    rsbac_write_unlock(&scd_list_head.lock, &flags);
    printk(KERN_INFO "rsbac_check_acl(): %u SCD items\n",
                 scd_list_head.count);


    printk(KERN_INFO
           "rsbac_check_acl(): Total of %lu registered ACLs, %u lists dirty\n",
           f_sum + scd_list_head.count + dev_list_head.count,
           dirty);
    return(0);
  };

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

#if defined(CONFIG_RSBAC_AUTO_WRITE)
int rsbac_write_acl(boolean need_lock)
  {
    struct rsbac_acl_device_list_item_t * device_p;
    int                                    err=0;
    u_int                                  count = 0;
    u_int                                  list_no;
    u_long                                 dflags;
  
    if (!rsbac_is_initialized())
      {
        printk(KERN_WARNING "rsbac_write_acl(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    /* Default ACLs */
    if(default_fd_dirty)
      {
        if(need_lock)
          lock_kernel();
        default_fd_dirty = FALSE;
        err=acl_write_default_fd_acl();
        if(need_lock)
          unlock_kernel();
        if(!err)
            count++;
        else
          {
            default_fd_dirty = TRUE;
            if(err != -RSBAC_ENOTWRITABLE)
              printk(KERN_WARNING
                     "rsbac_write_acl(): acl_write_default_fd_acl() returned error %i\n",
                     err);
          }
      }
    if(default_dev_dirty)
      {
        if(need_lock)
          lock_kernel();
        default_dev_dirty = FALSE;
        err=acl_write_default_dev_acl();
        if(need_lock)
          unlock_kernel();
        if(!err)
            count++;
        else
          {
            default_dev_dirty = TRUE;
            if(err != -RSBAC_ENOTWRITABLE)
              printk(KERN_WARNING
                     "rsbac_write_acl(): acl_write_default_dev_acl() returned error %i\n",
                     err);
          }
      }
    if(default_ipc_dirty)
      {
        if(need_lock)
          lock_kernel();
        default_ipc_dirty = FALSE;
        err=acl_write_default_ipc_acl();
        if(need_lock)
          unlock_kernel();
        if(!err)
            count++;
        else
          {
            default_ipc_dirty = TRUE;
            if(err != -RSBAC_ENOTWRITABLE)
              printk(KERN_WARNING
                     "rsbac_write_acl(): acl_write_default_ipc_acl() returned error %i\n",
                     err);
          }
      }
    if(default_scd_dirty)
      {
        if(need_lock)
          lock_kernel();
        default_scd_dirty = FALSE;
        err=acl_write_default_scd_acl();
        if(need_lock)
          unlock_kernel();
        if(!err)
            count++;
        else
          {
            default_scd_dirty = TRUE;
            if(err != -RSBAC_ENOTWRITABLE)
              printk(KERN_WARNING
                     "rsbac_write_acl(): acl_write_default_scd_acl() returned error %i\n",
                     err);
          }
      }
    if(default_u_dirty)
      {
        if(need_lock)
          lock_kernel();
        default_u_dirty = FALSE;
        err=acl_write_default_u_acl();
        if(need_lock)
          unlock_kernel();
        if(!err)
            count++;
        else
          {
            default_u_dirty = TRUE;
            if(err != -RSBAC_ENOTWRITABLE)
              printk(KERN_WARNING
                     "rsbac_write_acl(): acl_write_default_u_acl() returned error %i\n",
                     err);
          }
      }

    if(default_p_dirty)
      {
        if(need_lock)
          lock_kernel();
        default_p_dirty = FALSE;
        err=acl_write_default_p_acl();
        if(need_lock)
          unlock_kernel();
        if(!err)
            count++;
        else
          {
            default_p_dirty = TRUE;
            if(err != -RSBAC_ENOTWRITABLE)
              printk(KERN_WARNING
                     "rsbac_write_acl(): acl_write_default_p_acl() returned error %i\n",
                     err);
          }
      }


    /* wait for read access to device_list_head */
    rsbac_read_lock(&device_list_head.lock, &dflags);
    /* OK, go on */
/*    printk(KERN_INFO "rsbac_write_acl(): currently %u processes working on file/dir aci\n",
                     device_list_head.lock.lock); */
    device_p = device_list_head.head;
    while (device_p)
      {
        for(list_no = 0; list_no < RSBAC_ACL_NR_FD_LISTS; list_no++)
          {
            if(device_p->head_table[list_no].dirty)
              {
                if(need_lock)
                  lock_kernel();
                device_p->head_table[list_no].dirty = FALSE;
                err=acl_write_fd_list(list_no, &device_p->head_table[list_no], device_p);
                if(need_lock)
                  unlock_kernel();
                if(!err)
                  {
                    count++;
                  }
                else
                  {
                    device_p->head_table[list_no].dirty = TRUE;
                    if(err != -RSBAC_ENOTWRITABLE)
                      printk(KERN_WARNING
                             "rsbac_write_acl(): acl_write_fd_list() returned error %i for device %02u:%02u\n",
                             err,
                             MAJOR(device_p->id),
                             MINOR(device_p->id));
                  }
              }
          }
        device_p = device_p->next;
      };
    /* 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=acl_write_dev_list();
        if(need_lock)
          unlock_kernel();
        if(!err)
            count++;
        else
          {
            dev_list_head.dirty = TRUE;
            if(err != -RSBAC_ENOTWRITABLE)
              printk(KERN_WARNING
                     "rsbac_write_acl(): acl_write_dev_list() returned error %i\n",
                     err);
          }
      }

    if(scd_list_head.dirty)
      {
        if(need_lock)
          lock_kernel();
        scd_list_head.dirty = FALSE;
        err=acl_write_scd_list();
        if(need_lock)
          unlock_kernel();
        if(!err)
            count++;
        else
          {
            scd_list_head.dirty = TRUE;
            if(err != -RSBAC_ENOTWRITABLE)
              printk(KERN_WARNING
                     "rsbac_write_acl(): acl_write_scd_list() returned error %i\n",
                     err);
          }
      }

    if(group_list_head.dirty)
      {
        if(need_lock)
          lock_kernel();
        group_list_head.dirty = FALSE;
        err=acl_write_group_list();
        if(need_lock)
          unlock_kernel();
        if(!err)
            count++;
        else
          {
            group_list_head.dirty = TRUE;
            if(err != -RSBAC_ENOTWRITABLE)
              printk(KERN_WARNING
                     "rsbac_write_acl(): acl_write_group_list() returned error %i\n",
                     err);
          }
      }

    if(gm_list_head.dirty)
      {
        if(need_lock)
          lock_kernel();
        gm_list_head.dirty = FALSE;
        err=acl_write_gm_list();
        if(need_lock)
          unlock_kernel();
        if(!err)
            count++;
        else
          {
            gm_list_head.dirty = TRUE;
            if(err != -RSBAC_ENOTWRITABLE)
              printk(KERN_WARNING
                     "rsbac_write_acl(): acl_write_gm_list() returned error %i\n",
                     err);
          }
      }

    if(rsbac_debug_write)
      printk(KERN_DEBUG "rsbac_write_acl(): %u ACLs written\n",
             count);
    return(count);
  };
#endif

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

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

/* rsbac_acl_set_acl_entry
 * Set ACL entry for given target and subject to given rights. If entry does
 * not exist, it is created, thus cutting the inheritance from default/parent.
 */

int rsbac_acl_set_acl_entry        (enum   rsbac_target_t              target,
                                    union  rsbac_target_id_t           tid,
                                    enum   rsbac_acl_subject_type_t    subj_type,
                                           rsbac_acl_subject_id_t      subj_id,
                                           rsbac_acl_rights_vector_t   rights)
  {
      int err = 0;
      int list_no;
      struct rsbac_acl_device_list_item_t   * device_p;
      struct rsbac_acl_fd_list_item_t       * fd_item_p;
      struct rsbac_acl_dev_list_item_t      * dev_item_p;
      struct rsbac_acl_scd_list_item_t      * scd_item_p;
      struct rsbac_acl_sublist_item_t       * sub_item_p;
      struct rsbac_acl_entry_t                entry;
      u_long                                  flags,dflags;
    
      if (!rsbac_is_initialized())
        {
          printk(KERN_WARNING "rsbac_acl_set_acl_entry(): RSBAC not initialized\n");
          return(-RSBAC_ENOTINITIALIZED);
        }
      if (subj_type >= ACLS_NONE)
        return(-RSBAC_EINVALIDVALUE);
      if (in_interrupt())
        {
          printk(KERN_WARNING "rsbac_acl_set_acl_entry(): called from interrupt!\n");
        }
      switch (target)
        {
          case T_FILE:
          case T_DIR:
            
            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_set_acl_entry(): Setting file/dir ACL for device %02u:%02u, inode %u\n",
                         MAJOR(tid.file.device), MINOR(tid.file.device), tid.file.inode);
            /* default entry? */
            if(!tid.file.device && !tid.file.inode && !tid.file.dentry_p)
              {
                /* wait for write access to default_fd_list */
                rsbac_write_lock(&default_fd_lock, &flags);
                /* Set entry, add first, if necessary. */
                sub_item_p = lookup_fd_subitem(&default_fd_acl, subj_type, subj_id);
                if(sub_item_p)
                  {
                    sub_item_p->entry.rights = rights;
                  }
                else
                  {
                    entry.subj_type = subj_type;
                    entry.subj_id   = subj_id;
                    entry.rights   = rights;
                    sub_item_p = add_fd_subitem(&default_fd_acl, entry);
                    if(!sub_item_p)
                      err = -RSBAC_ECOULDNOTADDITEM;
                  }
                /* unprotect */
                rsbac_write_unlock(&default_fd_lock, &flags);
                /* if no error, mark dirty */
                if (!err)
                  {
                    default_fd_dirty = TRUE;
                  }
                break;
              }
            /* 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_acl_set_acl_entry(): passed device read lock\n"); */

            /* lookup device */
            device_p = acl_lookup_device(tid.file.device);
            if (!device_p)
              {
                printk(KERN_WARNING
                       "rsbac_acl_set_acl_entry(): Could not lookup device!\n");
                /* free read lock */
                rsbac_read_unlock(&device_list_head.lock, &dflags);
                return(-RSBAC_EINVALIDDEV);
              }
            /* protect this list */
            list_no = fd_hash(tid.file.inode);
            rsbac_write_lock(&device_p->head_table[list_no].lock, &flags);
/*         if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_set_acl_entry(): passed file/dir list head lock %i\n",
                      head_nr); */
            fd_item_p = do_lookup_fd_item(&device_p->head_table[list_no], tid.file.inode);
            if(!fd_item_p)
              { /* not found -> add */
                fd_item_p = do_add_fd_item(&device_p->head_table[list_no],
                                           tid.file.inode,
                                           RSBAC_ACL_DEFAULT_MASK);
                if(!fd_item_p)
                  { /* not added -> unprotect lists and return err */
                    rsbac_write_unlock(&device_p->head_table[list_no].lock, &flags);
                    rsbac_read_unlock(&device_list_head.lock, &dflags);
                    return(-RSBAC_ECOULDNOTADDITEM);
                  }
              }
            /* OK, fd item is there. Now set entry, add first, if necessary. */
            sub_item_p = lookup_fd_subitem(fd_item_p, subj_type, subj_id);
            if(sub_item_p)
              {
                sub_item_p->entry.rights = rights;
              }
            else
              {
                entry.subj_type = subj_type;
                entry.subj_id   = subj_id;
                entry.rights   = rights;
                sub_item_p = add_fd_subitem(fd_item_p, entry);
                if(!sub_item_p)
                  err = -RSBAC_ECOULDNOTADDITEM;
              }
            /* unprotect */
            rsbac_write_unlock(&device_p->head_table[list_no].lock, &flags);
            /* if no error, mark dirty */
            if (!err)
              {
                device_p->head_table[list_no].dirty = TRUE;
              }
            rsbac_read_unlock(&device_list_head.lock, &dflags);
            /* ready. */
            break;

          case T_DEV:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_set_acl_entry(): Setting device ACL for dev %c %02u:%02u\n",
                       'B'+tid.dev.type,
                       MAJOR(tid.dev.id),
                       MINOR(tid.dev.id));
*/
            /* default entry? */
            if((tid.dev.type == D_none) && !tid.dev.id)
              {
                /* wait for write access to default_dev_list */
                rsbac_write_lock(&default_dev_lock, &flags);
                /* Set entry, add first, if necessary. */
                sub_item_p = lookup_dev_subitem(&default_dev_acl, subj_type, subj_id);
                if(sub_item_p)
                  {
                    sub_item_p->entry.rights = rights;
                  }
                else
                  {
                    entry.subj_type = subj_type;
                    entry.subj_id   = subj_id;
                    entry.rights   = rights;
                    sub_item_p = add_dev_subitem(&default_dev_acl, entry);
                    if(!sub_item_p)
                      err = -RSBAC_ECOULDNOTADDITEM;
                  }
                /* unprotect */
                rsbac_write_unlock(&default_dev_lock, &flags);
                /* if no error, mark dirty */
                if (!err)
                  {
                    default_dev_dirty = TRUE;
                  }
                break;
              }
            /* protect the list */
            rsbac_write_lock(&dev_list_head.lock, &flags);
/*          if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_set_acl_entry(): passed device list head lock %i\n",
                      head_nr); */
            dev_item_p = lookup_dev_item(tid.dev);
            if(!dev_item_p)
              { /* not found -> add */
                dev_item_p = add_dev_item(tid.dev, RSBAC_ACL_DEFAULT_MASK);
                if(!dev_item_p)
                  { /* not added -> unprotect lists and return err */
                    rsbac_write_unlock(&dev_list_head.lock, &flags);
                    return(-RSBAC_ECOULDNOTADDITEM);
                  }
              }
            /* OK, dev_item is there. Now set entry, add first, if necessary. */
            sub_item_p = lookup_dev_subitem(dev_item_p, subj_type, subj_id);
            if(sub_item_p)
              {
                sub_item_p->entry.rights = rights;
              }
            else
              {
                entry.subj_type = subj_type;
                entry.subj_id   = subj_id;
                entry.rights   = rights;
                sub_item_p = add_dev_subitem(dev_item_p, entry);
                if(!sub_item_p)
                  err = -RSBAC_ECOULDNOTADDITEM;
              }
            dev_list_head.dirty = TRUE;
            /* unprotect */
            rsbac_write_unlock(&dev_list_head.lock, &flags);
            /* ready. */
            break;

          case T_IPC:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_set_acl_entry(): Setting IPC ACL for type %u\n",
                       tid.ipc.type);
*/
            /* default entry? */
            if(tid.ipc.type == I_none)
              {
                /* wait for write access to default_ipc_list */
                rsbac_write_lock(&default_ipc_lock, &flags);
                /* Set entry, add first, if necessary. */
                sub_item_p = lookup_ipc_subitem(&default_ipc_acl, subj_type, subj_id);
                if(sub_item_p)
                  {
                    sub_item_p->entry.rights = rights;
                  }
                else
                  {
                    entry.subj_type = subj_type;
                    entry.subj_id   = subj_id;
                    entry.rights   = rights;
                    sub_item_p = add_ipc_subitem(&default_ipc_acl, entry);
                    if(!sub_item_p)
                      err = -RSBAC_ECOULDNOTADDITEM;
                  }
                /* unprotect */
                rsbac_write_unlock(&default_ipc_lock, &flags);
                /* if no error, mark dirty */
                if (!err)
                  {
                    default_ipc_dirty = TRUE;
                  }
                break;
              }
            else
              {
                err = -RSBAC_EINVALIDTARGET;
              }
            /* ready. */
            break;

          case T_SCD:
/*            if (rsbac_debug_ds_acl)
                {
                  char tmp[80];
                  printk(KERN_DEBUG "rsbac_acl_set_acl_entry(): Setting SCD ACL for %s\n",
                         get_acl_scd_type_name(tmp, tid.scd));
                }
*/
            /* default entry? */
            if(tid.scd == AST_none)
              {
                /* wait for write access to default_scd_list */
                rsbac_write_lock(&default_scd_lock, &flags);
                /* Set entry, add first, if necessary. */
                sub_item_p = lookup_scd_subitem(&default_scd_acl, subj_type, subj_id);
                if(sub_item_p)
                  {
                    sub_item_p->entry.rights = rights;
                  }
                else
                  {
                    entry.subj_type = subj_type;
                    entry.subj_id   = subj_id;
                    entry.rights   = rights;
                    sub_item_p = add_scd_subitem(&default_scd_acl, entry);
                    if(!sub_item_p)
                      err = -RSBAC_ECOULDNOTADDITEM;
                  }
                /* unprotect */
                rsbac_write_unlock(&default_scd_lock, &flags);
                /* if no error, mark dirty */
                if (!err)
                  {
                    default_scd_dirty = TRUE;
                  }
                break;
              }
            /* protect the list */
            rsbac_write_lock(&scd_list_head.lock, &flags);
/*          if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_set_acl_entry(): passed SCD list head lock\n"); */
            scd_item_p = lookup_scd_item(tid.scd);
            if(!scd_item_p)
              { /* not found -> add */
                scd_item_p = add_scd_item(tid.scd, RSBAC_ACL_DEFAULT_MASK);
                if(!scd_item_p)
                  { /* not added -> unprotect lists and return err */
                    rsbac_write_unlock(&scd_list_head.lock, &flags);
                    return(-RSBAC_ECOULDNOTADDITEM);
                  }
              }
            /* OK, scd_item is there. Now set entry, add first, if necessary. */
            sub_item_p = lookup_scd_subitem(scd_item_p, subj_type, subj_id);
            if(sub_item_p)
              {
                sub_item_p->entry.rights = rights;
              }
            else
              {
                entry.subj_type = subj_type;
                entry.subj_id   = subj_id;
                entry.rights   = rights;
                sub_item_p = add_scd_subitem(scd_item_p, entry);
                if(!sub_item_p)
                  err = -RSBAC_ECOULDNOTADDITEM;
              }
            scd_list_head.dirty = TRUE;
            /* unprotect */
            rsbac_write_unlock(&scd_list_head.lock, &flags);
            /* ready. */
            break;

          case T_USER:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_set_acl_entry(): Setting user ACL for user %u\n",
                       tid.user);
*/
            /* default entry? */
            if(tid.user == RSBAC_NO_USER)
              {
                /* wait for write access to default_u_list */
                rsbac_write_lock(&default_u_lock, &flags);
                /* Set entry, add first, if necessary. */
                sub_item_p = lookup_u_subitem(&default_u_acl, subj_type, subj_id);
                if(sub_item_p)
                  {
                    sub_item_p->entry.rights = rights;
                  }
                else
                  {
                    entry.subj_type = subj_type;
                    entry.subj_id   = subj_id;
                    entry.rights   = rights;
                    sub_item_p = add_u_subitem(&default_u_acl, entry);
                    if(!sub_item_p)
                      err = -RSBAC_ECOULDNOTADDITEM;
                  }
                /* unprotect */
                rsbac_write_unlock(&default_u_lock, &flags);
                /* if no error, mark dirty */
                if (!err)
                  {
                    default_u_dirty = TRUE;
                  }
                break;
              }
            else
              {
                err = -RSBAC_EINVALIDTARGET;
              }
            /* ready. */
            break;

          case T_PROCESS:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_set_acl_entry(): Setting process ACL for pid %u\n",
                       tid.process);
*/
            /* default entry? */
            if(!tid.process)
              {
                /* wait for write access to default_p_list */
                rsbac_write_lock(&default_p_lock, &flags);
                /* Set entry, add first, if necessary. */
                sub_item_p = lookup_p_subitem(&default_p_acl, subj_type, subj_id);
                if(sub_item_p)
                  {
                    sub_item_p->entry.rights = rights;
                  }
                else
                  {
                    entry.subj_type = subj_type;
                    entry.subj_id   = subj_id;
                    entry.rights   = rights;
                    sub_item_p = add_p_subitem(&default_p_acl, entry);
                    if(!sub_item_p)
                      err = -RSBAC_ECOULDNOTADDITEM;
                  }
                /* unprotect */
                rsbac_write_unlock(&default_p_lock, &flags);
                /* if no error, mark dirty */
                if (!err)
                  {
                    default_p_dirty = TRUE;
                  }
                break;
              }
            else
              {
                err = -RSBAC_EINVALIDTARGET;
              }
            /* ready. */
            break;

          default:
            err = -RSBAC_EINVALIDTARGET;
        }
      return(err);
  }

/* rsbac_acl_remove_acl_entry
 * Remove ACL entry for given target and subject. This reactivates the
 * inheritance from default/parent.
 */

int rsbac_acl_remove_acl_entry     (enum   rsbac_target_t              target,
                                    union  rsbac_target_id_t           tid,
                                    enum   rsbac_acl_subject_type_t    subj_type,
                                           rsbac_acl_subject_id_t      subj_id)
  {
      int err = 0;
      int list_no;
      struct rsbac_acl_device_list_item_t   * device_p;
      struct rsbac_acl_fd_list_item_t       * fd_item_p;
      struct rsbac_acl_dev_list_item_t      * dev_item_p;
      struct rsbac_acl_scd_list_item_t      * scd_item_p;
      u_long                                  flags,dflags;
    
      if (!rsbac_is_initialized())
        {
          printk(KERN_WARNING "rsbac_acl_remove_acl_entry(): RSBAC not initialized\n");
          return(-RSBAC_ENOTINITIALIZED);
        }
      if (subj_type >= ACLS_NONE)
        return(-RSBAC_EINVALIDVALUE);
      if (in_interrupt())
        {
          printk(KERN_WARNING "rsbac_acl_remove_acl_entry(): called from interrupt!\n");
        }
      switch (target)
        {
          case T_FILE:
          case T_DIR:
            
            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_remove_acl_entry(): Removing file/dir ACL for device %02u:%02u, inode %u\n",
                       MAJOR(tid.file.device), MINOR(tid.file.device), tid.file.inode);
            /* default entry? */
            if(!tid.file.device && !tid.file.inode && !tid.file.dentry_p)
              {
                /* wait for write access to default_fd_acl */
                rsbac_write_lock(&default_fd_lock, &flags);
                /* Remove entry */
                remove_fd_subitem(&default_fd_acl, subj_type, subj_id);
                /* unprotect */
                rsbac_write_unlock(&default_fd_lock, &flags);
                /* mark dirty */
                default_fd_dirty = TRUE;
                break;
              }
            /* 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_acl_remove_acl_entry(): passed device read lock\n"); */

            /* lookup device */
            device_p = acl_lookup_device(tid.file.device);
            if (!device_p)
              {
                printk(KERN_WARNING
                       "rsbac_acl_remove_acl_entry(): Could not lookup device!\n");
                /* free read lock */
                rsbac_read_unlock(&device_list_head.lock, &dflags);
                return(-RSBAC_EINVALIDDEV);
              }
            /* protect this list */
            list_no = fd_hash(tid.file.inode);
            rsbac_write_lock(&device_p->head_table[list_no].lock, &flags);
/*         if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_remove_acl_entry(): passed file/dir list head lock %i\n",
                      head_nr); */
            fd_item_p = do_lookup_fd_item(&device_p->head_table[list_no], tid.file.inode);
            if(fd_item_p)
              {
                /* OK, fd item is there. Now remove entry. */
                remove_fd_subitem(fd_item_p, subj_type, subj_id);
                /* mark dirty */
                device_p->head_table[list_no].dirty = TRUE;
              }
            /* unprotect */
            rsbac_write_unlock(&device_p->head_table[list_no].lock, &flags);
            rsbac_read_unlock(&device_list_head.lock, &dflags);
            /* ready. */
            break;

          case T_DEV:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_remove_from_acl_entry(): Removing device ACL entry for dev %c %02u:%02u\n",
                       'B'+tid.dev.type,
                       MAJOR(tid.dev.id),
                       MINOR(tid.dev.id));
*/
            /* default entry? */
            if((tid.dev.type == D_none) && !tid.dev.id)
              {
                /* wait for write access to default_dev_acl */
                rsbac_write_lock(&default_dev_lock, &flags);
                /* Remove entry */
                remove_dev_subitem(&default_dev_acl, subj_type, subj_id);
                /* unprotect */
                rsbac_write_unlock(&default_dev_lock, &flags);
                /* mark dirty */
                default_dev_dirty = TRUE;
                break;
              }
            /* protect the list */
            rsbac_write_lock(&dev_list_head.lock, &flags);
/*          if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_remove_acl_entry(): passed device list head lock\n"); */
            dev_item_p = lookup_dev_item(tid.dev);
            if(dev_item_p)
              {
                /* OK, dev_item is there. Now remove entry. */
                remove_dev_subitem(dev_item_p, subj_type, subj_id);
                dev_list_head.dirty = TRUE;
              }
            /* unprotect */
            rsbac_write_unlock(&dev_list_head.lock, &flags);
            /* ready. */
            break;

          case T_IPC:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_remove_acl_entry(): Setting IPC ACL for type %u\n",
                       tid.ipc.type);
*/
            /* default entry? */
            if(tid.ipc.type == I_none)
              {
                /* wait for write access to default_ipc_acl */
                rsbac_write_lock(&default_ipc_lock, &flags);
                /* Remove entry */
                remove_ipc_subitem(&default_ipc_acl, subj_type, subj_id);
                /* unprotect */
                rsbac_write_unlock(&default_ipc_lock, &flags);
                /* mark dirty */
                default_ipc_dirty = TRUE;
                break;
              }
            else
              err = -RSBAC_EINVALIDTARGET;
            /* ready. */
            break;

          case T_SCD:
/*            if (rsbac_debug_ds_acl)
                {
                  char tmp[80];

                  printk(KERN_DEBUG "rsbac_acl_remove_from_acl_entry(): Removing SCD ACL entry for %s\n",
                         get_acl_scd_type_n(tmp, tid.scd));
                }
*/
            /* default entry? */
            if(tid.scd == AST_none)
              {
                /* wait for write access to default_scd_acl */
                rsbac_write_lock(&default_scd_lock, &flags);
                /* Remove entry */
                remove_scd_subitem(&default_scd_acl, subj_type, subj_id);
                /* unprotect */
                rsbac_write_unlock(&default_scd_lock, &flags);
                /* mark dirty */
                default_scd_dirty = TRUE;
                break;
              }
            /* protect the list */
            rsbac_write_lock(&scd_list_head.lock, &flags);
/*          if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_remove_acl_entry(): passed SCD list head lock\n"); */
            scd_item_p = lookup_scd_item(tid.scd);
            if(scd_item_p)
              {
                /* OK, scd_item is there. Now remove entry. */
                remove_scd_subitem(scd_item_p, subj_type, subj_id);
                scd_list_head.dirty = TRUE;
              }
            /* unprotect */
            rsbac_write_unlock(&scd_list_head.lock, &flags);
            /* ready. */
            break;

          case T_USER:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_remove_acl_entry(): Setting user ACL for user %u\n",
                       tid.user);
*/
            /* default entry? */
            if(tid.user == RSBAC_NO_USER)
              {
                /* wait for write access to default_u_acl */
                rsbac_write_lock(&default_u_lock, &flags);
                /* Remove entry */
                remove_u_subitem(&default_u_acl, subj_type, subj_id);
                /* unprotect */
                rsbac_write_unlock(&default_u_lock, &flags);
                /* mark dirty */
                default_u_dirty = TRUE;
                break;
              }
            else
              err = -RSBAC_EINVALIDTARGET;
            /* ready. */
            break;

          case T_PROCESS:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_remove_acl_entry(): Setting process ACL for pid %u\n",
                       tid.process);
*/
            /* default entry? */
            if(!tid.process)
              {
                /* wait for write access to default_p_acl */
                rsbac_write_lock(&default_p_lock, &flags);
                /* Remove entry */
                remove_p_subitem(&default_p_acl, subj_type, subj_id);
                /* unprotect */
                rsbac_write_unlock(&default_p_lock, &flags);
                /* mark dirty */
                default_p_dirty = TRUE;
                break;
              }
            else
              err = -RSBAC_EINVALIDTARGET;
            /* ready. */
            break;

          default:
            err = -RSBAC_EINVALIDTARGET;
        }
      return(err);
  }

/* rsbac_acl_remove_acl
 * Remove ACL for given target. For cleanup on delete.
 */

int rsbac_acl_remove_acl     (enum   rsbac_target_t              target,
                              union  rsbac_target_id_t           tid)
  {
      int err = 0;
      int list_no;
      struct rsbac_acl_device_list_item_t   * device_p;
      u_long                                  flags,dflags;
    
      if (!rsbac_is_initialized())
        {
          printk(KERN_WARNING "rsbac_acl_remove_acl(): RSBAC not initialized\n");
          return(-RSBAC_ENOTINITIALIZED);
        }
      if (in_interrupt())
        {
          printk(KERN_WARNING "rsbac_acl_remove_acl(): called from interrupt!\n");
        }
      switch (target)
        {
          case T_FILE:
          case T_DIR:
            
            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_remove_acl(): Removing file/dir ACL for device %02u:%02u, inode %u\n",
                       MAJOR(tid.file.device), MINOR(tid.file.device), tid.file.inode);
            /* default entry? */
            if(!tid.file.device && !tid.file.inode && !tid.file.dentry_p)
              {
                return -RSBAC_EINVALIDTARGET;
              }
            /* 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_acl_remove_acl(): passed device read lock\n"); */

            /* lookup device */
            device_p = acl_lookup_device(tid.file.device);
            if (!device_p)
              {
                printk(KERN_WARNING
                       "rsbac_acl_remove_acl(): Could not lookup device!\n");
                /* free read lock */
                rsbac_read_unlock(&device_list_head.lock, &dflags);
                return(-RSBAC_EINVALIDDEV);
              }
            /* protect this list */
            list_no = fd_hash(tid.file.inode);
            rsbac_write_lock(&device_p->head_table[list_no].lock, &flags);
/*         if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_remove_acl(): passed file/dir list head lock %i\n",
                      head_nr); */
            if(do_lookup_fd_item(&device_p->head_table[list_no], tid.file.inode))
              {
                /* OK, fd item is there. Now remove it. */
                do_remove_fd_item(&device_p->head_table[list_no], tid.file.inode);
                /* unprotect */
                rsbac_write_unlock(&device_p->head_table[list_no].lock, &flags);
                /* mark dirty */
                device_p->head_table[list_no].dirty = TRUE;
              }
            else
              {
                /* unprotect */
                rsbac_write_unlock(&device_p->head_table[list_no].lock, &flags);
              }
            rsbac_read_unlock(&device_list_head.lock, &dflags);
            /* ready. */
            break;

          case T_DEV:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_remove_acl(): Removing device ACL for dev %c %02u:%02u\n",
                       'B'+tid.dev.type,
                       MAJOR(tid.dev.id),
                       MINOR(tid.dev.id));
*/
            /* default entry? */
            if((tid.dev.type == D_none) && !tid.dev.id)
              {
                return -RSBAC_EINVALIDTARGET;
              }
            /* protect the list */
            rsbac_write_lock(&dev_list_head.lock, &flags);
/*          if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_remove_acl(): passed device list head lock\n"); */
            if(lookup_dev_item(tid.dev))
              {
                remove_dev_item(tid.dev);
                dev_list_head.dirty = TRUE;
              }
            /* unprotect */
            rsbac_write_unlock(&dev_list_head.lock, &flags);
            /* ready. */
            break;

          case T_SCD:
/*            if (rsbac_debug_ds_acl)
                {
                  char tmp[80];

                  printk(KERN_DEBUG "rsbac_acl_remove_acl(): Removing SCD ACL for %s\n",
                         get_acl_scd_type_name(tmp, tid.scd));
                }
*/
            /* default entry? */
            if(tid.scd == AST_none)
              {
                return -RSBAC_EINVALIDTARGET;
              }
            /* protect the list */
            rsbac_write_lock(&scd_list_head.lock, &flags);
/*          if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_remove_acl(): passed SCD list head lock\n"); */
            if(lookup_scd_item(tid.scd))
              {
                remove_scd_item(tid.scd);
                scd_list_head.dirty = TRUE;
              }
            /* unprotect */
            rsbac_write_unlock(&scd_list_head.lock, &flags);
            /* ready. */
            break;

          default:
            err = -RSBAC_EINVALIDTARGET;
        }
      return(err);
  }

/* rsbac_acl_add_to_acl_entry
 * Add given rights to ACL entry for given target and subject. If entry does
 * not exist, behaviour is exactly like rsbac_acl_set_acl_entry.
 */

int rsbac_acl_add_to_acl_entry     (enum   rsbac_target_t              target,
                                    union  rsbac_target_id_t           tid,
                                    enum   rsbac_acl_subject_type_t    subj_type,
                                           rsbac_acl_subject_id_t      subj_id,
                                           rsbac_acl_rights_vector_t   rights)
  {
      int err = 0;
      int list_no;
      struct rsbac_acl_device_list_item_t   * device_p;
      struct rsbac_acl_fd_list_item_t       * fd_item_p;
      struct rsbac_acl_dev_list_item_t      * dev_item_p;
      struct rsbac_acl_scd_list_item_t      * scd_item_p;
      struct rsbac_acl_sublist_item_t       * sub_item_p;
      struct rsbac_acl_entry_t                entry;
      u_long                                  flags,dflags;
    
      if (!rsbac_is_initialized())
        {
          printk(KERN_WARNING "rsbac_acl_add_to_acl_entry(): RSBAC not initialized\n");
          return(-RSBAC_ENOTINITIALIZED);
        }
      if (subj_type >= ACLS_NONE)
        return(-RSBAC_EINVALIDVALUE);
      if (in_interrupt())
        {
          printk(KERN_WARNING "rsbac_acl_add_to_acl_entry(): called from interrupt!\n");
        }
      switch (target)
        {
          case T_FILE:
          case T_DIR:
            
            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_add_to_acl_entry(): Adding to file/dir ACL for device %02u:%02u, inode %u\n",
                       MAJOR(tid.file.device), MINOR(tid.file.device), tid.file.inode);
            /* default entry? */
            if(!tid.file.device && !tid.file.inode && !tid.file.dentry_p)
              {
                /* wait for write access to default_fd_acl */
                rsbac_write_lock(&default_fd_lock, &flags);
                /* Set entry, add first, if necessary. */
                sub_item_p = lookup_fd_subitem(&default_fd_acl, subj_type, subj_id);
                if(sub_item_p)
                  {
                    sub_item_p->entry.rights |= rights;
                  }
                else
                  {
                    entry.subj_type = subj_type;
                    entry.subj_id   = subj_id;
                    entry.rights   = rights;
                    sub_item_p = add_fd_subitem(&default_fd_acl, entry);
                    if(!sub_item_p)
                      err = -RSBAC_ECOULDNOTADDITEM;
                  }
                /* unprotect */
                rsbac_write_unlock(&default_fd_lock, &flags);
                /* if no error, mark dirty */
                if (!err)
                  {
                    default_fd_dirty = TRUE;
                  }
                break;
              }
            /* 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_acl_add_to_acl_entry(): passed device read lock\n"); */

            /* lookup device */
            device_p = acl_lookup_device(tid.file.device);
            if (!device_p)
              {
                printk(KERN_WARNING
                       "rsbac_acl_add_to_acl_entry(): Could not lookup device!\n");
                /* free read lock */
                rsbac_read_unlock(&device_list_head.lock, &dflags);
                return(-RSBAC_EINVALIDDEV);
              }
            /* protect this list */
            list_no = fd_hash(tid.file.inode);
            rsbac_write_lock(&device_p->head_table[list_no].lock, &flags);
/*         if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_add_to_acl_entry(): passed file/dir list head lock %i\n",
                      head_nr); */
            fd_item_p = do_lookup_fd_item(&device_p->head_table[list_no], tid.file.inode);
            if(!fd_item_p)
              { /* not found -> add */
                fd_item_p = do_add_fd_item(&device_p->head_table[list_no],
                                           tid.file.inode,
                                           RSBAC_ACL_DEFAULT_MASK);
                if(!fd_item_p)
                  { /* not added -> unprotect lists and return err */
                    rsbac_write_unlock(&device_p->head_table[list_no].lock, &flags);
                    rsbac_read_unlock(&device_list_head.lock, &dflags);
                    return(-RSBAC_ECOULDNOTADDITEM);
                  }
              }
            /* OK, fd item is there. Now set entry, add first, if necessary. */
            sub_item_p = lookup_fd_subitem(fd_item_p, subj_type, subj_id);
            if(!sub_item_p)
              {
                entry.subj_type = subj_type;
                entry.subj_id   = subj_id;
                entry.rights   = rights;
                sub_item_p = add_fd_subitem(fd_item_p, entry);
                if(!sub_item_p)
                  err = -RSBAC_ECOULDNOTADDITEM;
              }
            else
              sub_item_p->entry.rights |= rights;
            /* unprotect */
            rsbac_write_unlock(&device_p->head_table[list_no].lock, &flags);
            /* if no error, mark dirty */
            if (!err)
              {
                device_p->head_table[list_no].dirty = TRUE;
              }
            rsbac_read_unlock(&device_list_head.lock, &dflags);
            /* ready. */
            break;

          case T_DEV:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_add_to_acl_entry(): Adding to device ACL entry for dev %c %02u:%02u\n",
                       'B'+tid.dev.type,
                       MAJOR(tid.dev.id),
                       MINOR(tid.dev.id));
*/
            /* default entry? */
            if((tid.dev.type == D_none) && !tid.dev.id)
              {
                /* wait for write access to default_dev_acl */
                rsbac_write_lock(&default_dev_lock, &flags);
                /* Set entry, add first, if necessary. */
                sub_item_p = lookup_dev_subitem(&default_dev_acl, subj_type, subj_id);
                if(sub_item_p)
                  {
                    sub_item_p->entry.rights |= rights;
                  }
                else
                  {
                    entry.subj_type = subj_type;
                    entry.subj_id   = subj_id;
                    entry.rights   = rights;
                    sub_item_p = add_dev_subitem(&default_dev_acl, entry);
                    if(!sub_item_p)
                      err = -RSBAC_ECOULDNOTADDITEM;
                  }
                /* unprotect */
                rsbac_write_unlock(&default_dev_lock, &flags);
                /* if no error, mark dirty */
                if (!err)
                  {
                    default_dev_dirty = TRUE;
                  }
                break;
              }
            /* protect the list */
            rsbac_write_lock(&dev_list_head.lock, &flags);
/*         if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_add_to_acl_entry(): passed device list head lock\n"); */
            dev_item_p = lookup_dev_item(tid.dev);
            if(!dev_item_p)
              { /* not found -> add */
                dev_item_p = add_dev_item(tid.dev, RSBAC_ACL_DEFAULT_MASK);
                if(!dev_item_p)
                  { /* not added -> unprotect lists and return err */
                    rsbac_write_unlock(&dev_list_head.lock, &flags);
                    return(-RSBAC_ECOULDNOTADDITEM);
                  }
              }
            /* OK, dev_item is there. Now set entry, add first, if necessary. */
            sub_item_p = lookup_dev_subitem(dev_item_p, subj_type, subj_id);
            if(!sub_item_p)
              {
                entry.subj_type = subj_type;
                entry.subj_id   = subj_id;
                entry.rights   = rights;
                sub_item_p = add_dev_subitem(dev_item_p, entry);
                if(!sub_item_p)
                  err = -RSBAC_ECOULDNOTADDITEM;
              }
            else
              sub_item_p->entry.rights |= rights;
            dev_list_head.dirty = TRUE;
            /* unprotect */
            rsbac_write_unlock(&dev_list_head.lock, &flags);
            /* ready. */
            break;

          case T_IPC:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_add_to_acl_entry(): Adding to IPC ACL for type %u\n",
                       tid.ipc.type);
*/
            /* default entry? */
            if(tid.ipc.type == I_none)
              {
                /* wait for write access to default_ipc_acl */
                rsbac_write_lock(&default_ipc_lock, &flags);
                /* Set entry, add first, if necessary. */
                sub_item_p = lookup_ipc_subitem(&default_ipc_acl, subj_type, subj_id);
                if(sub_item_p)
                  {
                    sub_item_p->entry.rights |= rights;
                  }
                else
                  {
                    entry.subj_type = subj_type;
                    entry.subj_id   = subj_id;
                    entry.rights   = rights;
                    sub_item_p = add_ipc_subitem(&default_ipc_acl, entry);
                    if(!sub_item_p)
                      err = -RSBAC_ECOULDNOTADDITEM;
                  }
                /* unprotect */
                rsbac_write_unlock(&default_ipc_lock, &flags);
                /* if no error, mark dirty */
                if (!err)
                  {
                    default_ipc_dirty = TRUE;
                  }
                break;
              }
            else
              err = -RSBAC_EINVALIDTARGET;
            /* ready. */
            break;

          case T_SCD:
/*            if (rsbac_debug_ds_acl)
                {
                  char tmp[80];

                  printk(KERN_DEBUG "rsbac_acl_add_to_acl_entry(): Adding to SCD ACL entry for %s\n",
                         get_acl_scd_type_name(tmp, tid.dev.id));
                }
*/
            /* default entry? */
            if(tid.scd == AST_none)
              {
                /* wait for write access to default_scd_acl */
                rsbac_write_lock(&default_scd_lock, &flags);
                /* Set entry, add first, if necessary. */
                sub_item_p = lookup_scd_subitem(&default_scd_acl, subj_type, subj_id);
                if(sub_item_p)
                  {
                    sub_item_p->entry.rights |= rights;
                  }
                else
                  {
                    entry.subj_type = subj_type;
                    entry.subj_id   = subj_id;
                    entry.rights   = rights;
                    sub_item_p = add_scd_subitem(&default_scd_acl, entry);
                    if(!sub_item_p)
                      err = -RSBAC_ECOULDNOTADDITEM;
                  }
                /* unprotect */
                rsbac_write_unlock(&default_scd_lock, &flags);
                /* if no error, mark dirty */
                if (!err)
                  {
                    default_scd_dirty = TRUE;
                  }
                break;
              }
            /* protect the list */
            rsbac_write_lock(&scd_list_head.lock, &flags);
/*         if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_add_to_acl_entry(): passed SCD list head lock\n"); */
            scd_item_p = lookup_scd_item(tid.scd);
            if(!scd_item_p)
              { /* not found -> add */
                scd_item_p = add_scd_item(tid.scd, RSBAC_ACL_DEFAULT_MASK);
                if(!scd_item_p)
                  { /* not added -> unprotect lists and return err */
                    rsbac_write_unlock(&scd_list_head.lock, &flags);
                    return(-RSBAC_ECOULDNOTADDITEM);
                  }
              }
            /* OK, scd_item is there. Now set entry, add first, if necessary. */
            sub_item_p = lookup_scd_subitem(scd_item_p, subj_type, subj_id);
            if(!sub_item_p)
              {
                entry.subj_type = subj_type;
                entry.subj_id   = subj_id;
                entry.rights   = rights;
                sub_item_p = add_scd_subitem(scd_item_p, entry);
                if(!sub_item_p)
                  err = -RSBAC_ECOULDNOTADDITEM;
              }
            else
              sub_item_p->entry.rights |= rights;
            scd_list_head.dirty = TRUE;
            /* unprotect */
            rsbac_write_unlock(&scd_list_head.lock, &flags);
            /* ready. */
            break;

          case T_USER:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_add_to_acl_entry(): Adding to user ACL for user %u\n",
                       tid.user);
*/
            /* default entry? */
            if(tid.user == RSBAC_NO_USER)
              {
                /* wait for write access to default_u_acl */
                rsbac_write_lock(&default_u_lock, &flags);
                /* Set entry, add first, if necessary. */
                sub_item_p = lookup_u_subitem(&default_u_acl, subj_type, subj_id);
                if(sub_item_p)
                  {
                    sub_item_p->entry.rights |= rights;
                  }
                else
                  {
                    entry.subj_type = subj_type;
                    entry.subj_id   = subj_id;
                    entry.rights   = rights;
                    sub_item_p = add_u_subitem(&default_u_acl, entry);
                    if(!sub_item_p)
                      err = -RSBAC_ECOULDNOTADDITEM;
                  }
                /* unprotect */
                rsbac_write_unlock(&default_u_lock, &flags);
                /* if no error, mark dirty */
                if (!err)
                  {
                    default_u_dirty = TRUE;
                  }
                break;
              }
            else
              err = -RSBAC_EINVALIDTARGET;
            /* ready. */
            break;

          case T_PROCESS:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_add_to_acl_entry(): Adding to process ACL for pid %u\n",
                       tid.process);
*/
            /* default entry? */
            if(!tid.process)
              {
                /* wait for write access to default_p_acl */
                rsbac_write_lock(&default_p_lock, &flags);
                /* Set entry, add first, if necessary. */
                sub_item_p = lookup_p_subitem(&default_p_acl, subj_type, subj_id);
                if(sub_item_p)
                  {
                    sub_item_p->entry.rights |= rights;
                  }
                else
                  {
                    entry.subj_type = subj_type;
                    entry.subj_id   = subj_id;
                    entry.rights   = rights;
                    sub_item_p = add_p_subitem(&default_p_acl, entry);
                    if(!sub_item_p)
                      err = -RSBAC_ECOULDNOTADDITEM;
                  }
                /* unprotect */
                rsbac_write_unlock(&default_p_lock, &flags);
                /* if no error, mark dirty */
                if (!err)
                  {
                    default_p_dirty = TRUE;
                  }
                break;
              }
            else
              err = -RSBAC_EINVALIDTARGET;
            /* ready. */
            break;

          default:
            err = -RSBAC_EINVALIDTARGET;
        }
      return(err);
  }

/* rsbac_acl_remove_from_acl_entry
 * Remove given rights from ACL entry for given target and subject. If entry does
 * not exist, nothing happens.
 * This function does NOT remove the ACL entry, so removing all rights results in
 * NO rights for this subject/target combination!
 */

int rsbac_acl_remove_from_acl_entry(enum   rsbac_target_t              target,
                                    union  rsbac_target_id_t           tid,
                                    enum   rsbac_acl_subject_type_t    subj_type,
                                           rsbac_acl_subject_id_t      subj_id,
                                           rsbac_acl_rights_vector_t   rights)
  {
      int err = 0;
      int list_no;
      struct rsbac_acl_device_list_item_t   * device_p;
      struct rsbac_acl_fd_list_item_t       * fd_item_p;
      struct rsbac_acl_dev_list_item_t      * dev_item_p;
      struct rsbac_acl_scd_list_item_t      * scd_item_p;
      struct rsbac_acl_sublist_item_t       * sub_item_p;
      u_long                                  flags,dflags;
    
      if (!rsbac_is_initialized())
        {
          printk(KERN_WARNING "rsbac_acl_remove_from_acl_entry(): RSBAC not initialized\n");
          return(-RSBAC_ENOTINITIALIZED);
        }
      if (subj_type >= ACLS_NONE)
        return(-RSBAC_EINVALIDVALUE);
      if (in_interrupt())
        {
          printk(KERN_WARNING "rsbac_acl_remove_from_acl_entry(): called from interrupt!\n");
        }
      switch (target)
        {
          case T_FILE:
          case T_DIR:
            
            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_remove_from_acl_entry(): Removing from file/dir ACL for device %02u:%02u, inode %u\n",
                       MAJOR(tid.file.device), MINOR(tid.file.device), tid.file.inode);
            /* default entry? */
            if(!tid.file.device && !tid.file.inode && !tid.file.dentry_p)
              {
                /* wait for write access to default_fd_acl */
                rsbac_write_lock(&default_fd_lock, &flags);
                /* revoke rights, if there. */
                sub_item_p = lookup_fd_subitem(&default_fd_acl, subj_type, subj_id);
                if(sub_item_p)
                  {
                    sub_item_p->entry.rights &= ~rights;
                  }
                /* unprotect */
                rsbac_write_unlock(&default_fd_lock, &flags);
                /* if no error, mark dirty */
                default_fd_dirty = TRUE;
                break;
              }
            /* 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_acl_remove_from_acl_entry(): passed device read lock\n"); */

            /* lookup device */
            device_p = acl_lookup_device(tid.file.device);
            if (!device_p)
              {
                printk(KERN_WARNING
                       "rsbac_acl_remove_from_acl_entry(): Could not lookup device!\n");
                /* free read lock */
                rsbac_read_unlock(&device_list_head.lock, &dflags);
                return(-RSBAC_EINVALIDDEV);
              }
            /* protect this list */
            list_no = fd_hash(tid.file.inode);
            rsbac_write_lock(&device_p->head_table[list_no].lock, &flags);
/*          if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_remove_from_acl_entry(): passed file/dir list head lock %i\n",
                      head_nr); */
            fd_item_p = do_lookup_fd_item(&device_p->head_table[list_no], tid.file.inode);
            if(fd_item_p)
              {
                /* OK, fd item is there. Look for ACL entry. */
                sub_item_p = lookup_fd_subitem(fd_item_p, subj_type, subj_id);
                if(sub_item_p)
                  { /* found ACL entry -> revoke rights */
                    sub_item_p->entry.rights &= ~rights;
                  }
              }
            /* unprotect */
            rsbac_write_unlock(&device_p->head_table[list_no].lock, &flags);
            /* if no error, mark dirty */
            if (!err)
              {
                device_p->head_table[list_no].dirty = TRUE;
              }
            rsbac_read_unlock(&device_list_head.lock, &dflags);
            /* ready. */
            break;

          case T_DEV:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_remove_from_acl_entry(): Removing from device ACL entry for dev %c %02u:%02u\n",
                       'B'+tid.dev.type,
                       MAJOR(tid.dev.id),
                       MINOR(tid.dev.id));
*/
            /* default entry? */
            if((tid.dev.type == D_none) && !tid.dev.id)
              {
                /* wait for write access to default_dev_acl */
                rsbac_write_lock(&default_dev_lock, &flags);
                /* revoke rights, if there. */
                sub_item_p = lookup_dev_subitem(&default_dev_acl, subj_type, subj_id);
                if(sub_item_p)
                  {
                    sub_item_p->entry.rights &= ~rights;
                  }
                /* unprotect */
                rsbac_write_unlock(&default_dev_lock, &flags);
                /* if no error, mark dirty */
                default_dev_dirty = TRUE;
                break;
              }
            /* protect the list */
            rsbac_write_lock(&dev_list_head.lock, &flags);
/*         if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_remove_from_acl_entry(): passed device list head lock\n"); */
            dev_item_p = lookup_dev_item(tid.dev);
            if(dev_item_p)
              {
                /* OK, dev_item is there. Now lookup entry. */
                sub_item_p = lookup_dev_subitem(dev_item_p, subj_type, subj_id);
                if(sub_item_p)
                  { /* found -> revoke rights */
                    sub_item_p->entry.rights &= ~rights;
                    dev_list_head.dirty = TRUE;
                  }
              }
            /* unprotect */
            rsbac_write_unlock(&dev_list_head.lock, &flags);
            /* ready. */
            break;

          case T_IPC:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_remove_from_acl_entry(): Setting IPC ACL for type %u\n",
                       tid.ipc.type);
*/
            /* default entry? */
            if(tid.ipc.type == I_none)
              {
                /* wait for write access to default_ipc_acl */
                rsbac_write_lock(&default_ipc_lock, &flags);
                /* revoke rights, if there. */
                sub_item_p = lookup_ipc_subitem(&default_ipc_acl, subj_type, subj_id);
                if(sub_item_p)
                  {
                    sub_item_p->entry.rights &= ~rights;
                  }
                /* unprotect */
                rsbac_write_unlock(&default_ipc_lock, &flags);
                /* if no error, mark dirty */
                default_ipc_dirty = TRUE;
                break;
              }
            else
              err = -RSBAC_EINVALIDTARGET;
            /* ready. */
            break;

          case T_SCD:
/*            if (rsbac_debug_ds_acl)
                {
                  char tmp[80];

                  printk(KERN_DEBUG
                         "rsbac_acl_remove_from_acl_entry(): Removing from SCD ACL entry for %s\n",
                         get_acl_scd_type_name(tmp, tid.scd));
                }
*/
            /* default entry? */
            if(tid.scd == AST_none)
              {
                /* wait for write access to default_scd_acl */
                rsbac_write_lock(&default_scd_lock, &flags);
                /* revoke rights, if there. */
                sub_item_p = lookup_scd_subitem(&default_scd_acl, subj_type, subj_id);
                if(sub_item_p)
                  {
                    sub_item_p->entry.rights &= ~rights;
                  }
                /* unprotect */
                rsbac_write_unlock(&default_scd_lock, &flags);
                /* if no error, mark dirty */
                default_scd_dirty = TRUE;
                break;
              }
            /* protect the list */
            rsbac_write_lock(&scd_list_head.lock, &flags);
/*         if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_remove_from_acl_entry(): passed SCD list head lock\n"); */
            scd_item_p = lookup_scd_item(tid.scd);
            if(scd_item_p)
              {
                /* OK, scd_item is there. Now lookup entry. */
                sub_item_p = lookup_scd_subitem(scd_item_p, subj_type, subj_id);
                if(sub_item_p)
                  { /* found -> revoke rights */
                    sub_item_p->entry.rights &= ~rights;
                    scd_list_head.dirty = TRUE;
                  }
              }
            /* unprotect */
            rsbac_write_unlock(&scd_list_head.lock, &flags);
            /* ready. */
            break;

          case T_USER:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_remove_from_acl_entry(): Setting process ACL for pid %u\n",
                       tid.process);
*/
            /* default entry? */
            if(!tid.process)
              {
                /* wait for write access to default_u_acl */
                rsbac_write_lock(&default_u_lock, &flags);
                /* revoke rights, if there. */
                sub_item_p = lookup_u_subitem(&default_u_acl, subj_type, subj_id);
                if(sub_item_p)
                  {
                    sub_item_p->entry.rights &= ~rights;
                  }
                /* unprotect */
                rsbac_write_unlock(&default_u_lock, &flags);
                /* if no error, mark dirty */
                default_u_dirty = TRUE;
                break;
              }
            else
              err = -RSBAC_EINVALIDTARGET;
            /* ready. */
            break;

          case T_PROCESS:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_remove_from_acl_entry(): Setting process ACL for pid %u\n",
                       tid.process);
*/
            /* default entry? */
            if(!tid.process)
              {
                /* wait for write access to default_p_acl */
                rsbac_write_lock(&default_p_lock, &flags);
                /* revoke rights, if there. */
                sub_item_p = lookup_p_subitem(&default_p_acl, subj_type, subj_id);
                if(sub_item_p)
                  {
                    sub_item_p->entry.rights &= ~rights;
                  }
                /* unprotect */
                rsbac_write_unlock(&default_p_lock, &flags);
                /* if no error, mark dirty */
                default_p_dirty = TRUE;
                break;
              }
            else
              err = -RSBAC_EINVALIDTARGET;
            /* ready. */
            break;

          default:
            err = -RSBAC_EINVALIDTARGET;
        }
      return(err);
  }

/* rsbac_acl_set_mask
 * Set inheritance mask for given target to given rights. If item does
 * not exist, it is created.
 */

int rsbac_acl_set_mask        (enum   rsbac_target_t              target,
                               union  rsbac_target_id_t           tid,
                                      rsbac_acl_rights_vector_t   mask)
  {
      int err = 0;
      int list_no;
      struct rsbac_acl_device_list_item_t   * device_p;
      struct rsbac_acl_fd_list_item_t       * fd_item_p;
      struct rsbac_acl_dev_list_item_t      * dev_item_p;
      struct rsbac_acl_scd_list_item_t      * scd_item_p;
      u_long                                  flags,dflags;
    
      if (!rsbac_is_initialized())
        {
          printk(KERN_WARNING "rsbac_acl_set_mask(): RSBAC not initialized\n");
          return(-RSBAC_ENOTINITIALIZED);
        }
      if (target >= T_NONE)
        return(-RSBAC_EINVALIDTARGET);
      if (in_interrupt())
        {
          printk(KERN_WARNING "rsbac_acl_set_mask(): called from interrupt!\n");
        }
      switch (target)
        {
          case T_FILE:
          case T_DIR:
            /* default entry? */
            if(!tid.file.device && !tid.file.inode && !tid.file.dentry_p)
              {
                return -RSBAC_EINVALIDTARGET;
              }
            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_set_mask(): Setting file/dir inheritance mask for device %02u:%02u, inode %u\n",
                         MAJOR(tid.file.device), MINOR(tid.file.device), 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_acl_set_mask(): passed device read lock\n"); */

            /* lookup device */
            device_p = acl_lookup_device(tid.file.device);
            if (!device_p)
              {
                printk(KERN_WARNING
                       "rsbac_acl_set_mask(): Could not lookup device!\n");
                /* free read lock */
                rsbac_read_unlock(&device_list_head.lock, &dflags);
                return(-RSBAC_EINVALIDDEV);
              }
            /* protect this list */
            list_no = fd_hash(tid.file.inode);
            rsbac_write_lock(&device_p->head_table[list_no].lock, &flags);
/*         if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_set_mask(): passed file/dir list head lock %i\n",
                      head_nr); */
            fd_item_p = do_lookup_fd_item(&device_p->head_table[list_no], tid.file.inode);
            if(!fd_item_p)
              { /* not found -> add */
                fd_item_p = do_add_fd_item(&device_p->head_table[list_no],
                                           tid.file.inode,
                                           RSBAC_ACL_DEFAULT_MASK);
                if(!fd_item_p)
                  { /* not added -> unprotect lists and return err */
                    rsbac_write_unlock(&device_p->head_table[list_no].lock, &flags);
                    rsbac_read_unlock(&device_list_head.lock, &dflags);
                    return(-RSBAC_ECOULDNOTADDITEM);
                  }
              }
            /* OK, fd item is there. Now set mask. */
            fd_item_p->mask = mask;
            /* unprotect */
            rsbac_write_unlock(&device_p->head_table[list_no].lock, &flags);
            /* if no error, mark dirty */
            if (!err)
              {
                device_p->head_table[list_no].dirty = TRUE;
              }
            rsbac_read_unlock(&device_list_head.lock, &dflags);
            /* ready. */
            break;

          case T_DEV:
            /* default entry? */
            if(tid.dev.type == D_none)
              {
                return -RSBAC_EINVALIDTARGET;
              }
            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_set_mask(): Setting device inheritance mask for dev %c %02u:%02u\n",
                       'B'+tid.dev.type,
                       MAJOR(tid.dev.id),
                       MINOR(tid.dev.id));
            /* protect the list */
            rsbac_write_lock(&dev_list_head.lock, &flags);
/*          if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_set_mask(): passed device list head lock\n"); */
            dev_item_p = lookup_dev_item(tid.dev);
            if(!dev_item_p)
              { /* not found -> add */
                dev_item_p = add_dev_item(tid.dev, RSBAC_ACL_DEFAULT_MASK);
                if(!dev_item_p)
                  { /* not added -> unprotect lists and return err */
                    rsbac_write_unlock(&dev_list_head.lock, &flags);
                    return(-RSBAC_ECOULDNOTADDITEM);
                  }
              }
            /* OK, dev_item is there. Now set mask. */
            dev_item_p->mask = mask;
            dev_list_head.dirty = TRUE;
            /* unprotect */
            rsbac_write_unlock(&dev_list_head.lock, &flags);
            /* ready. */
            break;

          case T_SCD:
            /* default entry? */
            if(tid.scd == AST_none)
              {
                return -RSBAC_EINVALIDTARGET;
              }
            if (rsbac_debug_ds_acl)
              {
                char tmp[80];

                printk(KERN_DEBUG "rsbac_acl_set_mask(): Setting SCD inheritance mask for %s\n",
                       get_acl_scd_type_name(tmp, tid.scd));
              }
            /* protect the list */
            rsbac_write_lock(&scd_list_head.lock, &flags);
/*          if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_set_mask(): passed SCD list head lock\n"); */
            scd_item_p = lookup_scd_item(tid.scd);
            if(!scd_item_p)
              { /* not found -> add */
                scd_item_p = add_scd_item(tid.scd, RSBAC_ACL_DEFAULT_MASK);
                if(!scd_item_p)
                  { /* not added -> unprotect lists and return err */
                    rsbac_write_unlock(&scd_list_head.lock, &flags);
                    return(-RSBAC_ECOULDNOTADDITEM);
                  }
              }
            /* OK, scd_item is there. Now set mask. */
            scd_item_p->mask = mask;
            scd_list_head.dirty = TRUE;
            /* unprotect */
            rsbac_write_unlock(&scd_list_head.lock, &flags);
            /* ready. */
            break;

          default:
            err = -RSBAC_EINVALIDTARGET;
        }
      return(err);
  }

/* rsbac_acl_get_mask
 * Get inheritance mask for given target. If item does
 * not exist, default mask is returned.
 */

int rsbac_acl_get_mask        (enum   rsbac_target_t              target,
                               union  rsbac_target_id_t           tid,
                                      rsbac_acl_rights_vector_t * mask_p)
  {
      int err = 0;
      int list_no;
      struct rsbac_acl_device_list_item_t   * device_p;
      struct rsbac_acl_fd_list_item_t       * fd_item_p;
      struct rsbac_acl_dev_list_item_t      * dev_item_p;
      struct rsbac_acl_scd_list_item_t      * scd_item_p;
      u_long                                  flags,dflags;
    
      if (!rsbac_is_initialized())
        {
          printk(KERN_WARNING "rsbac_acl_get_mask(): RSBAC not initialized\n");
          return(-RSBAC_ENOTINITIALIZED);
        }
      if (target >= T_NONE)
        return(-RSBAC_EINVALIDTARGET);
      if (in_interrupt())
        {
          printk(KERN_WARNING "rsbac_acl_get_mask(): called from interrupt!\n");
        }
      switch (target)
        {
          case T_FILE:
          case T_DIR:
            /* default entry? */
            if(!tid.file.device && !tid.file.inode && !tid.file.dentry_p)
              {
                return -RSBAC_EINVALIDTARGET;
              }
            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_get_mask(): Setting file/dir inheritance mask for device %02u:%02u, inode %u\n",
                         MAJOR(tid.file.device), MINOR(tid.file.device), 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_acl_get_mask(): passed device read lock\n"); */

            /* lookup device */
            device_p = acl_lookup_device(tid.file.device);
            if (!device_p)
              {
                printk(KERN_WARNING
                       "rsbac_acl_get_mask(): Could not lookup device!\n");
                /* free read lock */
                rsbac_read_unlock(&device_list_head.lock, &dflags);
                return(-RSBAC_EINVALIDDEV);
              }
            /* protect this list */
            list_no = fd_hash(tid.file.inode);
            rsbac_read_lock(&device_p->head_table[list_no].lock, &flags);
/*         if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_get_mask(): passed file/dir list head lock %i\n",
                      head_nr); */
            fd_item_p = do_lookup_fd_item(&device_p->head_table[list_no], tid.file.inode);
            if(fd_item_p)
              {
                *mask_p = fd_item_p->mask;
              }
            else
              {
                *mask_p = RSBAC_ACL_DEFAULT_MASK;
              }
            /* unprotect */
            rsbac_read_unlock(&device_p->head_table[list_no].lock, &flags);
            rsbac_read_unlock(&device_list_head.lock, &dflags);
            /* ready. */
            break;

          case T_DEV:
            /* default entry? */
            if(tid.dev.type == D_none)
              {
                return -RSBAC_EINVALIDTARGET;
              }
            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_get_mask(): Getting device inheritance mask for dev %c %02u:%02u\n",
                       'B'+tid.dev.type,
                       MAJOR(tid.dev.id),
                       MINOR(tid.dev.id));
            /* protect the list */
            rsbac_read_lock(&dev_list_head.lock, &flags);
/*          if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_get_mask(): passed device list head lock\n"); */
            dev_item_p = lookup_dev_item(tid.dev);
            if(dev_item_p)
              {
                *mask_p = dev_item_p->mask;
              }
            else
              {
                *mask_p = RSBAC_ACL_DEFAULT_MASK;
              }
            /* unprotect */
            rsbac_read_unlock(&dev_list_head.lock, &flags);
            /* ready. */
            break;

          case T_SCD:
            /* default entry? */
            if(tid.scd == AST_none)
              {
                return -RSBAC_EINVALIDTARGET;
              }
            if (rsbac_debug_ds_acl)
              {
                char tmp[80];

                printk(KERN_DEBUG
                       "rsbac_acl_get_mask(): Getting SCD inheritance mask for %s\n",
                       get_acl_scd_type_name(tmp, tid.scd));
              }
            /* protect the list */
            rsbac_read_lock(&scd_list_head.lock, &flags);
/*          if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_get_mask(): passed SCD list head lock\n"); */
            scd_item_p = lookup_scd_item(tid.scd);
            if(scd_item_p)
              {
                *mask_p = scd_item_p->mask;
              }
            else
              {
                *mask_p = RSBAC_ACL_DEFAULT_MASK;
              }
            /* unprotect */
            rsbac_read_unlock(&scd_list_head.lock, &flags);
            /* ready. */
            break;

          default:
            err = -RSBAC_EINVALIDTARGET;
        }
      return(err);
  }

/* rsbac_acl_get_rights
 * Get rights from ACL entry for given target and subject.
 * If entry does not exist and inherit is on, inherited rights are used.
 * If there is no parent, the default rights vector for this target type is returned.
 * This function does NOT add role or group rights to user rights!
 */

int rsbac_acl_get_rights       (enum   rsbac_target_t              target,
                                union  rsbac_target_id_t           tid,
                                enum   rsbac_acl_subject_type_t    subj_type,
                                       rsbac_acl_subject_id_t      subj_id,
                                       rsbac_acl_rights_vector_t * rights_p,
                                       boolean                     inherit)
  {
      int err = 0;
      boolean always_inherit=FALSE;
      int list_no;
      struct rsbac_acl_device_list_item_t   * device_p;
      struct rsbac_acl_fd_list_item_t       * fd_item_p;
      struct rsbac_acl_dev_list_item_t      * dev_item_p;
      struct rsbac_acl_scd_list_item_t      * scd_item_p;
      struct rsbac_acl_sublist_item_t       * sub_item_p = NULL;
      u_long                                  flags,dflags;
      rsbac_acl_rights_vector_t               i_rights = 0;

      if (!rsbac_is_initialized())
        {
          printk(KERN_WARNING "rsbac_acl_get_rights(): RSBAC not initialized\n");
          return(-RSBAC_ENOTINITIALIZED);
        }
      if (!rights_p)
        return(-RSBAC_EINVALIDPOINTER);
      if (subj_type >= ACLS_NONE)
        return(-RSBAC_EINVALIDVALUE);
      if (in_interrupt())
        {
          printk(KERN_WARNING "rsbac_acl_get_rights(): called from interrupt!\n");
        }
      switch (target)
        {
          case T_FILE:
          case T_DIR:
            
            if (rsbac_debug_ds_acl)
              {
                char tmp[20];
                printk(KERN_DEBUG "rsbac_acl_get_rights(): Getting rights of %s %u for device %02u:%02u, inode %u\n",
                       get_acl_subject_type_name(tmp,subj_type), subj_id, MAJOR(tid.file.device), MINOR(tid.file.device), tid.file.inode);
              }
            /* default entry? */
            if(!tid.file.device && !tid.file.inode && !tid.file.dentry_p)
              {
                /* wait for read access to default_fd_acl */
                rsbac_read_lock(&default_fd_lock, &flags);
                /* Locate entry */
                sub_item_p = lookup_fd_subitem(&default_fd_acl, subj_type, subj_id);
                if(sub_item_p)
                  {
                    *rights_p = sub_item_p->entry.rights;
                  }
                else
                  {
                    /* last resort: default rights */
                    *rights_p = default_fd_rights;
                  }
                /* unprotect */
                rsbac_read_unlock(&default_fd_lock, &flags);
                break;
              }
            /* 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_acl_get_rights(): passed device read lock\n"); */

            /* lookup device */
            device_p = acl_lookup_device(tid.file.device);
            if (!device_p)
              {
                printk(KERN_WARNING
                       "rsbac_acl_get_rights(): Could not lookup device!\n");
                /* free read lock */
                rsbac_read_unlock(&device_list_head.lock, &dflags);
                return(-RSBAC_EINVALIDDEV);
              }
            /* protect this list */
            list_no = fd_hash(tid.file.inode);
            rsbac_read_lock(&device_p->head_table[list_no].lock, &flags);
/*          if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_get_rights(): passed file/dir list head lock %i\n",
                      head_nr); */
            fd_item_p = do_lookup_fd_item(&device_p->head_table[list_no], tid.file.inode);
            if(   fd_item_p
               && ((sub_item_p = lookup_fd_subitem(fd_item_p, subj_type, subj_id)))
              )
              { /* found ACL entry -> copy rights */
                *rights_p |= sub_item_p->entry.rights;
              }
            if(   (!sub_item_p && inherit)
               || (always_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[list_no].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;
                    err = rsbac_acl_get_rights(target, tid, subj_type, subj_id, &i_rights, TRUE);
                    if(!err)
                      {
                        if(fd_item_p)
                          i_rights &= fd_item_p->mask;
                        *rights_p |= i_rights;
                      }
                    return err;
                  }
                else
                  {
                    /* no more recursion possible -> try default_fd_acl */
                    /* wait for read access to default_fd_acl */
                    rsbac_read_lock(&default_fd_lock, &flags);
                    sub_item_p = lookup_fd_subitem(&default_fd_acl, subj_type, subj_id);
                    if(sub_item_p)
                      {
                        /* found, use it */
                        i_rights = sub_item_p->entry.rights;
                        if(fd_item_p)
                          i_rights &= fd_item_p->mask;
                        *rights_p |= i_rights;
                      }
                    else
                      {
                        /* last resort: default rights */
                        *rights_p |= default_fd_rights;
                      }
                    rsbac_read_unlock(&default_fd_lock, &flags);
                  }
                #else /* VERSION >= 2.4.0 */
                  tid.file.inode = 0;
                  if(tid.file.dentry_p)
                    {
                      /* 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
                            )
                            {
                              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_acl_get_rights(): oops - d_parent == dentry_p!\n");
                            }
                          else
                            {
                              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[list_no].lock, &flags);
                  /* free access to device_list_head - see above */
                  rsbac_read_unlock(&device_list_head.lock, &dflags);
                  if(tid.file.inode)
                    {
                      /* parent must always be DIR */
                      err = rsbac_acl_get_rights(T_DIR, tid, subj_type, subj_id, &i_rights, TRUE);
                      if(!err)
                        {
                          if(fd_item_p)
                            i_rights &= fd_item_p->mask;
                          *rights_p |= i_rights;
                        }
                    }
                  else
                    {
                      /* no more recursion possible -> try default_fd_acl */
                      /* wait for read access to default_fd_acl */
                      rsbac_read_lock(&default_fd_lock, &flags);
                      sub_item_p = lookup_fd_subitem(&default_fd_acl, subj_type, subj_id);
                      if(sub_item_p)
                        {
                          /* found, use it */
                          i_rights = sub_item_p->entry.rights;
                          if(fd_item_p)
                            i_rights &= fd_item_p->mask;
                          *rights_p |= i_rights;
                        }
                      else
                        {
                          /* last resort: default rights */
                          *rights_p |= default_fd_rights;
                        }
                      rsbac_read_unlock(&default_fd_lock, &flags);
                    }
                  return err;
                #endif /* #else of Version < 2.4.0 */
              }
            else /* do not inherit */
              {
                /* last resort: default rights */
                *rights_p |= default_fd_rights;
              }
            /* unprotect */
            rsbac_read_unlock(&device_p->head_table[list_no].lock, &flags);
            rsbac_read_unlock(&device_list_head.lock, &dflags);
            /* ready. */
            break;

          case T_DEV:
            /* default entry? */
            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_get_rights(): Getting device rights for dev %c %02u:%02u\n",
                       'B'+tid.dev.type,
                       MAJOR(tid.dev.id),
                       MINOR(tid.dev.id));
            if((tid.dev.type == D_none) && !tid.dev.id)
              {
                /* wait for read access to default_dev_acl */
                rsbac_read_lock(&default_dev_lock, &flags);
                /* Locate entry */
                sub_item_p = lookup_dev_subitem(&default_dev_acl, subj_type, subj_id);
                if(sub_item_p)
                  {
                    *rights_p = sub_item_p->entry.rights;
                  }
                else
                  {
                    /* last resort: default rights */
                    *rights_p = default_dev_rights;
                  }
                /* unprotect */
                rsbac_read_unlock(&default_dev_lock, &flags);
                break;
              }
            /* protect the list */
            rsbac_read_lock(&dev_list_head.lock, &flags);
/*         if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_get_rights(): passed device list head lock %i\n",
                      head_nr); */
            dev_item_p = lookup_dev_item(tid.dev);
            if(   dev_item_p
               && ((sub_item_p = lookup_dev_subitem(dev_item_p, subj_type, subj_id)))
              )
              { /* found */
                *rights_p = sub_item_p->entry.rights;
              }
            else
              {
                /* try default_dev_acl */
                sub_item_p = lookup_dev_subitem(&default_dev_acl, subj_type, subj_id);
                if(sub_item_p)
                  {
                    /* found, use it */
                    *rights_p = sub_item_p->entry.rights;
                  }
                else
                  {
                    /* last resort: default rights */
                    *rights_p = default_dev_rights;
                  }
              }
            /* unprotect */
            rsbac_read_unlock(&dev_list_head.lock, &flags);
            /* ready. */
            break;

          case T_IPC:
            
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_get_rights(): Getting IPC rights for type %u\n",
                       tid.ipc.type);
*/
            /* Use default ACL */
            /* wait for read access to default_ipc_acl */
            rsbac_read_lock(&default_ipc_lock, &flags);
            /* Locate entry */
            sub_item_p = lookup_ipc_subitem(&default_ipc_acl, subj_type, subj_id);
            if(sub_item_p)
              {
                *rights_p = sub_item_p->entry.rights;
              }
            else
              {
                /* last resort: default rights */
                *rights_p = default_ipc_rights;
              }
            /* unprotect */
            rsbac_read_unlock(&default_ipc_lock, &flags);
            break;

          case T_SCD:
            /* default entry? */
            if (rsbac_debug_ds_acl)
              {
                char tmp[80];
                printk(KERN_DEBUG
                       "rsbac_acl_get_rights(): Getting SCD rights for %s\n",
                       get_acl_scd_type_name(tmp, tid.scd));
              }
            if(tid.scd == AST_none)
              {
                /* wait for read access to default_scd_acl */
                rsbac_read_lock(&default_scd_lock, &flags);
                /* Locate entry */
                sub_item_p = lookup_scd_subitem(&default_scd_acl, subj_type, subj_id);
                if(sub_item_p)
                  {
                    *rights_p = sub_item_p->entry.rights;
                  }
                else
                  {
                    /* last resort: default rights */
                    *rights_p = default_scd_rights;
                  }
                /* unprotect */
                rsbac_read_unlock(&default_scd_lock, &flags);
                break;
              }
            /* protect the list */
            rsbac_read_lock(&scd_list_head.lock, &flags);
/*         if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_get_rights(): passed SCD list head lock\n"); */
            scd_item_p = lookup_scd_item(tid.scd);
            if(   scd_item_p
               && ((sub_item_p = lookup_scd_subitem(scd_item_p, subj_type, subj_id)))
              )
              { /* found */
                *rights_p = sub_item_p->entry.rights;
              }
            else
              {
                /* try default_scd_acl */
                sub_item_p = lookup_scd_subitem(&default_scd_acl, subj_type, subj_id);
                if(sub_item_p)
                  {
                    /* found, use it */
                    *rights_p = sub_item_p->entry.rights;
                  }
                else
                  {
                    /* last resort: default rights */
                    *rights_p = default_scd_rights;
                  }
              }
            /* unprotect */
            rsbac_read_unlock(&scd_list_head.lock, &flags);
            /* ready. */
            break;

          case T_USER:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_get_rights(): Getting user rights for user %u\n",
                       tid.user);
*/
            /* Use default entry */
            /* wait for read access to default_u_acl */
            rsbac_read_lock(&default_u_lock, &flags);
            /* Locate entry */
            sub_item_p = lookup_u_subitem(&default_u_acl, subj_type, subj_id);
            if(sub_item_p)
              {
                *rights_p = sub_item_p->entry.rights;
              }
            else
              {
                /* last resort: default rights */
                *rights_p = default_u_rights;
              }
            /* unprotect */
            rsbac_read_unlock(&default_u_lock, &flags);
            break;

          case T_PROCESS:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_get_rights(): Getting process rights for pid %u\n",
                       tid.process);
*/
            /* Use default entry */
            /* wait for read access to default_p_acl */
            rsbac_read_lock(&default_p_lock, &flags);
            /* Locate entry */
            sub_item_p = lookup_p_subitem(&default_p_acl, subj_type, subj_id);
            if(sub_item_p)
              {
                *rights_p = sub_item_p->entry.rights;
              }
            else
              {
                /* last resort: default rights */
                *rights_p = default_p_rights;
              }
            /* unprotect */
            rsbac_read_unlock(&default_p_lock, &flags);
            break;

          default:
            err = -RSBAC_EINVALIDTARGET;
        }
      return(err);
  }

/* rsbac_acl_get_super
 * Show, whether supervisor right is set for given target and subject.
 * If supervisor right is not set, it is checked at all parents, unless
 * CONFIG_RSBAC_ACL_SUPER_FILTER is set *and* supervisor is masked out.
 */

int rsbac_acl_get_super (enum   rsbac_target_t              target,
                         union  rsbac_target_id_t           tid,
                         enum   rsbac_acl_subject_type_t    subj_type,
                                rsbac_acl_subject_id_t      subj_id,
                                boolean                   * result)
  {
      int err = 0;
      boolean                                 inherit=FALSE;
      int list_no;
      struct rsbac_acl_device_list_item_t   * device_p;
      struct rsbac_acl_fd_list_item_t       * fd_item_p;
      struct rsbac_acl_dev_list_item_t      * dev_item_p;
      struct rsbac_acl_scd_list_item_t      * scd_item_p;
      struct rsbac_acl_sublist_item_t       * sub_item_p = NULL;
      u_long                                  flags,dflags;
      rsbac_acl_rights_vector_t               i_svec = (rsbac_acl_rights_vector_t) 1 << ACLR_SUPERVISOR;
      boolean                                 i_result = FALSE;

      if (!rsbac_is_initialized())
        {
          printk(KERN_WARNING "rsbac_acl_get_super(): RSBAC not initialized\n");
          return(-RSBAC_ENOTINITIALIZED);
        }
      if (!result)
        return(-RSBAC_EINVALIDPOINTER);
      if (subj_type >= ACLS_NONE)
        return(-RSBAC_EINVALIDVALUE);
      if (in_interrupt())
        {
          printk(KERN_WARNING "rsbac_acl_get_super(): called from interrupt!\n");
        }
      switch (target)
        {
          case T_FILE:
          case T_DIR:
            
            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_get_super(): Checking supervisor right of subject %u,%u for device %02u:%02u, inode %u\n",
                         subj_type, subj_id, MAJOR(tid.file.device), MINOR(tid.file.device), tid.file.inode);
            /* default entry? */
            if(!tid.file.device && !tid.file.inode && !tid.file.dentry_p)
              {
                /* wait for read access to default_fd_acl */
                rsbac_read_lock(&default_fd_lock, &flags);
                /* Locate entry */
                sub_item_p = lookup_fd_subitem(&default_fd_acl, subj_type, subj_id);
                if(   (sub_item_p && (sub_item_p->entry.rights & i_svec))
                   || (default_fd_rights & i_svec)
                  )
                  *result = TRUE;
                else
                  *result = FALSE;
                /* unprotect */
                rsbac_read_unlock(&default_fd_lock, &flags);
                break;
              }
            /* 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_acl_get_super(): passed device read lock\n"); */

            /* lookup device */
            device_p = acl_lookup_device(tid.file.device);
            if (!device_p)
              {
                printk(KERN_WARNING
                       "rsbac_acl_get_super(): Could not lookup device!\n");
                /* free read lock */
                rsbac_read_unlock(&device_list_head.lock, &dflags);
                return(-RSBAC_EINVALIDDEV);
              }
            /* protect this list */
            list_no = fd_hash(tid.file.inode);
            rsbac_read_lock(&device_p->head_table[list_no].lock, &flags);
/*          if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_get_super(): passed file/dir list head lock %i\n",
                      head_nr); */
            fd_item_p = do_lookup_fd_item(&device_p->head_table[list_no], tid.file.inode);
            if(fd_item_p)
              {
                if(   (sub_item_p = lookup_fd_subitem(fd_item_p, subj_type, subj_id))
                   && sub_item_p->entry.rights & i_svec)
                  *result = TRUE;
                else
                #ifdef CONFIG_RSBAC_ACL_SUPER_FILTER
                  if(!(fd_item_p->mask & i_svec))
                    *result = FALSE;
                  else
                #endif
                    inherit = TRUE;
              }
            else
              inherit = TRUE;
              
            if(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[list_no].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;
                    err = rsbac_acl_get_super(target, tid, subj_type, subj_id, &i_result);
                    if(!err)
                      {
                        *result = i_result;
                      }
                    return err;
                  }
                else
                  {
                    /* no more recursion possible -> try default_fd_acl */
                    /* wait for read access to default_fd_acl */
                    rsbac_read_lock(&default_fd_lock, &flags);
                    sub_item_p = lookup_fd_subitem(&default_fd_acl, subj_type, subj_id);
                    if(   (sub_item_p && (sub_item_p->entry.rights & i_svec))
                       || (default_fd_rights & i_svec)
                      )
                      *result = TRUE;
                    else
                      *result = FALSE;
                    rsbac_read_unlock(&default_fd_lock, &flags);
                  }
             #else /* VERSION >= 2.4.0 */
                tid.file.inode = 0;
                if(tid.file.dentry_p)
                  {
                    /* 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
                          )
                          {
                            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_acl_get_rights(): oops - d_parent == dentry_p!\n");
                          }
                        else
                          {
                            tid.file.inode = tid.file.dentry_p->d_parent->d_inode->i_ino;
                            tid.file.dentry_p = tid.file.dentry_p->d_parent;
                          }
                      }
                  }
                if(tid.file.inode)
                  {
                    /* OK, recurse to parent */
                    /* unprotect the list - parent might be in another */
                    rsbac_read_unlock(&device_p->head_table[list_no].lock, &flags);
                    /* free access to device_list_head - see above */
                    rsbac_read_unlock(&device_list_head.lock, &dflags);
                    /* parent must always be DIR */
                    err = rsbac_acl_get_super(target, tid, subj_type, subj_id, &i_result);
                    if(!err)
                      {
                        *result = i_result;
                      }
                    return err;
                  }
                else
                  {
                    /* no more recursion possible -> try default_fd_acl */
                    /* wait for read access to default_fd_acl */
                    rsbac_read_lock(&default_fd_lock, &flags);
                    sub_item_p = lookup_fd_subitem(&default_fd_acl, subj_type, subj_id);
                    if(   (sub_item_p && (sub_item_p->entry.rights & i_svec))
                       || (default_fd_rights & i_svec)
                      )
                      *result = TRUE;
                    else
                      *result = FALSE;
                    rsbac_read_unlock(&default_fd_lock, &flags);
                  }
             #endif /* #else of Version < 2.4.0 */
              }
            /* unprotect */
            rsbac_read_unlock(&device_p->head_table[list_no].lock, &flags);
            rsbac_read_unlock(&device_list_head.lock, &dflags);
            /* ready. */
            break;

          case T_DEV:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_get_super(): Getting super right for dev %c %02u:%02u\n",
                       'B'+tid.dev.type,
                       MAJOR(tid.dev.id),
                       MINOR(tid.dev.id)); */
            if((tid.dev.type == D_none) && !tid.dev.id)
              {
                /* wait for read access to default_dev_acl */
                rsbac_read_lock(&default_dev_lock, &flags);
                /* Locate entry */
                sub_item_p = lookup_dev_subitem(&default_dev_acl, subj_type, subj_id);
                if(   (sub_item_p && (sub_item_p->entry.rights & i_svec))
                   || (default_p_rights & i_svec)
                  )
                  *result = TRUE;
                else
                  *result = FALSE;
                /* unprotect */
                rsbac_read_unlock(&default_dev_lock, &flags);
                break;
              }
            /* protect the list */
            rsbac_read_lock(&dev_list_head.lock, &flags);
/*         if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_get_super(): passed device list head lock\n"); */
            dev_item_p = lookup_dev_item(tid.dev);
            if(dev_item_p)
              {
                if(   (sub_item_p = lookup_dev_subitem(dev_item_p, subj_type, subj_id))
                   && sub_item_p->entry.rights & i_svec)
                  *result = TRUE;
                else
                #ifdef CONFIG_RSBAC_ACL_SUPER_FILTER
                  if(!(dev_item_p->mask & i_svec))
                    *result = FALSE;
                  else
                #endif
                    inherit = TRUE;
              }
            else
              inherit = TRUE;

            if(inherit)
              {
                /* no recursion possible -> try default_dev_acl */
                sub_item_p = lookup_dev_subitem(&default_dev_acl, subj_type, subj_id);
                if(   (sub_item_p && (sub_item_p->entry.rights & i_svec))
                   || (default_dev_rights & i_svec)
                  )
                  *result = TRUE;
                else
                  *result = FALSE;
              }
            /* unprotect */
            rsbac_read_unlock(&dev_list_head.lock, &flags);
            /* ready. */
            break;

          case T_IPC:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_get_super(): Getting super for type %u\n",
                       tid.ipc.type);
*/
            /* Use default entry */
            /* wait for read access to default_ipc_acl */
            rsbac_read_lock(&default_ipc_lock, &flags);
            /* Locate entry */
            sub_item_p = lookup_ipc_subitem(&default_ipc_acl, subj_type, subj_id);
            if(   (sub_item_p && (sub_item_p->entry.rights & i_svec))
               || (default_ipc_rights & i_svec)
              )
              *result = TRUE;
            else
              *result = FALSE;
            /* unprotect */
            rsbac_read_unlock(&default_ipc_lock, &flags);
            break;

          case T_SCD:
/*            if (rsbac_debug_ds_acl)
                {
                  char tmp[80];

                  printk(KERN_DEBUG
                         "rsbac_acl_get_super(): Getting super right for SCD %s\n",
                         get_acl_scd_type_name(tmp, tid.scd)); */
            if(tid.scd == AST_none)
              {
                /* wait for read access to default_scd_acl */
                rsbac_read_lock(&default_scd_lock, &flags);
                /* Locate entry */
                sub_item_p = lookup_scd_subitem(&default_scd_acl, subj_type, subj_id);
                if(   (sub_item_p && (sub_item_p->entry.rights & i_svec))
                   || (default_p_rights & i_svec)
                  )
                  *result = TRUE;
                else
                  *result = FALSE;
                /* unprotect */
                rsbac_read_unlock(&default_scd_lock, &flags);
                break;
              }
            /* protect the list */
            rsbac_read_lock(&scd_list_head.lock, &flags);
/*         if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_get_super(): passed device list head lock\n"); */
            scd_item_p = lookup_scd_item(tid.scd);
            if(scd_item_p)
              {
                if(   (sub_item_p = lookup_scd_subitem(scd_item_p, subj_type, subj_id))
                   && sub_item_p->entry.rights & i_svec)
                  *result = TRUE;
                else
                #ifdef CONFIG_RSBAC_ACL_SUPER_FILTER
                  if(!(scd_item_p->mask & i_svec))
                    *result = FALSE;
                  else
                #endif
                    inherit = TRUE;
              }
            else
              inherit = TRUE;

            if(inherit)
              {
                /* no recursion possible -> try default_scd_acl */
                sub_item_p = lookup_scd_subitem(&default_scd_acl, subj_type, subj_id);
                if(   (sub_item_p && (sub_item_p->entry.rights & i_svec))
                   || (default_scd_rights & i_svec)
                  )
                  *result = TRUE;
                else
                  *result = FALSE;
              }
            /* unprotect */
            rsbac_read_unlock(&scd_list_head.lock, &flags);
            /* ready. */
            break;

          case T_USER:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_get_super(): Getting user super for user %u\n",
                       tid.user);
*/
            /* Use default entry */
            /* wait for read access to default_u_acl */
            rsbac_read_lock(&default_u_lock, &flags);
            /* Locate entry */
            sub_item_p = lookup_u_subitem(&default_u_acl, subj_type, subj_id);
            if(   (sub_item_p && (sub_item_p->entry.rights & i_svec))
               || (default_u_rights & i_svec)
              )
              *result = TRUE;
            else
              *result = FALSE;
            /* unprotect */
            rsbac_read_unlock(&default_u_lock, &flags);
            break;

          case T_PROCESS:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_get_super(): Getting process super for pid %u\n",
                       tid.process);
*/
            /* Use default entry */
            /* wait for read access to default_p_acl */
            rsbac_read_lock(&default_p_lock, &flags);
            /* Locate entry */
            sub_item_p = lookup_p_subitem(&default_p_acl, subj_type, subj_id);
            if(   (sub_item_p && (sub_item_p->entry.rights & i_svec))
               || (default_p_rights & i_svec)
              )
              *result = TRUE;
            else
              *result = FALSE;
            /* unprotect */
            rsbac_read_unlock(&default_p_lock, &flags);
            break;

          default:
            err = -RSBAC_EINVALIDTARGET;
        }
      return(err);
  }

#if 0
/************************************************************************** */
/* The rsbac_acl_copy_fd_acl() function copies a file/dir ACL to another    */
/* file/dir ACL. The old ACL of fd2 is erased before copying.               */

int rsbac_acl_copy_fd_acl(struct rsbac_fs_file_t file1,
                          struct rsbac_fs_file_t file2)
    {
      struct rsbac_acl_device_list_item_t   * device1_p;
      struct rsbac_acl_device_list_item_t   * device2_p;
      u_long                                  flags1,flags2,dflags;
      struct rsbac_acl_fd_list_item_t * fd_item1_p;
      struct rsbac_acl_fd_list_item_t * fd_item2_p;
      struct rsbac_acl_sublist_item_t  * sub_item_p;
      int list1_no, list2_no;
      int err = 0;

      /* wait for read access to device_list_head */
      rsbac_read_lock(&device_list_head.lock, &dflags);
      /* lookup device */
      device1_p = acl_lookup_device(file1.device);
      if (!device1_p)
        {
          printk(KERN_WARNING
                 "rsbac_acl_copy_fd_acl(): Could not lookup device!\n");
          err = -RSBAC_EINVALIDDEV;
          goto out_dev_unlock;
        }
      device2_p = acl_lookup_device(file2.device);
      if (!device2_p)
        {
          printk(KERN_WARNING
                 "rsbac_acl_copy_fd_acl(): Could not lookup device!\n");
          err = -RSBAC_EINVALIDDEV;
          goto out_dev_unlock;
        }
      list1_no = fd_hash(file1.inode);
      list2_no = fd_hash(file2.inode);
      /* protect second list */
      rsbac_write_lock(&device2_p->head_table[list2_no].lock, &flags2);
      /* protect first list only, if different */
      if(   (device1_p != device2_p)
         || (list1_no != list2_no)
        )
        rsbac_read_lock(&device1_p->head_table[list1_no].lock, &flags1);
      /* lookup */
      fd_item1_p = do_lookup_fd_item(&device1_p->head_table[list1_no], file1.inode);
      if(fd_item1_p)
        {
          fd_item2_p = do_lookup_fd_item(&device2_p->head_table[list2_no], file2.inode);
          if(!fd_item2_p)
            fd_item2_p = do_add_fd_item(&device2_p->head_table[list2_no],
                                        file2.inode,
                                        fd_item1_p->mask);
          if(!fd_item2_p)
            {
              printk(KERN_WARNING
                     "rsbac_acl_copy_fd_acl(): Could not add fd item!\n");
              err = -RSBAC_ECOULDNOTADDITEM;
              goto out_unlock;
            }
          /* clear old ACL of fd_item2 */
          remove_all_fd_subitems(fd_item2_p);
          /* copy */
          sub_item_p = fd_item1_p->sublist_head;
          while(sub_item_p)
            {
              add_fd_subitem(fd_item2_p, sub_item_p->entry);
              sub_item_p = sub_item_p->next;
            }
        }
      else
        {
          /* remove acl of second file */
          do_remove_fd_item(&device2_p->head_table[list2_no], file2.inode);
        }
out_unlock:
      /* unprotect second list */
      rsbac_write_unlock(&device2_p->head_table[list2_no].lock, &flags2);
      /* unprotect first list only, if different */
      if(   (device1_p != device2_p)
         || (list1_no != list2_no)
        )
        rsbac_read_unlock(&device1_p->head_table[list1_no].lock, &flags1);
out_dev_unlock:
      /* free device read lock */
      rsbac_read_unlock(&device_list_head.lock, &dflags);

      return(err);
    }; /* end of copy_fd_acl() */
#endif /* 0 */

/*************************************************
 * rsbac_acl_get_tlist
 * Get subjects from ACL entries for given target.
 */

int rsbac_acl_get_tlist       (enum   rsbac_target_t            target,
                               union  rsbac_target_id_t         tid,
                               struct rsbac_acl_entry_t         entry_array[],
                                      u_int                     maxnum)
  {
      int count = 0;
      int list_no;
      struct rsbac_acl_device_list_item_t   * device_p;
      struct rsbac_acl_fd_list_item_t       * fd_item_p;
      struct rsbac_acl_dev_list_item_t      * dev_item_p;
      struct rsbac_acl_scd_list_item_t      * scd_item_p;
      struct rsbac_acl_sublist_item_t       * sub_item_p;
      u_long                                  flags,dflags;

      if (!rsbac_is_initialized())
        {
          printk(KERN_WARNING "rsbac_acl_get_tlist(): RSBAC not initialized\n");
          return(-RSBAC_ENOTINITIALIZED);
        }
      if (!entry_array)
        return(-RSBAC_EINVALIDPOINTER);
      if (in_interrupt())
        {
          printk(KERN_WARNING "rsbac_acl_get_tlist(): called from interrupt!\n");
        }
      switch (target)
        {
          case T_FILE:
          case T_DIR:
            
            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_get_tlist(): Getting tlist for device %02u:%02u, inode %u\n",
                       MAJOR(tid.file.device), MINOR(tid.file.device), tid.file.inode);
            /* default entry? */
            if(!tid.file.device && !tid.file.inode && !tid.file.dentry_p)
              {
                /* wait for read access to default_fd_acl */
                rsbac_read_lock(&default_fd_lock, &flags);
                /* copy all subitems */
                sub_item_p = default_fd_acl.sublist_head;
                while(sub_item_p && (count < maxnum))
                  {
                    entry_array[count] = sub_item_p->entry;
                    sub_item_p = sub_item_p->next;
                    count++;
                  }
                /* unprotect */
                rsbac_read_unlock(&default_fd_lock, &flags);
                break;
              }
            /* 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_acl_get_tlist(): passed device read lock\n"); */

            /* lookup device */
            device_p = acl_lookup_device(tid.file.device);
            if (!device_p)
              {
                printk(KERN_WARNING
                       "rsbac_acl_get_tlist(): Could not lookup device!\n");
                /* free read lock */
                rsbac_read_unlock(&device_list_head.lock, &dflags);
                return(-RSBAC_EINVALIDDEV);
              }
            /* protect this list */
            list_no = fd_hash(tid.file.inode);
            rsbac_read_lock(&device_p->head_table[list_no].lock, &flags);
/*          if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_get_tlist(): passed file/dir list head lock %i\n",
                      head_nr); */
            fd_item_p = do_lookup_fd_item(&device_p->head_table[list_no], tid.file.inode);
            if(fd_item_p)
              {
                /* copy all subitems */
                sub_item_p = fd_item_p->sublist_head;
                while(sub_item_p && (count < maxnum))
                  {
                    entry_array[count] = sub_item_p->entry;
                    sub_item_p = sub_item_p->next;
                    count++;
                  }
              }
            /* unprotect */
            rsbac_read_unlock(&device_p->head_table[list_no].lock, &flags);
            rsbac_read_unlock(&device_list_head.lock, &dflags);
            /* ready. */
            break;

          case T_DEV:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_get_tlist(): Getting tlist for dev %c %02u:%02u\n",
                       'B'+tid.dev.type,
                       MAJOR(tid.dev.id),
                       MINOR(tid.dev.id)); */
            if((tid.dev.type == D_none) && !tid.dev.id)
              {
                /* wait for read access to default_dev_acl */
                rsbac_read_lock(&default_dev_lock, &flags);
                /* copy all subitems */
                sub_item_p = default_dev_acl.sublist_head;
                while(sub_item_p && (count < maxnum))
                  {
                    entry_array[count] = sub_item_p->entry;
                    sub_item_p = sub_item_p->next;
                    count++;
                  }
                /* unprotect */
                rsbac_read_unlock(&default_dev_lock, &flags);
                break;
              }
            /* protect the list */
            rsbac_read_lock(&dev_list_head.lock, &flags);
/*         if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_get_tlist(): passed device list head lock\n"); */
            dev_item_p = lookup_dev_item(tid.dev);
            if(dev_item_p)
              {
                /* copy all subitems */
                sub_item_p = dev_item_p->sublist_head;
                while(sub_item_p && (count < maxnum))
                  {
                    entry_array[count] = sub_item_p->entry;
                    sub_item_p = sub_item_p->next;
                    count++;
                  }
              }
            /* unprotect */
            rsbac_read_unlock(&dev_list_head.lock, &flags);
            /* ready. */
            break;

          case T_IPC:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_get_tlist(): Getting tlist for IPC type %u\n",
                       tid.ipc.type);
*/
            /* default entry? */
            if(tid.ipc.type == I_none)
              {
                /* wait for read access to default_ipc_acl */
                rsbac_read_lock(&default_ipc_lock, &flags);
                /* copy all subitems */
                sub_item_p = default_ipc_acl.sublist_head;
                while(sub_item_p && (count < maxnum))
                  {
                    entry_array[count] = sub_item_p->entry;
                    sub_item_p = sub_item_p->next;
                    count++;
                  }
                /* unprotect */
                rsbac_read_unlock(&default_ipc_lock, &flags);
                break;
              }
            else
              count = 0;
            break;

          case T_SCD:
/*            if (rsbac_debug_ds_acl)
                {
                  char tmp[80];

                  printk(KERN_DEBUG "rsbac_acl_get_tlist(): Getting tlist for SCD %s\n",
                         get_acl_scd_type_name(tmp, tid.scd)); */
            if(tid.scd == AST_none)
              {
                /* wait for read access to default_scd_acl */
                rsbac_read_lock(&default_scd_lock, &flags);
                /* copy all subitems */
                sub_item_p = default_scd_acl.sublist_head;
                while(sub_item_p && (count < maxnum))
                  {
                    entry_array[count] = sub_item_p->entry;
                    sub_item_p = sub_item_p->next;
                    count++;
                  }
                /* unprotect */
                rsbac_read_unlock(&default_scd_lock, &flags);
                break;
              }
            /* protect the list */
            rsbac_read_lock(&scd_list_head.lock, &flags);
/*         if (rsbac_debug_ds)
              printk(KERN_DEBUG
                     "rsbac_acl_get_tlist(): passed device list head lock\n"); */
            scd_item_p = lookup_scd_item(tid.scd);
            if(scd_item_p)
              {
                /* copy all subitems */
                sub_item_p = scd_item_p->sublist_head;
                while(sub_item_p && (count < maxnum))
                  {
                    entry_array[count] = sub_item_p->entry;
                    sub_item_p = sub_item_p->next;
                    count++;
                  }
              }
            /* unprotect */
            rsbac_read_unlock(&scd_list_head.lock, &flags);
            /* ready. */
            break;

          case T_USER:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_get_tlist(): Getting tlist for pid %u\n",
                       tid.process);
*/
            /* default entry? */
            if(tid.user == RSBAC_NO_USER)
              {
                /* wait for read access to default_u_acl */
                rsbac_read_lock(&default_u_lock, &flags);
                /* copy all subitems */
                sub_item_p = default_u_acl.sublist_head;
                while(sub_item_p && (count < maxnum))
                  {
                    entry_array[count] = sub_item_p->entry;
                    sub_item_p = sub_item_p->next;
                    count++;
                  }
                /* unprotect */
                rsbac_read_unlock(&default_u_lock, &flags);
                break;
              }
            else
              count = 0;
            break;

          case T_PROCESS:
/*            if (rsbac_debug_ds_acl)
                printk(KERN_DEBUG "rsbac_acl_get_tlist(): Getting tlist for pid %u\n",
                       tid.process);
*/
            /* default entry? */
            if(!tid.process)
              {
                /* wait for read access to default_p_acl */
                rsbac_read_lock(&default_p_lock, &flags);
                /* copy all subitems */
                sub_item_p = default_p_acl.sublist_head;
                while(sub_item_p && (count < maxnum))
                  {
                    entry_array[count] = sub_item_p->entry;
                    sub_item_p = sub_item_p->next;
                    count++;
                  }
                /* unprotect */
                rsbac_read_unlock(&default_p_lock, &flags);
                break;
              }
            else
              count = 0;
            break;

          default:
            count = -RSBAC_EINVALIDTARGET;
        }
      return(count);
  }

/* add a group with new id and fill this id into *group_id_p */
/* if old content of group_id_p is 0, make new id, else try given id */
int rsbac_acl_add_group(rsbac_uid_t owner, enum rsbac_acl_group_type_t type, char * name,
                        rsbac_acl_group_id_t * group_id_p)
  {
           u_long                  flags;
    struct rsbac_acl_group_entry_t entry;
           int                     err=0;

    if(type >= ACLG_NONE)
      return -RSBAC_EINVALIDVALUE;
    if(!name)
      return -RSBAC_EINVALIDPOINTER;
    rsbac_write_lock(&group_list_head.lock, &flags);
    if(!*group_id_p)
      {
        /* step new group counter */
        group_list_head.last_new++;
        /* Just in case the counter has wrapped. It is almost impossible that all IDs are in use. */
        while(!group_list_head.last_new || lookup_group_item(group_list_head.last_new))
          group_list_head.last_new++;
        entry.id = group_list_head.last_new;
      }
    else
      {
        if(lookup_group_item(*group_id_p))
          {
            rsbac_write_unlock(&group_list_head.lock, &flags);
            return(-RSBAC_EEXISTS);
          }
        entry.id = *group_id_p;
      }
    entry.owner = owner;
    entry.type = type;
    strncpy(entry.name, name, RSBAC_ACL_GROUP_NAMELEN-1);
    entry.name[RSBAC_ACL_GROUP_NAMELEN-1] = 0;
    if(!add_group_item(entry))
      err = -RSBAC_ECOULDNOTADDITEM;
    else
      {
        *group_id_p = entry.id;
        group_list_head.dirty = TRUE;
      }
    rsbac_write_unlock(&group_list_head.lock, &flags);
    return(err);
  }

int rsbac_acl_change_group(     rsbac_acl_group_id_t     id,
                                rsbac_uid_t              owner,
                           enum rsbac_acl_group_type_t   type,
                                char                   * name)
  {
           u_long                        flags;
           int                           err=0;
    struct rsbac_acl_group_list_item_t * item_p;

    if(!id)
      return -RSBAC_EINVALIDVALUE;
    rsbac_write_lock(&group_list_head.lock, &flags);
    item_p = lookup_group_item(id);
    if(item_p)
      {
        item_p->entry.type = type;
        item_p->entry.owner = owner;
        strncpy(item_p->entry.name, name, RSBAC_ACL_GROUP_NAMELEN);
        item_p->entry.name[RSBAC_ACL_GROUP_NAMELEN-1] = 0;
      }
    else
      err = -RSBAC_ENOTFOUND;
    rsbac_write_unlock(&group_list_head.lock, &flags);
    return(err);
  }

int rsbac_acl_remove_group(rsbac_acl_group_id_t id)
  {
           u_long                  flags;
           int                     err=0;
    struct rsbac_acl_gm_list_item_t * gm_item_p;

    if(!id)
      return -RSBAC_EINVALIDVALUE;
    rsbac_write_lock(&group_list_head.lock, &flags);
    if(lookup_group_item(id))
      {
        remove_group_item(id);
        group_list_head.dirty = TRUE;
        rsbac_write_unlock(&group_list_head.lock, &flags);
        /* cleanup group memberships */
        rsbac_write_lock(&gm_list_head.lock, &flags);
        gm_item_p = gm_list_head.head;
        while(gm_item_p)
          {
            if(lookup_gm_subitem(gm_item_p, id))
              {
                remove_gm_subitem(gm_item_p, id);
                gm_list_head.dirty = TRUE;
              }
            gm_item_p = gm_item_p->next;
          }
        rsbac_write_unlock(&gm_list_head.lock, &flags);
      }
    else
      rsbac_write_unlock(&group_list_head.lock, &flags);
    return(err);
  }

int rsbac_acl_get_group_entry(rsbac_acl_group_id_t group, struct rsbac_acl_group_entry_t * entry_p)
  {
           u_long                        flags;
           int                           err=0;
    struct rsbac_acl_group_list_item_t * item_p;

    if(!group)
      return -RSBAC_EINVALIDVALUE;
    if(!entry_p)
      return -RSBAC_EINVALIDPOINTER;
    rsbac_read_lock(&group_list_head.lock, &flags);
    item_p = lookup_group_item(group);
    if(item_p)
      {
        entry_p->id = item_p->entry.id;
        entry_p->owner = item_p->entry.owner;
        entry_p->type = item_p->entry.type;
        strncpy(entry_p->name, item_p->entry.name, RSBAC_ACL_GROUP_NAMELEN);
      }
    else
      err = -RSBAC_ENOTFOUND;
    rsbac_read_unlock(&group_list_head.lock, &flags);
    return(err);
  }

int rsbac_acl_list_groups(rsbac_uid_t owner,
                          boolean include_global,
                          struct rsbac_acl_group_entry_t entry_array[],
                          int maxnum)
  {
           u_long                        flags;
           int                           err=0;
    struct rsbac_acl_group_list_item_t * item_p;

    if(maxnum <= 0)
      return -RSBAC_EINVALIDVALUE;
    if(!entry_array)
      return -RSBAC_EINVALIDPOINTER;
    rsbac_read_lock(&group_list_head.lock, &flags);
    item_p = group_list_head.head;
    while(item_p && (err < maxnum))
      {
        if(   (item_p->entry.owner == owner)
           || (include_global && (item_p->entry.type == ACLG_GLOBAL))
          )
          {
            entry_array[err].id = item_p->entry.id;
            entry_array[err].owner = item_p->entry.owner;
            entry_array[err].type = item_p->entry.type;
            strncpy(entry_array[err].name, item_p->entry.name, RSBAC_ACL_GROUP_NAMELEN);
            err++;
          }
        item_p = item_p->next;
      }
    rsbac_read_unlock(&group_list_head.lock, &flags);
    return(err);
  }

/* check group existence */
boolean rsbac_acl_group_exist(rsbac_acl_group_id_t group)
  {
           u_long                        flags;
           boolean                       result = FALSE;

    if(!group)
      return TRUE;
    rsbac_read_lock(&group_list_head.lock, &flags);
    if(lookup_group_item(group))
      result = TRUE;
    rsbac_read_unlock(&group_list_head.lock, &flags);
    return(result);
  }

int rsbac_acl_add_group_member(rsbac_acl_group_id_t group, rsbac_uid_t user)
  {
           u_long                        flags, flags2;
           int                           err=0;
    struct rsbac_acl_gm_list_item_t * item_p;

    if(!group)
      return -RSBAC_EINVALIDVALUE;
    rsbac_read_lock(&group_list_head.lock, &flags);
    if(!lookup_group_item(group))
      {
        rsbac_read_unlock(&group_list_head.lock, &flags);
        return -RSBAC_EINVALIDVALUE;
      }
    rsbac_write_lock(&gm_list_head.lock, &flags2);
    item_p = lookup_gm_item(user);
    if(!item_p)
      {
        item_p = add_gm_item(user);
        if(!item_p)
          {
            err = -RSBAC_ECOULDNOTADDITEM;
            goto out_unlock;
          }
      }
    if(!lookup_gm_subitem(item_p, group))
      {
        if(!add_gm_subitem(item_p, group))
          {
            err = -RSBAC_ECOULDNOTADDITEM;
          }
        else
          {
            gm_list_head.dirty = TRUE;
          }
      }

out_unlock:    
    rsbac_write_unlock(&gm_list_head.lock, &flags2);
    rsbac_read_unlock(&group_list_head.lock, &flags);
    return(err);
  }

int rsbac_acl_remove_group_member(rsbac_acl_group_id_t group, rsbac_uid_t user)
  {
           u_long                        flags, flags2;
           int                           err=0;
    struct rsbac_acl_gm_list_item_t * item_p;

    if(!group)
      return -RSBAC_EINVALIDVALUE;
    rsbac_read_lock(&group_list_head.lock, &flags);
    if(!lookup_group_item(group))
      {
        rsbac_read_unlock(&group_list_head.lock, &flags);
        return -RSBAC_EINVALIDVALUE;
      }
    rsbac_write_lock(&gm_list_head.lock, &flags2);
    item_p = lookup_gm_item(user);
    if(!item_p)
      {
        goto out_unlock;
      }
    if(lookup_gm_subitem(item_p, group))
      {
        remove_gm_subitem(item_p, group);
        if(!item_p->sublist_length)
          remove_gm_item(item_p->user);
        gm_list_head.dirty = TRUE;
      }

out_unlock:    
    rsbac_write_unlock(&gm_list_head.lock, &flags2);
    rsbac_read_unlock(&group_list_head.lock, &flags);
    return(err);
  }

/* check membership */
boolean rsbac_acl_group_member(rsbac_acl_group_id_t group, rsbac_uid_t user)
  {
           u_long                        flags, flags2;
           boolean                       result = FALSE;
    struct rsbac_acl_gm_list_item_t * item_p;

    if(!group)
      return -RSBAC_EINVALIDVALUE;
    rsbac_read_lock(&group_list_head.lock, &flags);
    if(lookup_group_item(group))
      {
        rsbac_read_lock(&gm_list_head.lock, &flags2);
        item_p = lookup_gm_item(user);
        if(item_p && lookup_gm_subitem(item_p, group))
          {
            result = TRUE;
          }
        rsbac_read_unlock(&gm_list_head.lock, &flags2);
      }
    rsbac_read_unlock(&group_list_head.lock, &flags);
    return(result);
  }

/* build kmalloc'd array of all group memberships of the given user */
/* returns number of groups or negative error */
/* Attention: memory deallocation with kfree must be done by caller! */
int rsbac_acl_get_user_groups(rsbac_uid_t user, rsbac_acl_group_id_t ** group_pp)
  {
           u_long                        flags;
           int                           err=0;
    struct rsbac_acl_gm_list_item_t    * item_p;
           rsbac_acl_group_id_t        * group_p = NULL;

    rsbac_read_lock(&gm_list_head.lock, &flags);
    item_p = lookup_gm_item(user);
    if(item_p)
      {
        u_int buflen = sizeof(rsbac_acl_group_id_t) * item_p->sublist_length;

        group_p=(rsbac_acl_group_id_t *) kmalloc(buflen, GFP_KERNEL);
        if(group_p)
          {
            struct rsbac_acl_gm_sublist_item_t * subitem_p;

            subitem_p = item_p->sublist_head;
            while(subitem_p)
              {
                group_p[err] = subitem_p->group;
                err++;
                subitem_p = subitem_p->next;
              }
          }
        else
          err = -RSBAC_ENOMEM;
      }
    rsbac_read_unlock(&gm_list_head.lock, &flags);
    *group_pp = group_p;
    return(err);
  }

/* Returns number of members or negative error */
int rsbac_acl_get_group_members(rsbac_acl_group_id_t group, rsbac_uid_t user_array[], int maxnum)
  {
           u_long                        flags;
           int                           err=0;
    struct rsbac_acl_gm_list_item_t    * item_p;

    if(!group || (maxnum <= 0))
      return -RSBAC_EINVALIDVALUE;
    rsbac_read_lock(&group_list_head.lock, &flags);
    if(!lookup_group_item(group))
      {
        rsbac_read_unlock(&group_list_head.lock, &flags);
        return -RSBAC_EINVALIDVALUE;
      }
    rsbac_read_unlock(&group_list_head.lock, &flags);
    rsbac_read_lock(&gm_list_head.lock, &flags);
    item_p = gm_list_head.head;
    while(item_p && (err < maxnum))
      {
        if(lookup_gm_subitem(item_p, group))
          {
            user_array[err] = item_p->user;
            err++;
          }
        item_p = item_p->next;
      }
    rsbac_read_unlock(&gm_list_head.lock, &flags);
    return(err);
  }

/********************************************/
/* remove user from all groups and all ACLs */
int rsbac_acl_remove_user(rsbac_uid_t user)
  {
           int                     err=0;
    struct rsbac_acl_group_list_item_t * group_item_p;
    struct rsbac_acl_fd_list_item_t  * fd_item_p;
    struct rsbac_acl_dev_list_item_t * dev_item_p;
    struct rsbac_acl_scd_list_item_t * scd_item_p;
    u_int                              i;
    u_long flags,dflags;
    struct rsbac_acl_device_list_item_t   * device_p;

    rsbac_write_lock(&gm_list_head.lock, &flags);
    if(lookup_gm_item(user))
      {
        remove_gm_item(user);
        gm_list_head.dirty = TRUE;
      }
    rsbac_write_lock(&group_list_head.lock, &dflags);
    group_item_p = group_list_head.head;
    while(group_item_p)
      {
        if(group_item_p->entry.owner == user)
          {
            struct rsbac_acl_gm_list_item_t    * gm_item_p;
            rsbac_acl_group_id_t group;

            group = group_item_p->entry.id;
            group_item_p = group_item_p->next;
            remove_group_item(group);
            group_list_head.dirty = TRUE;
            /* cleanup group memberships */
            gm_item_p = gm_list_head.head;
            while(gm_item_p)
              {
                if(lookup_gm_subitem(gm_item_p, group))
                  {
                    remove_gm_subitem(gm_item_p, group);
                    gm_list_head.dirty = TRUE;
                  }
                gm_item_p = gm_item_p->next;
              }
          }
        else
          group_item_p = group_item_p->next;
      }
    rsbac_write_unlock(&group_list_head.lock, &dflags);
    rsbac_write_unlock(&gm_list_head.lock, &flags);

    /* protect default_fd list */
    rsbac_write_lock(&default_fd_lock, &flags);
    if(lookup_fd_subitem(&default_fd_acl, ACLS_USER, user))
      {
        remove_fd_subitem(&default_fd_acl, ACLS_USER, user);
        default_fd_dirty = TRUE;
      }
    /* unprotect the list */
    rsbac_write_unlock(&default_fd_lock, &flags);

    /* protect default_dev list */
    rsbac_write_lock(&default_dev_lock, &flags);
    if(lookup_dev_subitem(&default_dev_acl, ACLS_USER, user))
      {
        remove_dev_subitem(&default_dev_acl, ACLS_USER, user);
        default_dev_dirty = TRUE;
      }
    /* unprotect the list */
    rsbac_write_unlock(&default_dev_lock, &flags);

    /* protect default_ipc_list */
    rsbac_write_lock(&default_ipc_lock, &flags);
    if(lookup_ipc_subitem(&default_ipc_acl, ACLS_USER, user))
      {
        remove_ipc_subitem(&default_ipc_acl, ACLS_USER, user);
        default_ipc_dirty = TRUE;
      }
    /* unprotect the list */
    rsbac_write_unlock(&default_ipc_lock, &flags);

    /* protect default_scd_list */
    rsbac_write_lock(&default_scd_lock, &flags);
    if(lookup_scd_subitem(&default_scd_acl, ACLS_USER, user))
      {
        remove_scd_subitem(&default_scd_acl, ACLS_USER, user);
        default_scd_dirty = TRUE;
      }
    /* unprotect the list */
    rsbac_write_unlock(&default_scd_lock, &flags);

    /* protect default_u_list */
    rsbac_write_lock(&default_u_lock, &flags);
    if(lookup_u_subitem(&default_u_acl, ACLS_USER, user))
      {
        remove_u_subitem(&default_u_acl, ACLS_USER, user);
        default_u_dirty = TRUE;
      }
    /* unprotect the list */
    rsbac_write_unlock(&default_u_lock, &flags);

    /* protect default_p list */
    rsbac_write_lock(&default_p_lock, &flags);
    if(lookup_p_subitem(&default_p_acl, ACLS_USER, user))
      {
        remove_p_subitem(&default_p_acl, ACLS_USER, user);
        default_p_dirty = TRUE;
      }
    /* unprotect the list */
    rsbac_write_unlock(&default_p_lock, &flags);

    /* protect device list */
    rsbac_read_lock(&device_list_head.lock, &dflags);
    device_p = device_list_head.head;
    while(device_p)
      {
        for(i=0; i<RSBAC_ACL_NR_FD_LISTS; i++)
          {
            /* protect file list */
            rsbac_write_lock(&device_p->head_table[i].lock, &flags);
            fd_item_p = device_p->head_table[i].head;
            while (fd_item_p)
              {
                if(lookup_fd_subitem(fd_item_p, ACLS_USER, user))
                  {
                    remove_fd_subitem(fd_item_p, ACLS_USER, user);
                    device_p->head_table[i].dirty = TRUE;
                  }
                fd_item_p = fd_item_p->next;
              };
            /* unprotect the list */
            rsbac_write_unlock(&device_p->head_table[i].lock, &flags);
          }
        device_p = device_p->next;
      }
    /* unprotect device list */
    rsbac_read_unlock(&device_list_head.lock, &dflags);

    /* protect dev list */
    rsbac_write_lock(&dev_list_head.lock, &flags);
    dev_item_p = dev_list_head.head;
    while (dev_item_p)
      {
        if(lookup_dev_subitem(dev_item_p, ACLS_USER, user))
          {
            remove_dev_subitem(dev_item_p, ACLS_USER, user);
            dev_list_head.dirty = TRUE;
          }
        dev_item_p = dev_item_p->next;
      };
    /* unprotect the list */
    rsbac_write_unlock(&dev_list_head.lock, &flags);

    /* protect scd list */
    rsbac_write_lock(&scd_list_head.lock, &flags);
    scd_item_p = scd_list_head.head;
    while (scd_item_p)
      {
        if(lookup_scd_subitem(scd_item_p, ACLS_USER, user))
          {
            remove_scd_subitem(scd_item_p, ACLS_USER, user);
            scd_list_head.dirty = TRUE;
          }
        scd_item_p = scd_item_p->next;
      };
    /* unprotect the list */
    rsbac_write_unlock(&scd_list_head.lock, &flags);

    return(err);
  }

/* end of acl_data_structures.c */
