/*************************************************** */
/* Rule Set Based Access Control                     */
/* Implementation of AUTH data structures            */
/* Author and (c) 1999-2001: Amon Ott <ao@rsbac.org> */
/*                                                   */
/* Last modified: 31/Jul/2001                        */
/*************************************************** */

#include <linux/types.h>
#include <linux/sched.h>
#include <linux/mm.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/auth_data_structures.h>
#include <rsbac/error.h>
#include <rsbac/helpers.h>
#include <rsbac/adf.h>
#include <rsbac/aci.h>
#include <rsbac/auth.h>
#include <rsbac/proc_fs.h>
#include <rsbac/rkmem.h>
#include <rsbac/getname.h>
#include <linux/string.h>
#include <linux/smp_lock.h>

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

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

static struct rsbac_auth_device_list_head_t      device_list_head;
static struct rsbac_auth_p_cap_set_list_head_t   p_cap_set_list_head;

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

boolean writable(struct super_block * sb_p);

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

static struct rsbac_auth_cap_set_sublist_item_t * 
         add_f_cap_set_subitem(struct rsbac_auth_f_cap_set_list_item_t * set,
                                      rsbac_uid_t first_id,
                                      rsbac_uid_t last_id);

static struct rsbac_auth_f_cap_set_list_item_t *
  lookup_f_cap_set(struct rsbac_auth_device_list_item_t * device_p,
                          rsbac_auth_f_cap_set_id_t cap_set);

static struct rsbac_auth_f_cap_set_list_item_t* 
         add_f_cap_set_item(struct rsbac_auth_device_list_item_t * device_p,
                                   rsbac_auth_f_cap_set_id_t id);

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

/* 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_auth(), the write       */
/* function is called on every change from all functions that modify data.  */

/* read_f_cap_set_list() */
/* reading a device AUTH list from filesystem */

static int read_f_cap_set_list(struct rsbac_auth_device_list_item_t * device_p,
                               kdev_t kdev)
  {
    struct file                     file;
    int                             err = 0;
    u_int                           i;
    int                             tmperr;
    u_long                          read_count = 0;
    rsbac_auth_f_cap_set_id_t       id;
    rsbac_uid_t                     sub_first;
    rsbac_uid_t                     sub_last;
    rsbac_old_uid_t                 old_sub_id;
    u_int                           len;
    rsbac_version_t                 aci_version;
    mm_segment_t oldfs;

    if(!device_p)
      return(-RSBAC_EINVALIDPOINTER);

    /* open file */
    if ((err = rsbac_read_open (RSBAC_AUTH_F_CAP_FILENAME,
                                &file,
                                kdev) ))
      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
               "read_f_cap_set_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_AUTH_F_CAP_SET_VERSION:
          break;
        case RSBAC_AUTH_OLD_F_CAP_SET_VERSION:
          printk(KERN_WARNING
                 "read_f_cap_set_list(): old version %u on device %02u:%02u, new is %u - upconverting!\n",
                 aci_version,
                 MAJOR(kdev),
                 MINOR(kdev),
                 RSBAC_AUTH_F_CAP_SET_VERSION);
          break;
        default:
          printk(KERN_WARNING
                 "read_f_cap_set_list(): wrong version %u on device %02u:%02u, expected %u - skipping file and setting no_write for DEV list!\n",
                 aci_version,
                 MAJOR(kdev),
                 MINOR(kdev),
                 RSBAC_AUTH_F_CAP_SET_VERSION);
          device_p->no_write = TRUE;
          err = -RSBAC_EREADFAILED;
          goto end_read;
      }
    do
      {
        tmperr = file.f_op->read(&file,
                                 (char *) &id,
                                 sizeof(id),
                                 &file.f_pos);
        /* if successful, add item without writing to disk */
        if (tmperr > 0)
          {
            if(!add_f_cap_set_item(device_p,id))
              {
                printk(KERN_WARNING
                       "read_f_cap_set_list(): could not add item %u!\n",
                       id);
                tmperr = 0;
                goto end_read;
              }
            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++)
                  {
                    switch(aci_version)
                      {
                        case RSBAC_AUTH_F_CAP_SET_VERSION:
                          tmperr = file.f_op->read(&file,
                                                  (char *) &sub_first,
                                                  sizeof(sub_first),
                                                  &file.f_pos);
                          if(tmperr>0)
                            tmperr = file.f_op->read(&file,
                                                     (char *) &sub_last,
                                                     sizeof(sub_last),
                                                     &file.f_pos);
                          break;
                        case RSBAC_AUTH_OLD_F_CAP_SET_VERSION:
                          old_sub_id = 0;
                          tmperr = file.f_op->read(&file,
                                                  (char *) &old_sub_id,
                                                  sizeof(old_sub_id),
                                                  &file.f_pos);
                          switch(old_sub_id)
                            {
                              case RSBAC_AUTH_OLD_OWNER_F_CAP:
                                sub_first = RSBAC_AUTH_OWNER_F_CAP;
                                sub_last = RSBAC_AUTH_OWNER_F_CAP;
                                break;
                              default:
                                sub_first = old_sub_id;
                                sub_last = old_sub_id;
                            }
                          break;
                      }
                    if(tmperr > 0)
                      {
                        if (!add_f_cap_set_subitem(lookup_f_cap_set(device_p, id),
                                                   sub_first,
                                                   sub_last))
                          {
                            printk(KERN_WARNING
                                   "read_f_cap_set_list(): could not add sub-item %u:%u for id %u on device %02u:%02u!\n",
                                   sub_first,
                                   sub_last,
                                   id,
                                   MAJOR(kdev),
                                   MINOR(kdev));
                            i = len;
                            tmperr = -1;
                          }
                      }
                    else
                      {
                        i = len;
                        tmperr = -1;
                      }
                  }
              }
          }
      }
    while (tmperr > 0); /* end of do */

    if (tmperr < 0)
      {
        printk(KERN_WARNING "read_f_cap_set_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);

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

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

#ifndef CONFIG_RSBAC_NO_WRITE
static int write_f_cap_set_list(struct rsbac_auth_device_list_item_t * device_p)
  {
    struct file                     file;
    int                             err = 0;
    struct rsbac_auth_f_cap_set_list_item_t * current_p = NULL;
    struct rsbac_auth_cap_set_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_AUTH_F_CAP_SET_VERSION;
    char          * buffer = NULL;
    boolean         vmalloc_used = FALSE;

    if(!device_p)
      return(-RSBAC_EINVALIDPOINTER);
    /* test no_write */
    if(device_p->no_write)
      return(-RSBAC_ENOTWRITABLE);
    /* protect this list */
    rsbac_read_lock(&device_p->list_head.lock, &flags);
    /* calculate buflen */
    buflen = device_p->list_head.count *
              (sizeof(current_p->id) /* one set id each */
               + sizeof(int) )       /* sublist len */
             + sizeof(aci_version);
    current_p = device_p->list_head.head;
    while (current_p) 
      {
        buflen += current_p->sublist_length
         * (sizeof(sub_item_p->first_id) + sizeof(sub_item_p->last_id));
        current_p = current_p->next;
      }
    /* try to rsbac_vkmalloc */
    buffer = rsbac_vkmalloc(buflen, &vmalloc_used);
    if(!buffer)
      {
        /* unprotect this list */
        rsbac_read_unlock(&device_p->list_head.lock, &flags);
        return(-RSBAC_ENOMEM);
      }
    current_p = device_p->list_head.head;
    /* copy version */
    memcpy(buffer,
           (char *) &aci_version,
           sizeof(aci_version));
    write_count = sizeof(aci_version);
    /* copy list */
    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->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->first_id),
                   sizeof(sub_item_p->first_id));
            write_count += sizeof(sub_item_p->first_id);
            memcpy(buffer+write_count,
                   (char *) &(sub_item_p->last_id),
                   sizeof(sub_item_p->last_id));
            write_count += sizeof(sub_item_p->last_id);
            sub_item_p = sub_item_p->next;
          }
        current_p = current_p->next;
      }
    /* unprotect this list */
    rsbac_read_unlock(&device_p->list_head.lock, &flags);

    /* get rsbac write-to-disk semaphore */
    down(&rsbac_write_sem);
    
    /* open file */
    if ((err = rsbac_write_open(RSBAC_AUTH_F_CAP_FILENAME,
                                &file,
                                device_p->id) ))
      {
        up(&rsbac_write_sem);
        /* free buffer */
        rsbac_vkfree(buffer, vmalloc_used);
        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 = rsbac_min(write_count - written, RSBAC_MAX_WRITE_CHUNK);
        tmperr = file.f_op->write(&file,
                                  buffer+written,
	                          bytes,
	                          &file.f_pos);
        if(tmperr > 0)
          {
            written += tmperr;
          }
      }
    if (tmperr < 0)
      {
        printk(KERN_WARNING
               "write_f_cap_set_list(): write error %i on file %s on device %02u:%02u!\n",
               tmperr,
               RSBAC_AUTH_F_CAP_FILENAME,
               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);

#ifdef CONFIG_RSBAC_DEBUG
    if (rsbac_debug_write)
      printk(KERN_DEBUG "write_f_cap_set_list(): %u main lists written to device %02u:%02u.\n",
             device_p->list_head.count,
             MAJOR(device_p->id),
             MINOR(device_p->id));
#endif
    /* End of write access                     */
    rsbac_write_close(&file);
    /* free overall sem */
    up(&rsbac_write_sem);
    /* free buffer */
    rsbac_vkfree(buffer, vmalloc_used);
    return(err);
  }; /* end of write_f_cap_set_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_auth_device_list_item_t * lookup_device(kdev_t kdev)
    {
      struct rsbac_auth_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;
        }
      /* it is the current item -> return it */
        return (curr);
    };

/* lookup_p_cap_set_subitem() */
/* lookup process cap set sub item, return pointer */
static struct rsbac_auth_cap_set_sublist_item_t *
  lookup_p_cap_set_subitem(struct rsbac_auth_p_cap_set_list_item_t * set,
                                      rsbac_uid_t first_id,
                                      rsbac_uid_t last_id)
    {
      struct rsbac_auth_cap_set_sublist_item_t  * curr;

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

/* lookup_f_cap_set_subitem() */
/* lookup file cap set sub item, return pointer */
static struct rsbac_auth_cap_set_sublist_item_t *
  lookup_f_cap_set_subitem(struct rsbac_auth_f_cap_set_list_item_t * set,
                                      rsbac_uid_t first_id,
                                      rsbac_uid_t last_id)
    {
      struct rsbac_auth_cap_set_sublist_item_t  * curr;

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

/* lookup_p_cap_set() */
/* lookup process cap set item */
static struct rsbac_auth_p_cap_set_list_item_t *
  lookup_p_cap_set(rsbac_auth_p_cap_set_id_t cap_set)
    {
      struct rsbac_auth_p_cap_set_list_item_t  * curr = p_cap_set_list_head.curr;
      
      /* is the current item the one we look for? yes -> return, else search */
      if (curr && (curr->id == cap_set) )
        return (curr);
      else
        curr = p_cap_set_list_head.head;
        while (curr && (curr->id != cap_set) )
          curr = curr->next;

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

/* lookup_f_cap_set() */
/* lookup file cap set item */
static struct rsbac_auth_f_cap_set_list_item_t *
  lookup_f_cap_set(struct rsbac_auth_device_list_item_t * device_p,
                          rsbac_auth_f_cap_set_id_t cap_set)
    {
      struct rsbac_auth_f_cap_set_list_item_t  * curr;

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

        if (curr)
          device_p->list_head.curr=curr;
        return (curr);
    };


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

/* Create a device item without adding to list. No locking needed. */
static struct rsbac_auth_device_list_item_t 
          * create_device_item(kdev_t kdev)
    {
      struct rsbac_auth_device_list_item_t * new_item_p;

      /* allocate memory for new device, return NULL, if failed */
      if ( !(new_item_p = (struct rsbac_auth_device_list_item_t *)
                    rsbac_kmalloc(sizeof(*new_item_p)) ) )
         return(NULL);
         
      new_item_p->id = kdev;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
      new_item_p->mount_count = 1;
#endif
      new_item_p->no_write = FALSE;

      /* init file/dir sublists */
      new_item_p->list_head.lock = RW_LOCK_UNLOCKED;
      new_item_p->list_head.head = NULL;
      new_item_p->list_head.tail = NULL;
      new_item_p->list_head.curr = NULL;
      new_item_p->list_head.dirty = FALSE;
      new_item_p->list_head.count = 0;
        
      return(new_item_p);
    };

/* Add an existing device item to list. Locking needed. */
static struct rsbac_auth_device_list_item_t 
          * add_device_item(struct rsbac_auth_device_list_item_t * device_p)
    {
      if (!device_p)
         return(NULL);
         
      /* add new device to device list */
      if (!device_list_head.head)
        { /* first device */
          device_list_head.head=device_p;
          device_list_head.tail=device_p;
          device_list_head.curr=device_p;
          device_list_head.count=1;
          device_p->prev=NULL;
          device_p->next=NULL;
        }  
      else
        { /* there is another device -> hang to tail */
          device_p->prev=device_list_head.tail;
          device_p->next=NULL;
          device_list_head.tail->next=device_p;
          device_list_head.tail=device_p;
          device_list_head.curr=device_p;
          device_list_head.count++;
        };
      return(device_p);
    };

static struct rsbac_auth_cap_set_sublist_item_t * 
         add_p_cap_set_subitem(struct rsbac_auth_p_cap_set_list_item_t * set,
                                      rsbac_uid_t first_id,
                                      rsbac_uid_t last_id)
    {
      struct rsbac_auth_cap_set_sublist_item_t * new_item_p = NULL;

      if(!set)
        return(NULL);      
      if ( !(new_item_p = (struct rsbac_auth_cap_set_sublist_item_t *)
                 rsbac_kmalloc(sizeof(*new_item_p))) )
        return(NULL);
      new_item_p->first_id = first_id;
      new_item_p->last_id = last_id;

      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_auth_cap_set_sublist_item_t * 
         add_f_cap_set_subitem(struct rsbac_auth_f_cap_set_list_item_t * set,
                                      rsbac_uid_t first_id,
                                      rsbac_uid_t last_id)
    {
      struct rsbac_auth_cap_set_sublist_item_t * new_item_p = NULL;
      struct rsbac_auth_cap_set_sublist_item_t * tmp_p = NULL;

      if(!set)
        return(NULL);      
      if ( !(new_item_p = (struct rsbac_auth_cap_set_sublist_item_t *)
                 rsbac_kmalloc(sizeof(*new_item_p))) )
        return(NULL);
      new_item_p->first_id = first_id;
      new_item_p->last_id = last_id;

      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
        {
          tmp_p = set->sublist_head;
          if(tmp_p->first_id > first_id)
            { /* new item becomes first */
              new_item_p->prev=NULL;
              new_item_p->next=tmp_p;
              tmp_p->prev = new_item_p;
              set->sublist_head=new_item_p;
            }
          else
            { /* search for place */
              while(   tmp_p->next
                    && (tmp_p->next->first_id < first_id)
                   )
                tmp_p = tmp_p->next;
              /* insert behind */
              new_item_p->prev=tmp_p;
              new_item_p->next=tmp_p->next;
              tmp_p->next = new_item_p;
              if(!new_item_p->next)
                set->sublist_tail=new_item_p;
              else
                new_item_p->next->prev = new_item_p;
            }
          set->sublist_length++;
        };
      return(new_item_p);
    };


static struct rsbac_auth_p_cap_set_list_item_t* 
         add_p_cap_set_item(rsbac_auth_p_cap_set_id_t id)
    {
      struct rsbac_auth_p_cap_set_list_item_t * new_item_p = NULL;

      if ( !(new_item_p = (struct rsbac_auth_p_cap_set_list_item_t *)
                 rsbac_kmalloc(sizeof(*new_item_p))) )
        return(NULL);
      new_item_p->id = id;
      new_item_p->sublist_length = 0;
      new_item_p->sublist_head = NULL;
      new_item_p->sublist_tail = NULL;
      
      if (!p_cap_set_list_head.head)
        {
          p_cap_set_list_head.head=new_item_p;
          p_cap_set_list_head.tail=new_item_p;
          p_cap_set_list_head.curr=new_item_p;
          p_cap_set_list_head.count = 1;
          new_item_p->prev=NULL;
          new_item_p->next=NULL;          
        }  
      else
        {
          new_item_p->prev=p_cap_set_list_head.tail;
          new_item_p->next=NULL;
          p_cap_set_list_head.tail->next=new_item_p;
          p_cap_set_list_head.tail=new_item_p;
          p_cap_set_list_head.curr=new_item_p;
          p_cap_set_list_head.count++;
        };
      return(new_item_p);
    };

static struct rsbac_auth_f_cap_set_list_item_t* 
         add_f_cap_set_item(struct rsbac_auth_device_list_item_t * device_p,
                            rsbac_auth_f_cap_set_id_t id)
    {
      struct rsbac_auth_f_cap_set_list_item_t * new_item_p = NULL;

      if(!device_p)
        return NULL;
      if ( !(new_item_p = (struct rsbac_auth_f_cap_set_list_item_t *)
                 rsbac_kmalloc(sizeof(*new_item_p))) )
        return(NULL);
      new_item_p->id = id;
      new_item_p->sublist_length = 0;
      new_item_p->sublist_head = NULL;
      new_item_p->sublist_tail = NULL;
      
      if (!device_p->list_head.head)
        {
          device_p->list_head.head=new_item_p;
          device_p->list_head.tail=new_item_p;
          device_p->list_head.curr=new_item_p;
          device_p->list_head.count = 1;
          new_item_p->prev=NULL;
          new_item_p->next=NULL;          
        }  
      else
        {
          new_item_p->prev=device_p->list_head.tail;
          new_item_p->next=NULL;
          device_p->list_head.tail->next=new_item_p;
          device_p->list_head.tail=new_item_p;
          device_p->list_head.curr=new_item_p;
          device_p->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_p_cap_set_subitem(struct rsbac_auth_p_cap_set_list_item_t * set,
                                            rsbac_uid_t first_id,
                                            rsbac_uid_t last_id)
    {
      struct rsbac_auth_cap_set_sublist_item_t * item_p;
    
      /* first we must locate the item. */
      if (set && (item_p = lookup_p_cap_set_subitem(set, first_id,last_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 */
          rsbac_kfree(item_p);    
        };  /* end of if: item was found */

    }; /* end of remove_p_cap_set_subitem() */

static void remove_f_cap_set_subitem(struct rsbac_auth_f_cap_set_list_item_t * set,
                                            rsbac_uid_t first_id,
                                            rsbac_uid_t last_id)
    {
      struct rsbac_auth_cap_set_sublist_item_t * item_p;
    
      /* first we must locate the item. */
      if (set && (item_p = lookup_f_cap_set_subitem(set, first_id, last_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 */
          rsbac_kfree(item_p);    
        };  /* end of if: item was found */

    }; /* end of remove_f_cap_set_subitem() */


static void
  remove_all_p_cap_set_subitems(struct rsbac_auth_p_cap_set_list_item_t * set_p)
    {
      struct rsbac_auth_cap_set_sublist_item_t * sub_item_p;
      struct rsbac_auth_cap_set_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;
              rsbac_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_p_cap_set_subitems() */

static void
  remove_all_f_cap_set_subitems(struct rsbac_auth_f_cap_set_list_item_t * set_p)
    {
      struct rsbac_auth_cap_set_sublist_item_t * sub_item_p;
      struct rsbac_auth_cap_set_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;
              rsbac_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_f_cap_set_subitems() */


static void remove_p_cap_set_item(rsbac_auth_p_cap_set_id_t id)
    {
      struct rsbac_auth_p_cap_set_list_item_t * item_p;
      struct rsbac_auth_cap_set_sublist_item_t * sub_item_p;
      struct rsbac_auth_cap_set_sublist_item_t * next_sub_item_p;
    
      /* first we must locate the item. */
      if ( (item_p = lookup_p_cap_set(id)) )
        { /* ok, item was found */
          if ( (p_cap_set_list_head.head == item_p) )
             { /* item is head */
               if ( (p_cap_set_list_head.tail == item_p) )
                 { /* item is head and tail = only item -> list will be empty*/
                   p_cap_set_list_head.head = NULL;
                   p_cap_set_list_head.tail = NULL;
                 }
               else
                 { /* item is head, but not tail -> next item becomes head */
                   item_p->next->prev = NULL;
                   p_cap_set_list_head.head = item_p->next;
                 };
             }
          else
             { /* item is not head */
               if ( (p_cap_set_list_head.tail == item_p) )
                 { /*item is not head, but tail -> previous item becomes tail*/
                   item_p->prev->next = NULL;
                   p_cap_set_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 */
          p_cap_set_list_head.curr=NULL;
          /* adjust count */
          p_cap_set_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;
              rsbac_kfree(sub_item_p);
              sub_item_p = next_sub_item_p;
            }
          rsbac_kfree(item_p);    
        };  /* end of if: item was found */

    }; /* end of remove_p_cap_set_item() */

static void remove_f_cap_set_item(struct rsbac_auth_device_list_item_t * device_p,
                                  rsbac_auth_f_cap_set_id_t id)
    {
      struct rsbac_auth_f_cap_set_list_item_t * item_p;
      struct rsbac_auth_cap_set_sublist_item_t * sub_item_p;
      struct rsbac_auth_cap_set_sublist_item_t * next_sub_item_p;
    
      /* first we must locate the item. */
      if ( (item_p = lookup_f_cap_set(device_p,id)) )
        { /* ok, item was found */
          if ( (device_p->list_head.head == item_p) )
             { /* item is head */
               if ( (device_p->list_head.tail == item_p) )
                 { /* item is head and tail = only item -> list will be empty*/
                   device_p->list_head.head = NULL;
                   device_p->list_head.tail = NULL;
                 }
               else
                 { /* item is head, but not tail -> next item becomes head */
                   item_p->next->prev = NULL;
                   device_p->list_head.head = item_p->next;
                 };
             }
          else
             { /* item is not head */
               if ( (device_p->list_head.tail == item_p) )
                 { /*item is not head, but tail -> previous item becomes tail*/
                   item_p->prev->next = NULL;
                   device_p->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_p->list_head.curr=NULL;
          /* adjust count */
          device_p->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;
              rsbac_kfree(sub_item_p);
              sub_item_p = next_sub_item_p;
            }
          rsbac_kfree(item_p);    
        };  /* end of if: item was found */

    }; /* end of remove_f_cap_set_item() */

static void clear_device_item(struct rsbac_auth_device_list_item_t * item_p)
    {
      struct rsbac_auth_f_cap_set_list_item_t * cap_item_p;
      struct rsbac_auth_f_cap_set_list_item_t * cap_item_p2;

      if(!item_p)
        return;

      /* First clean up sublists... */
      cap_item_p = item_p->list_head.head;
      while (cap_item_p)
        {
          cap_item_p2 = cap_item_p->next;
          item_p->list_head.curr = cap_item_p;
          remove_f_cap_set_item(item_p, cap_item_p->id);
          cap_item_p = cap_item_p2;
        }
      /* OK, lets remove the device item itself */
      rsbac_kfree(item_p);
    }; /* end of clear_device_item() */

static void remove_device_item(kdev_t kdev)
    {
      struct rsbac_auth_device_list_item_t    * item_p;

      /* first we must locate the item. */
      if ( (item_p = lookup_device(kdev)) )
        { /* ok, item was found */
          if (device_list_head.head == item_p)  
             { /* item is head */
               if (device_list_head.tail == item_p)
                 { /* item is head and tail = only item -> list will be empty*/
                   device_list_head.head = NULL;
                   device_list_head.tail = NULL;
                 }
               else
                 { /* item is head, but not tail -> next item becomes head */
                   item_p->next->prev = NULL;
                   device_list_head.head = item_p->next;
                 };
             }
          else
             { /* item is not head */
               if (device_list_head.tail == item_p)
                 { /*item is not head, but tail -> previous item becomes tail*/
                   item_p->prev->next = NULL;
                   device_list_head.tail = item_p->prev;
                 }
               else
                 { /* item is neither head nor tail -> item is cut out */
                   item_p->prev->next = item_p->next;
                   item_p->next->prev = item_p->prev;
                 };
             };
             
          /* curr is no longer valid -> reset.                              */
          device_list_head.curr=NULL;
          /* adjust counter */
          device_list_head.count--;
          
          /* now we can remove the item from memory. This means cleaning up */
          /* everything below. */
          clear_device_item(item_p);
        };  /* end of if: item was found */

    }; /* end of remove_device_item() */

/************************************************************************** */
/* The copy_fp_cap_set_item() function copies a file cap set to a process   */
/* cap set */

static int copy_fp_cap_set_item(struct rsbac_auth_device_list_item_t * device_p,
                                rsbac_auth_f_cap_set_id_t fid,
                                rsbac_auth_p_cap_set_id_t pid)
    {
      struct rsbac_auth_p_cap_set_list_item_t  * p_item_p;
      struct rsbac_auth_f_cap_set_list_item_t  * f_item_p;
      struct rsbac_auth_cap_set_sublist_item_t * sub_item_p;

      /* locate the file item. */
      if (!(f_item_p = lookup_f_cap_set(device_p,fid)) )
        { /* f_item was not found -> remove process set */
          remove_p_cap_set_item(pid);
          return(0);
        }
      /* locate the process item. */
      if (!(p_item_p = lookup_p_cap_set(pid)) )
        { /* p_item cannot be found -> add */
          if (!(p_item_p = add_p_cap_set_item(pid)) )
            return(-RSBAC_ECOULDNOTADDITEM);
        }
      else
        {
          /* clear old p_set */
          remove_all_p_cap_set_subitems(p_item_p);
        }
      /* copy */
      sub_item_p = f_item_p->sublist_head;
      while(sub_item_p)
        {
          add_p_cap_set_subitem(p_item_p,
                                sub_item_p->first_id,
                                sub_item_p->last_id);
          sub_item_p = sub_item_p->next;
        }
      return(0);
    }; /* end of copy_fp_cap_set_item() */

/************************************************************************** */
/* The copy_pp_cap_set_item() function copies a process cap set to another  */

static int copy_pp_cap_set_item(rsbac_auth_p_cap_set_id_t old_pid,
                                rsbac_auth_p_cap_set_id_t new_pid)
    {
      struct rsbac_auth_p_cap_set_list_item_t  * old_p_item_p;
      struct rsbac_auth_p_cap_set_list_item_t  * new_p_item_p;
      struct rsbac_auth_cap_set_sublist_item_t * sub_item_p;
    
      /* locate the old process item. */
      if (!(old_p_item_p = lookup_p_cap_set(old_pid)) )
        { /* old_p_item was not found -> remove new_p_item too and return */
          remove_p_cap_set_item(new_pid);
          return(0);
        }
      /* There is an old_p_item -> locate the new process item. */
      if ((new_p_item_p = lookup_p_cap_set(new_pid)) )
        { /* new_p_item was found -> clear */
          remove_all_p_cap_set_subitems(new_p_item_p);
        }
      else
        {
          new_p_item_p = add_p_cap_set_item(new_pid);
          if(!new_p_item_p)
            return(-RSBAC_ECOULDNOTADDITEM);
        }
      /* copy */
      sub_item_p = old_p_item_p->sublist_head;
      while(sub_item_p)
        {
          add_p_cap_set_subitem(new_p_item_p,
                                sub_item_p->first_id,
                                sub_item_p->last_id);
          sub_item_p = sub_item_p->next;
        }
      return(0);
    }; /* end of copy_pp_cap_set_item() */

/************************************************************ */
/* The get_f_caplist() function copies a cap set to an array  */

static int
  get_f_caplist(struct rsbac_auth_f_cap_set_list_item_t * set_p,
                       rsbac_uid_t caplist[],
                       int maxnum)
    {
      struct rsbac_auth_cap_set_sublist_item_t * sub_item_p;
      int count=0;
    
      /* first we must locate the item. */
      if (set_p && (maxnum > 0))
        { /* ok, valid set pointer */
          /* copy all subitems */
          sub_item_p = set_p->sublist_head;
          while(sub_item_p && (count < maxnum))
            {
              caplist[2*count] = sub_item_p->first_id;
              caplist[2*count+1] = sub_item_p->last_id;
              count++;
              sub_item_p = sub_item_p->next;
            }
        };  /* end of if: item was found */
      return(count);
    }; /* end of get_f_caplist() */


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

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

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

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

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

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

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

    union rsbac_target_id_t       rsbac_target_id;
    union rsbac_attribute_value_t rsbac_attribute_value;

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

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

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

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

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

	p = k_buf + 8 + 9;

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

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

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

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

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
static int
stats_auth_proc_info(char *buffer, char **start, off_t offset, int length, int dummy)
#else
static int
stats_auth_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_auth_p_cap_set_list_item_t * p_cap_set_item_p;
    struct rsbac_auth_f_cap_set_list_item_t * f_cap_set_item_p;
    u_int                                     cap_set_count = 0;
    u_int                                     member_count = 0;
    u_long flags,dflags;
    struct rsbac_auth_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_auth_proc_info(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
#ifdef CONFIG_RSBAC_DEBUG
    if (rsbac_debug_aef_auth)
      printk(KERN_DEBUG "stats_auth_proc_info(): calling ADF\n");
#endif
    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, "AUTH Status\n-----------\n");

    /* protect process cap set list */
    rsbac_read_lock(&p_cap_set_list_head.lock, &flags);
    p_cap_set_item_p = p_cap_set_list_head.head;
    while (p_cap_set_item_p)
      {
        cap_set_count++;
        member_count += p_cap_set_item_p->sublist_length;
        p_cap_set_item_p = p_cap_set_item_p->next;
      };
    /* unprotect the list */
    rsbac_read_unlock(&p_cap_set_list_head.lock, &flags);
    len += sprintf(buffer + len, "%u process cap set items, sum of %u members\n",
                   cap_set_count,member_count);
    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 */
        cap_set_count = 0;
        member_count = 0;
        /* protect file cap set list */
        rsbac_read_lock(&device_p->list_head.lock, &flags);
        f_cap_set_item_p = device_p->list_head.head;
        while (f_cap_set_item_p)
          {
            cap_set_count++;
            member_count += f_cap_set_item_p->sublist_length;
            f_cap_set_item_p = f_cap_set_item_p->next;
          };
        /* unprotect the list */
        rsbac_read_unlock(&device_p->list_head.lock, &flags);
        if(device_p->list_head.dirty)
          {
            len += sprintf(buffer + len, "device %02u:%02u has %u file cap set items, sum of %u members, list is dirty\n",
                           MAJOR(device_p->id),
                           MINOR(device_p->id),
                           cap_set_count,member_count);
          }
        else
          {
            len += sprintf(buffer + len, "device %02u:%02u has %u file cap set items, sum of %u members, list is clean\n",
                           MAJOR(device_p->id),
                           MINOR(device_p->id),
                           cap_set_count,member_count);
          }
        pos = begin + len;
        if (pos < offset)
          {
            len = 0;
            begin = pos;
          }
        if (pos > offset+length)
          goto out_unlock;
        device_p = device_p->next;
      }
out_unlock:
    /* unprotect device list */
    rsbac_read_unlock(&device_list_head.lock, &dflags);

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
auth_caplist_proc_info(char *buffer, char **start, off_t offset, int length, int dummy)
#else
static int
auth_caplist_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_auth_p_cap_set_list_item_t  * p_cap_set_item_p;
    struct rsbac_auth_f_cap_set_list_item_t  * f_cap_set_item_p;
    struct rsbac_auth_cap_set_sublist_item_t * cap_set_sub_item_p;
    u_int                                      count = 0;
    u_int                                      member_count = 0;
    u_long flags,dflags;
    struct rsbac_auth_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 "auth_caplist_proc_info(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
#ifdef CONFIG_RSBAC_DEBUG
    if (rsbac_debug_aef_auth)
      printk(KERN_DEBUG "auth_caplist_proc_info(): calling ADF\n");
#endif
    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, "AUTH Cap Lists\n--------------\n");

    /* protect process cap set list */
    rsbac_read_lock(&p_cap_set_list_head.lock, &flags);
    p_cap_set_item_p = p_cap_set_list_head.head;
    member_count = 0;
    len += sprintf(buffer + len, "Process capabilities:\nset-id  count   cap-members");
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    while (p_cap_set_item_p)
      {
        count++;
        len += sprintf(buffer + len, "\n %u\t%u\t",
                       p_cap_set_item_p->id,
                       p_cap_set_item_p->sublist_length);
        pos = begin + len;
        if (pos < offset)
          {
            len = 0;
            begin = pos;
          }
        if (pos > offset+length)
          goto out;
        cap_set_sub_item_p = p_cap_set_item_p->sublist_head;
        while (cap_set_sub_item_p)
          {
            member_count++;
            if(cap_set_sub_item_p->first_id != cap_set_sub_item_p->last_id)
              len += sprintf(buffer + len, "%u:%u ",
                             cap_set_sub_item_p->first_id,
                             cap_set_sub_item_p->last_id);
            else
              len += sprintf(buffer + len, "%u ",
                             cap_set_sub_item_p->first_id);
            pos = begin + len;
            if (pos < offset)
              {
                len = 0;
                begin = pos;
              }
            if (pos > offset+length)
              goto out;
            cap_set_sub_item_p = cap_set_sub_item_p->next;
          };
        p_cap_set_item_p = p_cap_set_item_p->next;
      };
    /* unprotect the list */
    rsbac_read_unlock(&p_cap_set_list_head.lock, &flags);
    len += sprintf(buffer + len, "\n\n%u process cap set 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;

    len += sprintf(buffer + len, "\nFile capabilities:\nset-id  count   cap-members");
    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;
        /* protect file cap set list */
        rsbac_read_lock(&device_p->list_head.lock, &flags);
        f_cap_set_item_p = device_p->list_head.head;
        while (f_cap_set_item_p)
          {
            count++;
            len += sprintf(buffer + len, "\n%u\t%u\t",
                           f_cap_set_item_p->id,
                           f_cap_set_item_p->sublist_length);
            pos = begin + len;
            if (pos < offset)
              {
                len = 0;
                begin = pos;
              }
            if (pos > offset+length)
              goto out_funlock;
            cap_set_sub_item_p = f_cap_set_item_p->sublist_head;
            while (cap_set_sub_item_p)
              {
                member_count++;
                if(cap_set_sub_item_p->first_id != cap_set_sub_item_p->last_id)
                  len += sprintf(buffer + len, "%u:%u ",
                                 cap_set_sub_item_p->first_id,
                                 cap_set_sub_item_p->last_id);
                else
                  len += sprintf(buffer + len, "%u ",
                                 cap_set_sub_item_p->first_id);
                pos = begin + len;
                if (pos < offset)
                  {
                    len = 0;
                    begin = pos;
                  }
                if (pos > offset+length)
                  goto out_funlock;
                cap_set_sub_item_p = cap_set_sub_item_p->next;
              };
            f_cap_set_item_p = f_cap_set_item_p->next;
          };
        /* unprotect the list */
        rsbac_read_unlock(&device_p->list_head.lock, &flags);
        if(device_p->list_head.dirty)
          {
            len += sprintf(buffer + len, "\ndevice %02u:%02u has %u file cap set items, sum of %u members, list is dirty\n",
                           MAJOR(device_p->id),
                           MINOR(device_p->id),
                           count,member_count);
          }
        else
          {
            len += sprintf(buffer + len, "\ndevice %02u:%02u has %u file cap set items, sum of %u members, list is clean\n",
                           MAJOR(device_p->id),
                           MINOR(device_p->id),
                           count,member_count);
          }
        pos = begin + len;
        if (pos < offset)
          {
            len = 0;
            begin = pos;
          }
        if (pos > offset+length)
          goto out_unlock;
        device_p = device_p->next;
      }
out_unlock:
    /* unprotect device list */
    rsbac_read_unlock(&device_list_head.lock, &dflags);

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

out_funlock:
  /* unprotect the list */
  rsbac_read_unlock(&device_p->list_head.lock, &flags);
  goto out_unlock;
}
#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 AUTH data structures. After this call, all AUTH    */
/* 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_auth() will initialize all rw-spinlocks to unlocked.          */

int __init rsbac_init_auth(void)
  {
    int  err = 0;
    struct rsbac_auth_device_list_item_t * device_p = NULL;
    u_long dflags;
    struct proc_dir_entry * tmp_entry_p;

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

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

    /* Init lists */
    p_cap_set_list_head.lock = RW_LOCK_UNLOCKED;
    p_cap_set_list_head.head = NULL;
    p_cap_set_list_head.tail = NULL;
    p_cap_set_list_head.curr = NULL;
    p_cap_set_list_head.count = 0;
    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;

    /* read all data */
#ifdef CONFIG_RSBAC_DEBUG
    if (rsbac_debug_ds_auth)
      printk(KERN_DEBUG "rsbac_init_auth(): Reading data\n");
#endif
    device_p = create_device_item(rsbac_root_dev);
    if (!device_p)
      {
        printk(KERN_CRIT "rsbac_init_auth(): Could not add device!\n");
        return(-RSBAC_ECOULDNOTADDDEVICE);
      }
    if((err = read_f_cap_set_list(device_p,rsbac_root_dev)))
      {
        if(err == -RSBAC_ENOTFOUND)
          printk(KERN_WARNING
                 "rsbac_init_auth(): No file capabilities on dev %02u:%02u.\n",
                 MAJOR(rsbac_root_dev), MINOR(rsbac_root_dev));
        else
          {
            char * tmp = rsbac_kmalloc(RSBAC_MAXNAMELEN);

            if(tmp)
              {
                printk(KERN_WARNING
                       "rsbac_init_auth(): File capabilities not fully read from dev %02u:%02u, error %s!\n",
                       MAJOR(rsbac_root_dev), MINOR(rsbac_root_dev),
                       get_error_name(tmp, err));
                rsbac_kfree(tmp);
              }
          }
      }
    /* wait for write access to device_list_head */
    rsbac_write_lock(&device_list_head.lock, &dflags);
    device_p = add_device_item(device_p);
    /* device was added, allow access */
    rsbac_write_unlock(&device_list_head.lock, &dflags);
    if (!device_p)
      {
        printk(KERN_CRIT "rsbac_init_auth(): Could not add device!\n");
        return(-RSBAC_ECOULDNOTADDDEVICE);
      }

    #if defined(CONFIG_RSBAC_PROC) && defined(CONFIG_PROC_FS)
    tmp_entry_p = create_proc_entry("auth_devices",
                                    S_IFREG | S_IRUGO | S_IWUGO,
                                    proc_rsbac_root_p);
    if(tmp_entry_p)
      {
        tmp_entry_p->get_info = auth_devices_proc_info;
        tmp_entry_p->write_proc = auth_devices_proc_write;
      }
    tmp_entry_p = create_proc_entry("stats_auth",
                                    S_IFREG | S_IRUGO,
                                    proc_rsbac_root_p);
    if(tmp_entry_p)
      {
        tmp_entry_p->get_info = stats_auth_proc_info;
      }
    tmp_entry_p = create_proc_entry("auth_caplist",
                                    S_IFREG | S_IRUGO,
                                    proc_rsbac_root_p);
    if(tmp_entry_p)
      {
        tmp_entry_p->get_info = auth_caplist_proc_info;
      }
    #endif

#ifdef CONFIG_RSBAC_DEBUG
    if (rsbac_debug_ds_auth)
      printk(KERN_DEBUG "rsbac_init_auth(): Ready.\n");
#endif
    return(err);
  };

int rsbac_mount_auth(kdev_t kdev)
  {
    int err = 0;
    struct rsbac_auth_device_list_item_t * device_p;
    struct rsbac_auth_device_list_item_t * new_device_p;
    u_long dflags;

    if (!rsbac_is_initialized())
      {
        printk(KERN_WARNING "rsbac_mount_auth(): RSBAC not initialized\n");
          return(-RSBAC_ENOTINITIALIZED);
      }
#ifdef CONFIG_RSBAC_DEBUG
    if (rsbac_debug_ds_auth)
      printk(KERN_DEBUG "rsbac_mount_auth(): mounting device %02u:%02u\n",
             MAJOR(kdev),MINOR(kdev));
#endif
    /* wait for write access to device_list_head */
    rsbac_read_lock(&device_list_head.lock, &dflags);
    device_p = lookup_device(kdev);
    /* repeated mount? */
    if(device_p)
      {
        #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
        printk(KERN_WARNING "rsbac_mount_auth: repeated mount of device %02u:%02u\n",
               MAJOR(kdev), MINOR(kdev));
	#else
        printk(KERN_WARNING "rsbac_mount_auth: repeated mount %u of device %02u:%02u\n",
               device_p->mount_count, MAJOR(kdev), MINOR(kdev));
	device_p->mount_count++;
	#endif
        rsbac_read_unlock(&device_list_head.lock, &dflags);
        return 0;
      }
    rsbac_read_unlock(&device_list_head.lock, &dflags);

    new_device_p = create_device_item(kdev);
    if(!new_device_p)
      return -RSBAC_ECOULDNOTADDDEVICE;

    /* There can be no /rsbac dir on non-device mounts (MAJOR 0), so do not even try */
    if(MAJOR(kdev))
      {
        /* block writing */
        down(&rsbac_write_sem);
        /* read attributes */
        err = read_f_cap_set_list(new_device_p, kdev);
        #ifdef CONFIG_RSBAC_DEBUG
        if(err && rsbac_debug_ds_auth)
          {
            if(err == -RSBAC_ENOTFOUND)
              printk(KERN_WARNING
                     "rsbac_init_auth(): No file capabilities on dev %02u:%02u.\n",
                     MAJOR(kdev), MINOR(kdev));
            else
              {
                char * tmp = rsbac_kmalloc(RSBAC_MAXNAMELEN);

                if(tmp)
                  {
                    printk(KERN_WARNING
                           "rsbac_init_auth(): File capabilities not fully read from dev %02u:%02u, error %s!\n",
                           MAJOR(kdev), MINOR(kdev),
                           get_error_name(tmp, err));
                    rsbac_kfree(tmp);
                  }
              }
          }
        #endif
        /* allow writing */
        up(&rsbac_write_sem);
      }

    /* wait for read access to device_list_head */
    rsbac_read_lock(&device_list_head.lock, &dflags);
    /* make sure to only add, if this device item has not been added in the meantime */
    device_p = lookup_device(kdev);
    if(device_p)
      {
        printk(KERN_WARNING
               "rsbac_mount_auth(): mount race for device %02u:%02u detected!\n",
               MAJOR(kdev), MINOR(kdev));
        #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
	device_p->mount_count++;
	#endif
        rsbac_read_unlock(&device_list_head.lock, &dflags);
	clear_device_item(new_device_p);
      }
    else
      {
        rsbac_read_unlock(&device_list_head.lock, &dflags);
        rsbac_write_lock(&device_list_head.lock, &dflags);
        device_p = add_device_item(new_device_p);
        rsbac_write_unlock(&device_list_head.lock, &dflags);
        if(!device_p)
          {
            printk(KERN_WARNING "rsbac_mount_auth: adding device %02u:%02u failed!\n",
                   MAJOR(kdev), MINOR(kdev));
            clear_device_item(new_device_p);
            err = -RSBAC_ECOULDNOTADDDEVICE;
          }
      }
    return(err);
  };
  
/* When umounting a device, its file cap set list must be removed. */

int rsbac_umount_auth(kdev_t kdev)
  {
    u_long flags;
    struct rsbac_auth_device_list_item_t * device_p;

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

#ifdef CONFIG_RSBAC_DEBUG
    if (rsbac_debug_ds)
      printk(KERN_DEBUG "rsbac_umount_auth(): umounting device %02u:%02u\n",
             MAJOR(kdev), MINOR(kdev));
#endif
    /* 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... */
    device_p = lookup_device(kdev);
    if(device_p)
      {
        #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
        if(device_p->mount_count == 1)
        #endif
          remove_device_item(kdev);
        #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
        else
          {
            if(device_p->mount_count > 1)
              {
                device_p->mount_count--;
              }
            else
              {
                printk(KERN_WARNING "rsbac_mount_auth: device %02u:%02u has mount_count < 1!\n",
                       MAJOR(kdev), MINOR(kdev));
              }
          }
        #endif
      }

    /* allow access */
    rsbac_write_unlock(&device_list_head.lock, &flags);
    return(0);
  };

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

int rsbac_stats_auth(void)
  {
    struct rsbac_auth_p_cap_set_list_item_t * p_cap_set_item_p;
    struct rsbac_auth_f_cap_set_list_item_t * f_cap_set_item_p;
    u_int                                     cap_set_count = 0;
    u_int                                     member_count = 0;
    u_long flags,dflags;
    struct rsbac_auth_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_auth(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
#ifdef CONFIG_RSBAC_DEBUG
    if (rsbac_debug_aef_auth)
      printk(KERN_DEBUG "rsbac_stats_auth(): calling ADF\n");
#endif
    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 "AUTH Status\n-----------\n");

    /* protect process cap set list */
    rsbac_read_lock(&p_cap_set_list_head.lock, &flags);
    p_cap_set_item_p = p_cap_set_list_head.head;
    member_count = 0;
    while (p_cap_set_item_p)
      {
        cap_set_count++;
        member_count += p_cap_set_item_p->sublist_length;
        p_cap_set_item_p = p_cap_set_item_p->next;
      };
    /* unprotect the list */
    rsbac_read_unlock(&p_cap_set_list_head.lock, &flags);
    printk(KERN_INFO "%u process cap set items, sum of %u members\n",
           cap_set_count,member_count);

    /* protect device list */
    rsbac_read_lock(&device_list_head.lock, &dflags);
    device_p = device_list_head.head;
    while(device_p)
      {
        /* reset counters */
        cap_set_count = 0;
        member_count = 0;
        /* protect file cap set list */
        rsbac_read_lock(&device_p->list_head.lock, &flags);
        f_cap_set_item_p = device_p->list_head.head;
        while (f_cap_set_item_p)
          {
            cap_set_count++;
            member_count += f_cap_set_item_p->sublist_length;
            f_cap_set_item_p = f_cap_set_item_p->next;
          };
        /* unprotect the list */
        rsbac_read_unlock(&device_p->list_head.lock, &flags);
        printk(KERN_INFO "device %02u:%02u has %u file cap set items, sum of %u members\n",
                         MAJOR(device_p->id),
                         MINOR(device_p->id),
                         cap_set_count,member_count);
        device_p = device_p->next;
      }
    /* unprotect device list */
    rsbac_read_unlock(&device_list_head.lock, &dflags);
    return(0);
  };

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

int rsbac_check_auth(int correct, int check_inode)
  {
    struct rsbac_auth_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;
    u_int                               f_dirty = 0, dirty = 0;
    u_long                              flags, dflags;
    struct super_block                * sb_p;
    struct inode                      * inode_p;
    struct rsbac_auth_f_cap_set_list_item_t * f_item_p, * f_item_tmp_p;
    struct rsbac_auth_p_cap_set_list_item_t  * p_item_p, * p_item_tmp_p;
    struct task_struct                * task_p;
    struct rsbac_auth_f_cap_set_list_head_t * head_p;
  
    if (!rsbac_is_initialized())
      {
        printk(KERN_WARNING "rsbac_check_auth(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }

    /* process list */
    rsbac_write_lock(&p_cap_set_list_head.lock, &flags);
    tmp_count = 0;
    p_item_p = p_cap_set_list_head.head;
    while(p_item_p)
      {
        read_lock(&tasklist_lock);
        task_p = find_task_by_pid(p_item_p->id);
        read_unlock(&tasklist_lock);
        if(!task_p)
          {
            p_item_tmp_p = p_item_p;
            p_item_p = p_item_p->next;
            if(correct)
              {
                printk(KERN_WARNING
                       "rsbac_check_auth(): process item has invalid id %u, removed!\n",
                       p_item_tmp_p->id);
                remove_p_cap_set_item(p_item_tmp_p->id);
                continue;
              }
            else
              {
                printk(KERN_WARNING
                       "rsbac_check_auth(): process item has invalid id %u!\n",
                       p_item_tmp_p->id);
              }
          }
        else
          {
            p_item_p = p_item_p->next;
          }
        tmp_count++;
      }
    if(tmp_count != p_cap_set_list_head.count)
      {
        printk(KERN_WARNING
               "rsbac_check_auth(): correcting count mismatch for process list - was %u, counted %lu!\n",
               p_cap_set_list_head.count, tmp_count);
        p_cap_set_list_head.count = tmp_count;
      }
    rsbac_write_unlock(&p_cap_set_list_head.lock, &flags);
    printk(KERN_INFO "rsbac_check_auth(): %u process-items\n",
                 p_cap_set_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_auth(): 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 = rsbac_get_super_block(device_p->id);
            if(!sb_p)
              {
                printk(KERN_WARNING "rsbac_check_auth(): 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_auth(): 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 */
        tmp_count = 0;
        head_p = &device_p->list_head;
        rsbac_write_lock(&head_p->lock, &flags);
        f_item_p = head_p->head;
        while(f_item_p)
          {
            /* check for inode on disk (but not for reiserfs, because of 64bit inode numbers) */
            #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
            if(sb_p)
            #else
            if(sb_p && !sb_p->s_op->read_inode2)
            #endif
              {
                inode_p = iget(sb_p, f_item_p->id);
                if(is_bad_inode(inode_p))
                  { /* inode is bad -> remove */
                    b_count++;
                    if(correct)
                      {
                        printk(KERN_INFO
                               "rsbac_check_auth(): f_item for bad inode %u on device %02u:%02u, removing!\n",
                               f_item_p->id, MAJOR(device_p->id), MINOR(device_p->id));
                        f_item_tmp_p = f_item_p;
                        f_item_p = f_item_p->next;
                        head_p->curr = f_item_tmp_p;
                        remove_f_cap_set_item(device_p, f_item_tmp_p->id);
                        head_p->dirty = TRUE;
                        continue;
                      }
                    else
                      {
                        printk(KERN_INFO
                               "rsbac_check_auth(): f_item for bad inode %u on device %02u:%02u!\n",
                               f_item_p->id, MAJOR(device_p->id), MINOR(device_p->id));
                      }
                  } /* 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_auth(): f_item for deleted inode %u on device %02u:%02u, removing!\n",
                                       f_item_p->id, MAJOR(device_p->id), MINOR(device_p->id));
                                f_item_tmp_p = f_item_p;
                                f_item_p = f_item_p->next;
                                head_p->curr = f_item_tmp_p;
                                remove_f_cap_set_item(device_p, f_item_tmp_p->id);
                                head_p->dirty = TRUE;
                                continue;
                              }
                            else
                              {
                                printk(KERN_INFO
                                       "rsbac_check_auth(): f_item for deleted inode %u on device %02u:%02u!\n",
                                       f_item_p->id, MAJOR(device_p->id), MINOR(device_p->id));
                              }
                          }
                        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_auth(): f_item for inode %u with nlink <= 0 on device %02u:%02u, removing!\n",
                                           f_item_p->id, MAJOR(device_p->id), MINOR(device_p->id));
                                    f_item_tmp_p = f_item_p;
                                    f_item_p = f_item_p->next;
                                    head_p->curr = f_item_tmp_p;
                                    remove_f_cap_set_item(device_p, f_item_tmp_p->id);
                                    head_p->dirty = TRUE;
                                    continue;
                                  }
                                else
                                  {
                                    printk(KERN_INFO
                                           "rsbac_check_auth(): deleted inode %u on device %02u:%02u, has no dtime!\n",
                                           f_item_p->id, MAJOR(device_p->id), MINOR(device_p->id));
                                  }
                              }
                          }
                      }
                  } /* end of is_good_inode */
                iput(inode_p);
              } /* end of sb_p */

            /* if all values for this item (apart from id) are default values,
               remove it */
            if(!f_item_p->sublist_length)
              {
                no_member_count++;
                if(correct)
                  {
#ifdef CONFIG_RSBAC_DEBUG
                    if(rsbac_debug_ds)
                      printk(KERN_DEBUG
                             "rsbac_check_auth(): f_item for inode %u on device %02u:%02u, has no set members, removing!\n",
                             f_item_p->id, MAJOR(device_p->id), MINOR(device_p->id));
#endif
                    f_item_tmp_p = f_item_p;
                    f_item_p = f_item_p->next;
                    remove_f_cap_set_item(device_p, f_item_tmp_p->id);
                    head_p->dirty = TRUE;
                    continue;
                  }
#ifdef CONFIG_RSBAC_DEBUG
                else
                  {
                    if(rsbac_debug_ds)
                      printk(KERN_DEBUG
                             "rsbac_check_auth(): f_item for inode %u on device %02u:%02u, has no set members!\n",
                             f_item_p->id, MAJOR(device_p->id), MINOR(device_p->id));
                  }
#endif
              }
            tmp_count++;
            f_item_p = f_item_p->next;
          }
        if(tmp_count != head_p->count)
          {
            printk(KERN_WARNING
                   "rsbac_check_auth(): correcting count mismatch for device %02u:%02u - was %u, counted %lu!\n",
                   MAJOR(device_p->id), MINOR(device_p->id), 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;
        switch(correct)
          {
            case 2:  printk(KERN_INFO
                            "rsbac_check_auth(): Device %02u:%02u has %lu fd-items (%lu removed (%lu bad inodes, %lu dtimed inodes, %lu unlinked inodes, %lu had no members)) 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_auth(): Device %02u:%02u has %lu fd-items (%lu removed (%lu bad inodes, %lu dtimed inodes, %lu had no members), %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_auth(): Device %02u:%02u has %lu fd-items (%lu with bad inodes, %lu with dtimed inodes, %lu unlinked inodes, %lu without members) 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_auth(): Sum of %u Devices with %lu fd-items\n",
                 device_list_head.count, f_sum);
    /* free access to device_list_head */
    rsbac_read_unlock(&device_list_head.lock, &dflags);
    
    printk(KERN_INFO
           "rsbac_check_auth(): Total of %lu registered auth items, %u lists dirty\n",
           f_sum + p_cap_set_list_head.count,
           dirty);
    return(0);
  };

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

#if defined(CONFIG_RSBAC_AUTO_WRITE)
int rsbac_write_auth(boolean need_lock)
  {
    struct rsbac_auth_device_list_item_t * device_p;
    int                                    err=0;
    u_int                                  count = 0;
    u_long                                 dflags;
  
    if (!rsbac_is_initialized())
      {
        printk(KERN_WARNING "rsbac_write_auth(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    /* wait for read access to device_list_head */
    rsbac_read_lock(&device_list_head.lock, &dflags);
    /* OK, go on */
/*    printk(KERN_INFO "rsbac_write_auth(): currently %u processes working on file/dir aci\n",
                     device_list_head.lock.lock); */
    device_p = device_list_head.head;
    while (device_p)
      {
        if(device_p->list_head.dirty)
          {
            if(need_lock)
              lock_kernel();
            device_p->list_head.dirty = FALSE;
            err=write_f_cap_set_list(device_p);
            if(need_lock)
              unlock_kernel();
            if(!err)
              {
                count++;
              }
            else
              {
                device_p->list_head.dirty = TRUE;
                if(err != -RSBAC_ENOTWRITABLE)
                  printk(KERN_WARNING
                         "rsbac_write_auth(): write_f_cap_set_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);

#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_write)
      printk(KERN_DEBUG "rsbac_write_auth(): %u file cap lists written\n",
             count);
#endif
    return(count);
  };
#endif

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

/* All these procedures handle the rw-spinlocks to protect the targets during */
/* access.                                                                  */
/* Trying to access a never created or removed set returns an error! */

/* rsbac_auth_add_to_capset */
/* Add a set member to a set sublist. Set behaviour: also returns success, */
/* if member was already in set! */

int rsbac_auth_add_to_p_capset(rsbac_auth_p_cap_set_id_t id,
                               rsbac_uid_t first_uid,
                               rsbac_uid_t last_uid)
  {
    int                                     err=0;
    struct rsbac_auth_p_cap_set_list_item_t * cap_set_p;
    u_long flags;

    if (!rsbac_is_initialized())
      {
        printk(KERN_WARNING "rsbac_auth_add_to_p_capset(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    if (in_interrupt())
      {
        printk(KERN_WARNING "rsbac_auth_add_to_p_capset(): called from interrupt!\n");
      }
    if(first_uid > last_uid)
      return(-RSBAC_EINVALIDVALUE);

    /* protect the list */
    rsbac_write_lock(&p_cap_set_list_head.lock, &flags);
    /* check, whether set-id exists */
    cap_set_p = lookup_p_cap_set(id);
    if (   !cap_set_p
        && !((cap_set_p = add_p_cap_set_item(id)))
       )
      {
        err = -RSBAC_ECOULDNOTADDITEM;
        goto out;
      }
    /* check for subitem, try to add subitem, if it does not exist, */
    /* and return error, if adding failed */
    if (   (!lookup_p_cap_set_subitem(cap_set_p,first_uid,last_uid))
        && (!add_p_cap_set_subitem(cap_set_p,first_uid,last_uid)))
      err = -RSBAC_ECOULDNOTADDITEM;
out:
    /* unprotect the list */
    rsbac_write_unlock(&p_cap_set_list_head.lock, &flags);
    return(err);
  }

int rsbac_auth_add_to_f_capset(rsbac_auth_file_t file,
                               rsbac_uid_t first_uid,
                               rsbac_uid_t last_uid)
  {
    int                                     err=0;
    struct rsbac_auth_f_cap_set_list_item_t * cap_set_p;
    u_long flags,dflags;
    struct rsbac_auth_device_list_item_t   * device_p;

    if (!rsbac_is_initialized())
      {
        printk(KERN_WARNING "rsbac_auth_add_to_f_capset(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    if (in_interrupt())
      {
        printk(KERN_WARNING "rsbac_auth_add_to_f_capset(): called from interrupt!\n");
      }
    if(first_uid > last_uid)
      return(-RSBAC_EINVALIDVALUE);

    /* protect device list */
    rsbac_read_lock(&device_list_head.lock, &dflags);
    device_p = lookup_device(file.device);
    if(!device_p)
      {
        printk(KERN_WARNING "rsbac_auth_add_to_f_capset(): invalid device %02u:%02u!\n",
               MAJOR(file.device),MINOR(file.device));
        rsbac_read_unlock(&device_list_head.lock, &dflags);
        return(-RSBAC_EINVALIDDEV);
      }
    /* protect the list */
    rsbac_write_lock(&device_p->list_head.lock, &flags);
    /* check, whether set-id exists, add, if not */
    cap_set_p = lookup_f_cap_set(device_p,file.inode);
    if (   !cap_set_p
        && !((cap_set_p = add_f_cap_set_item(device_p, file.inode)))
       )
      {
        err = -RSBAC_ECOULDNOTADDITEM;
        goto out;
      }
    /* check for subitem, try to add subitem, if it does not exist, */
    /* and return error, if adding failed */
    if (   (!lookup_f_cap_set_subitem(cap_set_p, first_uid, last_uid))
        && (!add_f_cap_set_subitem(cap_set_p, first_uid, last_uid)))
      err = -RSBAC_ECOULDNOTADDITEM;
    else
      { /* successful -> write to disk or mark dirty */
        device_p->list_head.dirty = TRUE;
      }
out:
    /* unprotect the lists */
    rsbac_write_unlock(&device_p->list_head.lock, &flags);
    rsbac_read_unlock(&device_list_head.lock, &dflags);
    return(err);
  }

/* rsbac_auth_remove_from_capset */
/* Remove a set member from a sublist. Set behaviour: Returns no error, if */
/* member is not in list.                                                  */

int rsbac_auth_remove_from_p_capset(rsbac_auth_p_cap_set_id_t id,
                                    rsbac_uid_t first_uid,
                                    rsbac_uid_t last_uid)
  {
    struct rsbac_auth_p_cap_set_list_item_t * cap_set_p;
    u_long flags;
    
    if (!rsbac_is_initialized())
      {
        printk(KERN_WARNING "rsbac_auth_remove_from_p_capset(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    if (in_interrupt())
      {
        printk(KERN_WARNING "rsbac_auth_remove_from_p_capset(): called from interrupt!\n");
      }
    if(first_uid > last_uid)
      return(-RSBAC_EINVALIDVALUE);

    /* protect the list */
    rsbac_write_lock(&p_cap_set_list_head.lock, &flags);
    /* check, whether set-id exists */
    cap_set_p = lookup_p_cap_set(id);
    if (cap_set_p)
      {
        remove_p_cap_set_subitem(cap_set_p, first_uid, last_uid);
        /* remove set, if empty */
        if(!cap_set_p->sublist_head)
          remove_p_cap_set_item(id);
      }
    /* unprotect the lists */
    rsbac_write_unlock(&p_cap_set_list_head.lock, &flags);
    return(0);
  }

int rsbac_auth_remove_from_f_capset(rsbac_auth_file_t file,
                                    rsbac_uid_t first_uid,
                                    rsbac_uid_t last_uid)
  {
    int                                    err=0;
    struct rsbac_auth_f_cap_set_list_item_t * cap_set_p;
    u_long flags,dflags;
    struct rsbac_auth_device_list_item_t   * device_p;

    if (!rsbac_is_initialized())
      {
        printk(KERN_WARNING "rsbac_auth_remove_from_f_capset(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    if (in_interrupt())
      {
        printk(KERN_WARNING "rsbac_auth_remove_from_f_capset(): called from interrupt!\n");
      }
    if(first_uid > last_uid)
      return(-RSBAC_EINVALIDVALUE);

    /* protect device list */
    rsbac_read_lock(&device_list_head.lock, &dflags);
    device_p = lookup_device(file.device);
    if(!device_p)
      {
        printk(KERN_WARNING "rsbac_auth_remove_from_f_capset(): invalid device %02u:%02u!\n",
               MAJOR(file.device),MINOR(file.device));
        rsbac_read_unlock(&device_list_head.lock, &dflags);
        return(-RSBAC_EINVALIDDEV);
      }
    /* protect the list */
    rsbac_write_lock(&device_p->list_head.lock, &flags);
    /* check, whether set-id exists */
    cap_set_p = lookup_f_cap_set(device_p,file.inode);
    if (cap_set_p)
      { /* remove */
        remove_f_cap_set_subitem(cap_set_p, first_uid, last_uid);
        /* remove set, if empty */
        if(!cap_set_p->sublist_head)
          remove_f_cap_set_item(device_p, file.inode);
        /* successful -> write to disk or mark dirty */
        device_p->list_head.dirty = TRUE;
      }
    /* unprotect the lists */
    rsbac_write_unlock(&device_p->list_head.lock, &flags);
    rsbac_read_unlock(&device_list_head.lock, &dflags);
    return(err);
  }

/* rsbac_auth_clear_capset */
/* Remove all set members from a sublist. Set behaviour: Returns no error, */
/* if list is empty.                                                       */

int rsbac_auth_clear_p_capset(rsbac_auth_p_cap_set_id_t id)
  {
    int                                    err=0;
    struct rsbac_auth_p_cap_set_list_item_t * cap_set_p;
    u_long flags;
    
    if (!rsbac_is_initialized())
      {
        printk(KERN_WARNING "rsbac_auth_clear_p_capset(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    if (in_interrupt())
      {
        printk(KERN_WARNING "rsbac_auth_clear_p_capset(): called from interrupt!\n");
      }
    /* protect the list */
    rsbac_write_lock(&p_cap_set_list_head.lock, &flags);
    /* check, whether set-id exists */
    cap_set_p = lookup_p_cap_set(id);
    if (cap_set_p)
      remove_all_p_cap_set_subitems(cap_set_p);
    /* unprotect the list */
    rsbac_write_unlock(&p_cap_set_list_head.lock, &flags);
    return(err);
  }

int rsbac_auth_clear_f_capset(rsbac_auth_file_t file)
  {
    int                                    err=0;
    struct rsbac_auth_f_cap_set_list_item_t * cap_set_p;
    u_long flags,dflags;
    struct rsbac_auth_device_list_item_t   * device_p;

    if (!rsbac_is_initialized())
      {
        printk(KERN_WARNING "rsbac_auth_clear_f_capset(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    if (in_interrupt())
      {
        printk(KERN_WARNING "rsbac_auth_clear_f_capset(): called from interrupt!\n");
      }
    /* protect device list */
    rsbac_read_lock(&device_list_head.lock, &dflags);
    device_p = lookup_device(file.device);
    if(!device_p)
      {
        printk(KERN_WARNING "rsbac_auth_clear_f_capset(): invalid device %02u:%02u!\n",
               MAJOR(file.device),MINOR(file.device));
        rsbac_read_unlock(&device_list_head.lock, &dflags);
        return(-RSBAC_EINVALIDDEV);
      }
    /* protect the list */
    rsbac_write_lock(&device_p->list_head.lock, &flags);
    /* check, whether set-id exists */
    cap_set_p = lookup_f_cap_set(device_p,file.inode);
    if (cap_set_p)
      { /* remove */
        remove_all_f_cap_set_subitems(cap_set_p);
        /* successful -> write to disk or mark dirty */
        device_p->list_head.dirty = TRUE;
      }
    /* unprotect the lists */
    rsbac_write_unlock(&device_p->list_head.lock, &flags);
    rsbac_read_unlock(&device_list_head.lock, &dflags);
    return(err);
  }

/* rsbac_auth_capset_member */
/* Return truth value, whether member is in set */

boolean  rsbac_auth_p_capset_member(rsbac_auth_p_cap_set_id_t id,
                                    rsbac_uid_t member)
  {
    boolean                                   res=FALSE;
    struct rsbac_auth_p_cap_set_list_item_t * cap_set_p;
    struct rsbac_auth_cap_set_sublist_item_t * sub_item_p;
    u_long flags;

    if (!rsbac_is_initialized())
      {
        printk(KERN_WARNING "rsbac_auth_p_capset_member(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    if (in_interrupt())
      {
        printk(KERN_WARNING "rsbac_auth_p_capset_member(): called from interrupt!\n");
      }
    /* protect the list */
    rsbac_read_lock(&p_cap_set_list_head.lock, &flags);
    /* check, whether set-id exists */
    cap_set_p = lookup_p_cap_set(id);
    if (cap_set_p)
      {
        sub_item_p = cap_set_p->sublist_head;
        while(sub_item_p)
          {
            if (   (member >= sub_item_p->first_id)
                && (member <= sub_item_p->last_id)
               )
              {
                res = TRUE;
                break;
              }
            sub_item_p = sub_item_p->next;
          }
      }
    /* unprotect the list */
    rsbac_read_unlock(&p_cap_set_list_head.lock, &flags);
    return(res);
  }

boolean  rsbac_auth_f_capset_member(rsbac_auth_file_t file,
                                    rsbac_uid_t member)
  {
    boolean                                   res = FALSE;
    struct rsbac_auth_f_cap_set_list_item_t * cap_set_p;
    struct rsbac_auth_cap_set_sublist_item_t * sub_item_p;
    u_long flags,dflags;
    struct rsbac_auth_device_list_item_t   * device_p;

    if (!rsbac_is_initialized())
      {
        printk(KERN_WARNING "rsbac_auth_f_capset_member(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    if (in_interrupt())
      {
        printk(KERN_WARNING "rsbac_auth_f_capset_member(): called from interrupt!\n");
      }
    /* protect device list */
    rsbac_read_lock(&device_list_head.lock, &dflags);
    device_p = lookup_device(file.device);
    if(!device_p)
      {
        printk(KERN_WARNING "rsbac_auth_f_capset_member(): invalid device %02u:%02u!\n",
               MAJOR(file.device),MINOR(file.device));
        rsbac_read_unlock(&device_list_head.lock, &dflags);
        return(-RSBAC_EINVALIDDEV);
      }
    /* protect the list */
    rsbac_read_lock(&device_p->list_head.lock, &flags);
    /* check, whether set-id exists */
    cap_set_p = lookup_f_cap_set(device_p,file.inode);
    if (cap_set_p)
      {
        sub_item_p = cap_set_p->sublist_head;
        while(sub_item_p)
          {
            if (   (member >= sub_item_p->first_id)
                && (member <= sub_item_p->last_id)
               )
              {
                res = TRUE;
                break;
              }
            sub_item_p = sub_item_p->next;
          }
      }
    /* unprotect the lists */
    rsbac_read_unlock(&device_p->list_head.lock, &flags);
    rsbac_read_unlock(&device_list_head.lock, &dflags);
    return(res);
  }

/* rsbac_auth_remove_capset */
/* Remove a full set. For cleanup, if object is deleted. */
/* To empty an existing set use rsbac_auth_clear_capset. */

int rsbac_auth_remove_p_capset(rsbac_auth_p_cap_set_id_t id)
  {
    u_long flags;

    if (!rsbac_is_initialized)
      {
        printk(KERN_WARNING "rsbac_auth_remove_p_capset(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    if (in_interrupt())
      {
        printk(KERN_WARNING "rsbac_auth_remove_p_capset(): called from interrupt!\n");
      }
#ifdef CONFIG_RSBAC_DEBUG
    if (rsbac_debug_ds_auth)
      printk(KERN_DEBUG
             "rsbac_auth_remove_capset(): Removing process cap set data\n");
#endif
    /* protect the list */
    rsbac_write_lock(&p_cap_set_list_head.lock, &flags);
    /* call the remove function */
    remove_p_cap_set_item(id);
    /* we are ready, so unprotect the list */
    rsbac_write_unlock(&p_cap_set_list_head.lock, &flags);
    return(0);
  }

int rsbac_auth_remove_f_capset(rsbac_auth_file_t file)
  {
    u_long flags,dflags;
    struct rsbac_auth_device_list_item_t * device_p;

    if (!rsbac_is_initialized)
      {
        printk(KERN_WARNING "rsbac_auth_remove_f_capset(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    if (in_interrupt())
      {
        printk(KERN_WARNING "rsbac_auth_remove_f_capset(): called from interrupt!\n");
      }
#ifdef CONFIG_RSBAC_DEBUG
    if (rsbac_debug_ds_auth)
      printk(KERN_DEBUG
             "rsbac_auth_remove_capset(): Removing file cap set data\n");
#endif
    /* protect device list */
    rsbac_read_lock(&device_list_head.lock, &dflags);
    device_p = lookup_device(file.device);
    if(!device_p)
      {
        printk(KERN_WARNING "rsbac_auth_remove_f_capset(): invalid device %02u:%02u!\n",
               MAJOR(file.device),MINOR(file.device));
        rsbac_read_unlock(&device_list_head.lock, &dflags);
        return(-RSBAC_EINVALIDDEV);
      }
    /* protect the list */
    rsbac_write_lock(&device_p->list_head.lock, &flags);
    /* call the remove function */
    if(lookup_f_cap_set(device_p,file.inode))
      {
        remove_f_cap_set_item(device_p,file.inode);
        /* successful -> write to disk or only mark dirty */
        device_p->list_head.dirty = TRUE;
      }
    /* unprotect the lists */
    rsbac_write_unlock(&device_p->list_head.lock, &flags);
    rsbac_read_unlock(&device_list_head.lock, &dflags);
    return(0);
  }

int rsbac_auth_copy_fp_capset(rsbac_auth_file_t    file,
                              rsbac_auth_p_cap_set_id_t p_cap_set_id)
  {
    u_long flags,dflags,pflags;
    struct rsbac_auth_device_list_item_t * device_p;
    int err=0;

    if (!rsbac_is_initialized)
      {
        printk(KERN_WARNING "rsbac_auth_copy_fp_capset(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    if (in_interrupt())
      {
        printk(KERN_WARNING "rsbac_auth_copy_fp_capset(): called from interrupt!\n");
      }
#ifdef CONFIG_RSBAC_DEBUG
    if (rsbac_debug_ds_auth)
      printk(KERN_DEBUG
             "rsbac_auth_copy_fp_capset(): Copying file cap set data to process cap set\n");
#endif
    /* protect device list */
    rsbac_read_lock(&device_list_head.lock, &dflags);
    device_p = lookup_device(file.device);
    if(!device_p)
      {
        printk(KERN_WARNING "rsbac_auth_copy_fp_capset(): invalid device %02u:%02u!\n",
               MAJOR(file.device),MINOR(file.device));
        rsbac_read_unlock(&device_list_head.lock, &dflags);
        return(-RSBAC_EINVALIDDEV);
      }
    /* protect the set lists */
    rsbac_read_lock(&device_p->list_head.lock, &flags);
    rsbac_write_lock(&p_cap_set_list_head.lock, &pflags);
    /* call the copy function */
    err = copy_fp_cap_set_item(device_p,file.inode,p_cap_set_id);
    /* unprotect the lists */
    rsbac_write_unlock(&p_cap_set_list_head.lock, &pflags);
    rsbac_read_unlock(&device_p->list_head.lock, &flags);
    rsbac_read_unlock(&device_list_head.lock, &dflags);
    return(err);
  }

int rsbac_auth_copy_pp_capset(rsbac_auth_p_cap_set_id_t old_p_set_id,
                              rsbac_auth_p_cap_set_id_t new_p_set_id)
  {
    u_long pflags;
    int err=0;

    if (!rsbac_is_initialized)
      {
        printk(KERN_WARNING "rsbac_auth_copy_pp_capset(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    if (in_interrupt())
      {
        printk(KERN_WARNING "rsbac_auth_copy_pp_capset(): called from interrupt!\n");
      }
#ifdef CONFIG_RSBAC_DEBUG
    if (rsbac_debug_ds_auth)
      printk(KERN_DEBUG
             "rsbac_auth_copy_pp_capset(): Copying process cap set data to process cap set\n");
#endif
    /* protect the set list */
    rsbac_write_lock(&p_cap_set_list_head.lock, &pflags);
    /* call the copy function */
    err = copy_pp_cap_set_item(old_p_set_id,new_p_set_id);
    /* unprotect the lists */
    rsbac_write_unlock(&p_cap_set_list_head.lock, &pflags);
    return(err);
  }

int rsbac_auth_get_f_caplist(rsbac_auth_file_t file,
                             rsbac_uid_t caplist[],
                             int maxnum)
  {
    u_long flags,dflags;
    struct rsbac_auth_device_list_item_t * device_p;
    struct rsbac_auth_f_cap_set_list_item_t * cap_set_p;

    if (!rsbac_is_initialized)
      {
        printk(KERN_WARNING "rsbac_auth_get_f_caplist(): RSBAC not initialized\n");
        return(-RSBAC_ENOTINITIALIZED);
      }
    if (in_interrupt())
      {
        printk(KERN_WARNING "rsbac_auth_get_f_caplist(): called from interrupt!\n");
      }
#ifdef CONFIG_RSBAC_DEBUG
    if (rsbac_debug_ds_auth)
      printk(KERN_DEBUG
             "rsbac_auth_get_f_caplist(): Getting file cap set list\n");
#endif
    /* protect device list */
    rsbac_read_lock(&device_list_head.lock, &dflags);
    device_p = lookup_device(file.device);
    if(!device_p)
      {
        printk(KERN_WARNING "rsbac_auth_get_f_caplist(): invalid device %02u:%02u!\n",
               MAJOR(file.device),MINOR(file.device));
        rsbac_read_unlock(&device_list_head.lock, &dflags);
        return(-RSBAC_EINVALIDDEV);
      }
    /* protect the list */
    rsbac_read_lock(&device_p->list_head.lock, &flags);
    /* lookup set */
    if((cap_set_p = lookup_f_cap_set(device_p,file.inode)))
      {
        maxnum = get_f_caplist(cap_set_p,caplist,maxnum);
      }
    else
      {
        maxnum = 0;
      }
    /* unprotect the lists */
    rsbac_read_unlock(&device_p->list_head.lock, &flags);
    rsbac_read_unlock(&device_list_head.lock, &dflags);
    return(maxnum);
  }

/* end of auth_data_structures.c */
