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

#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/lists.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 rsbac_list_handle_t process_handle = NULL;
#ifdef CONFIG_RSBAC_AUTH_DAC_OWNER
static rsbac_list_handle_t process_eff_handle = NULL;
static rsbac_list_handle_t process_fs_handle = NULL;
#endif

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

boolean writable(struct super_block * sb_p);

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

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

static inline int fd_hash(rsbac_inode_nr_t inode)
  {
    return(inode % RSBAC_AUTH_NR_CAP_FD_LISTS);
  }
#ifdef CONFIG_RSBAC_AUTH_DAC_OWNER
static inline int eff_fd_hash(rsbac_inode_nr_t inode)
  {
    return(inode % RSBAC_AUTH_NR_CAP_EFF_FD_LISTS);
  }
static inline int fs_fd_hash(rsbac_inode_nr_t inode)
  {
    return(inode % RSBAC_AUTH_NR_CAP_FS_FD_LISTS);
  }
#endif

static int cap_compare(void * desc1, void * desc2)
  {
    struct rsbac_auth_cap_range_t * range1 = desc1;
    struct rsbac_auth_cap_range_t * range2 = desc2;

    if(!desc1 || !desc2)
      return 0;
    if(range1->first < range2->first)
      return -1;
    if(range1->first > range2->first)
      return 1;
    if(range1->last < range2->last)
      return -1;
    if(range1->last > range2->last)
      return 1;
    return 0;
  };

static int single_cap_compare(void * desc1, void * desc2)
  {
    struct rsbac_auth_cap_range_t * range = desc1;
    rsbac_uid_t * uid = desc2;

    if(!desc1 || !desc2)
      return 0;
    if(   (*uid < range->first)
       || (*uid > range->last)
      )
      return 1;
    else
      return 0;
  };

/* auth_register_fd_lists() */
/* register fd ACL lists for device */

static int auth_register_fd_lists(struct rsbac_auth_device_list_item_t * device_p,
                                  kdev_t kdev)
  {
    char                          * name;
    int                             err = 0;
    int                             tmperr;
    char                            number[10];
    u_int                           file_no;
    struct rsbac_list_lol_info_t lol_info;

    if(!device_p)
      return(-RSBAC_EINVALIDPOINTER);
    name = rsbac_kmalloc(RSBAC_MAXNAMELEN);
    if(!name)
      return -RSBAC_ENOMEM;

    /* register all the AUTH lists of lists */
    for (file_no = 0; file_no < RSBAC_AUTH_NR_CAP_FD_LISTS; file_no++)
      {
        /* construct name from base name + number */
        strcpy(name, RSBAC_AUTH_FD_FILENAME);
        strcat(name, inttostr(number,file_no) );

        lol_info.version = RSBAC_AUTH_FD_LIST_VERSION;
        lol_info.key = RSBAC_AUTH_LIST_KEY;
        lol_info.desc_size = sizeof(rsbac_inode_nr_t);
        lol_info.data_size = 0;
        lol_info.subdesc_size = sizeof(struct rsbac_auth_cap_range_t);
        lol_info.subdata_size = 0; /* rights */
        lol_info.max_age = 0;
        tmperr = rsbac_list_lol_register(RSBAC_LIST_VERSION,
                                         &(device_p->handles[file_no]),
                                         lol_info,
                                         RSBAC_LIST_PERSIST | RSBAC_LIST_DEF_DATA,
                                         rsbac_list_compare_u32,
                                         cap_compare,
                                         NULL,
                                         NULL,
                                         NULL,
                                         NULL,
                                         name,
                                         kdev);
        if(tmperr)
          {
            char * tmp;

            tmp = rsbac_kmalloc(RSBAC_MAXNAMELEN);
            if(tmp)
              {
                printk(KERN_WARNING
                       "auth_register_fd_lists(): registering list %s for device %02u:%02u failed with error %s!\n",
                       name,
                       MAJOR(kdev),
                       MINOR(kdev),
                       get_error_name(tmp, tmperr));
                rsbac_kfree(tmp);
              }
            err = tmperr;
          }
      }
#ifdef CONFIG_RSBAC_AUTH_DAC_OWNER
    /* register all the AUTH DAC lists of lists */
    for (file_no = 0; file_no < RSBAC_AUTH_NR_CAP_EFF_FD_LISTS; file_no++)
      {
        /* construct name from base name + number */
        strcpy(name, RSBAC_AUTH_FD_EFF_FILENAME);
        strcat(name, inttostr(number,file_no) );

        lol_info.version = RSBAC_AUTH_FD_EFF_LIST_VERSION;
        lol_info.key = RSBAC_AUTH_LIST_KEY;
        lol_info.desc_size = sizeof(rsbac_inode_nr_t);
        lol_info.data_size = 0;
        lol_info.subdesc_size = sizeof(struct rsbac_auth_cap_range_t);
        lol_info.subdata_size = 0; /* rights */
        lol_info.max_age = 0;
        tmperr = rsbac_list_lol_register(RSBAC_LIST_VERSION,
                                         &(device_p->eff_handles[file_no]),
                                         lol_info,
                                         RSBAC_LIST_PERSIST | RSBAC_LIST_DEF_DATA,
                                         rsbac_list_compare_u32,
                                         cap_compare,
                                         NULL,
                                         NULL,
                                         NULL,
                                         NULL,
                                         name,
                                         kdev);
        if(tmperr)
          {
            char * tmp;

            tmp = rsbac_kmalloc(RSBAC_MAXNAMELEN);
            if(tmp)
              {
                printk(KERN_WARNING
                       "auth_register_fd_lists(): registering list %s for device %02u:%02u failed with error %s!\n",
                       name,
                       MAJOR(kdev),
                       MINOR(kdev),
                       get_error_name(tmp, tmperr));
                rsbac_kfree(tmp);
              }
            err = tmperr;
          }
      }
    for (file_no = 0; file_no < RSBAC_AUTH_NR_CAP_FS_FD_LISTS; file_no++)
      {
        /* construct name from base name + number */
        strcpy(name, RSBAC_AUTH_FD_FS_FILENAME);
        strcat(name, inttostr(number,file_no) );

        lol_info.version = RSBAC_AUTH_FD_FS_LIST_VERSION;
        lol_info.key = RSBAC_AUTH_LIST_KEY;
        lol_info.desc_size = sizeof(rsbac_inode_nr_t);
        lol_info.data_size = 0;
        lol_info.subdesc_size = sizeof(struct rsbac_auth_cap_range_t);
        lol_info.subdata_size = 0; /* rights */
        lol_info.max_age = 0;
        tmperr = rsbac_list_lol_register(RSBAC_LIST_VERSION,
                                         &(device_p->fs_handles[file_no]),
                                         lol_info,
                                         RSBAC_LIST_PERSIST | RSBAC_LIST_DEF_DATA,
                                         rsbac_list_compare_u32,
                                         cap_compare,
                                         NULL,
                                         NULL,
                                         NULL,
                                         NULL,
                                         name,
                                         kdev);
        if(tmperr)
          {
            char * tmp;

            tmp = rsbac_kmalloc(RSBAC_MAXNAMELEN);
            if(tmp)
              {
                printk(KERN_WARNING
                       "auth_register_fd_lists(): registering list %s for device %02u:%02u failed with error %s!\n",
                       name,
                       MAJOR(kdev),
                       MINOR(kdev),
                       get_error_name(tmp, tmperr));
                rsbac_kfree(tmp);
              }
            err = tmperr;
          }
      }
#endif
    return err;
  }

/* auth_detach_fd_lists() */
/* detach from fd AUTH lists for device */

static int auth_detach_fd_lists(struct rsbac_auth_device_list_item_t * device_p)
  {
    char                          * name;
    int                             err = 0;
    int                             tmperr;
    char                            number[10];
    u_int                           file_no;

    if(!device_p)
      return(-RSBAC_EINVALIDPOINTER);
    name = rsbac_kmalloc(RSBAC_MAXNAMELEN);
    if(!name)
      return -RSBAC_ENOMEM;

    /* detach all the AUTH lists of lists */
    for (file_no = 0; file_no < RSBAC_AUTH_NR_CAP_FD_LISTS; file_no++)
      {
        /* construct name from base name + number */
        strcpy(name, RSBAC_AUTH_FD_FILENAME);
        strcat(name, inttostr(number,file_no) );

        tmperr = rsbac_list_lol_detach(&device_p->handles[file_no],
                                       RSBAC_AUTH_LIST_KEY);
        if(tmperr)
          {
            char * tmp;

            tmp = rsbac_kmalloc(RSBAC_MAXNAMELEN);
            if(tmp)
              {
                printk(KERN_WARNING
                       "auth_detach_fd_lists(): detaching from list %s for device %02u:%02u failed with error %s!\n",
                       name,
                       MAJOR(device_p->id),
                       MINOR(device_p->id),
                       get_error_name(tmp, tmperr));
                rsbac_kfree(tmp);
              }
            err = tmperr;
          }
      }
#ifdef CONFIG_RSBAC_AUTH_DAC_OWNER
    for (file_no = 0; file_no < RSBAC_AUTH_NR_CAP_EFF_FD_LISTS; file_no++)
      {
        /* construct name from base name + number */
        strcpy(name, RSBAC_AUTH_FD_EFF_FILENAME);
        strcat(name, inttostr(number,file_no) );

        tmperr = rsbac_list_lol_detach(&device_p->eff_handles[file_no],
                                       RSBAC_AUTH_LIST_KEY);
        if(tmperr)
          {
            char * tmp;

            tmp = rsbac_kmalloc(RSBAC_MAXNAMELEN);
            if(tmp)
              {
                printk(KERN_WARNING
                       "auth_detach_fd_lists(): detaching from list %s for device %02u:%02u failed with error %s!\n",
                       name,
                       MAJOR(device_p->id),
                       MINOR(device_p->id),
                       get_error_name(tmp, tmperr));
                rsbac_kfree(tmp);
              }
            err = tmperr;
          }
      }
    for (file_no = 0; file_no < RSBAC_AUTH_NR_CAP_FS_FD_LISTS; file_no++)
      {
        /* construct name from base name + number */
        strcpy(name, RSBAC_AUTH_FD_FS_FILENAME);
        strcat(name, inttostr(number,file_no) );

        tmperr = rsbac_list_lol_detach(&device_p->fs_handles[file_no],
                                       RSBAC_AUTH_LIST_KEY);
        if(tmperr)
          {
            char * tmp;

            tmp = rsbac_kmalloc(RSBAC_MAXNAMELEN);
            if(tmp)
              {
                printk(KERN_WARNING
                       "auth_detach_fd_lists(): detaching from list %s for device %02u:%02u failed with error %s!\n",
                       name,
                       MAJOR(device_p->id),
                       MINOR(device_p->id),
                       get_error_name(tmp, tmperr));
                rsbac_kfree(tmp);
              }
            err = tmperr;
          }
      }
#endif

    return err;
  }

/************************************************************************** */
/* 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);
    };

/************************************************************************** */
/* 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;
      int i;

      /* 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 */
      for(i=0 ; i < RSBAC_AUTH_NR_CAP_FD_LISTS ; i++)
        new_item_p->handles[i] = NULL;
#ifdef CONFIG_RSBAC_AUTH_DAC_OWNER
      for(i=0 ; i < RSBAC_AUTH_NR_CAP_EFF_FD_LISTS ; i++)
        new_item_p->eff_handles[i] = NULL;
      for(i=0 ; i < RSBAC_AUTH_NR_CAP_FS_FD_LISTS ; i++)
        new_item_p->fs_handles[i] = NULL;
#endif
      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);
    };

/************************************************************************** */
/* 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 clear_device_item(struct rsbac_auth_device_list_item_t * item_p)
    {
      if(!item_p)
        return;

      /* First deregister lists... */
      auth_detach_fd_lists(item_p);
      /* 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_inode_nr_t fid,
                                rsbac_pid_t pid)
    {
      struct rsbac_auth_cap_range_t  * cap_item_p;
      rsbac_time_t * ttl_p;
      int i;
      long count;

      rsbac_list_lol_remove(process_handle, &pid);
      count = rsbac_list_lol_get_all_subdesc_ttl(device_p->handles[fd_hash(fid)],
                                                 &fid,
                                                 (void **) &cap_item_p,
                                                 &ttl_p);
      if(count > 0)
        {
          for(i=0; i < count ; i++)
            {
              rsbac_list_lol_subadd_ttl(process_handle,
                                        ttl_p[i],
                                        &pid,
                                        &cap_item_p[i],
                                        NULL);
            }
          vfree(cap_item_p);
          vfree(ttl_p);
        }
      else
        {
          if(   (count < 0)
             && (count != -RSBAC_ENOTFOUND)
            )
            return count;
        }

#ifdef CONFIG_RSBAC_AUTH_DAC_OWNER
      rsbac_list_lol_remove(process_eff_handle, &pid);
      count = rsbac_list_lol_get_all_subdesc_ttl(device_p->eff_handles[eff_fd_hash(fid)],
                                                 &fid,
                                                 (void **) &cap_item_p,
                                                 &ttl_p);
      if(count > 0)
        {
          for(i=0; i < count ; i++)
            {
              rsbac_list_lol_subadd_ttl(process_eff_handle,
                                        ttl_p[i],
                                        &pid,
                                        &cap_item_p[i],
                                        NULL);
            }
          vfree(cap_item_p);
          vfree(ttl_p);
        }
      else
        {
          if(   (count < 0)
             && (count != -RSBAC_ENOTFOUND)
            )
            return count;
        }
      rsbac_list_lol_remove(process_fs_handle, &pid);
      count = rsbac_list_lol_get_all_subdesc_ttl(device_p->fs_handles[fs_fd_hash(fid)],
                                                 &fid,
                                                 (void **) &cap_item_p,
                                                 &ttl_p);
      if(count > 0)
        {
          for(i=0; i < count ; i++)
            {
              rsbac_list_lol_subadd_ttl(process_fs_handle,
                                        ttl_p[i],
                                        &pid,
                                        &cap_item_p[i],
                                        NULL);
            }
          vfree(cap_item_p);
          vfree(ttl_p);
        }
      else
        {
          if(   (count < 0)
             && (count != -RSBAC_ENOTFOUND)
            )
            return count;
        }
#endif

      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_handle(rsbac_list_handle_t handle,
                                       rsbac_pid_t old_pid,
                                       rsbac_pid_t new_pid)
    {
      struct rsbac_auth_cap_range_t  * cap_item_p;
      rsbac_time_t * ttl_p;
      int i;
      long count;

      rsbac_list_lol_remove(handle, &new_pid);
      count = rsbac_list_lol_get_all_subdesc_ttl(handle,
                                                 &old_pid,
                                                 (void **) &cap_item_p,
                                                 &ttl_p);
      if(count > 0)
        {
          for(i=0; i < count ; i++)
            {
              rsbac_list_lol_subadd_ttl(handle,
                                        ttl_p[i],
                                        &new_pid,
                                        &cap_item_p[i],
                                        NULL);
            }
          vfree(cap_item_p);
          vfree(ttl_p);
        }
      else
        {
          if(count < 0)
            return count;
        }
      return 0;
    }

static int copy_pp_cap_set_item(rsbac_pid_t old_pid,
                                rsbac_pid_t new_pid)
    {
      int res;

      res = copy_pp_cap_set_item_handle(process_handle, old_pid, new_pid);

#ifdef CONFIG_RSBAC_AUTH_DAC_OWNER
      if(res)
        return res;
      res = copy_pp_cap_set_item_handle(process_eff_handle, old_pid, new_pid);
      if(res)
        return res;
      res = copy_pp_cap_set_item_handle(process_fs_handle, old_pid, new_pid);
#endif
      return(res);
    }; /* end of copy_pp_cap_set_item() */

/************************************************* */
/*               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)
              {
                /* trigger rsbac_mount() */
                rsbac_read_unlock(&device_list_head.lock, &flags);
                rsbac_get_super_block(MKDEV(major, minor));
                /* retry */
                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;

    u_int                                     cap_set_count = 0;
    u_int                                     member_count = 0;
    u_long dflags;
    struct rsbac_auth_device_list_item_t   * device_p;
    int i;

    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");

    len += sprintf(buffer + len, "%lu process cap set items, sum of %lu members\n",
                   rsbac_list_lol_count(process_handle),
                   rsbac_list_lol_all_subcount(process_handle));
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

#ifdef CONFIG_RSBAC_AUTH_DAC_OWNER
    len += sprintf(buffer + len, "%lu process eff cap set items, sum of %lu members\n",
                   rsbac_list_lol_count(process_eff_handle),
                   rsbac_list_lol_all_subcount(process_eff_handle));
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;
    len += sprintf(buffer + len, "%lu process fs cap set items, sum of %lu members\n",
                   rsbac_list_lol_count(process_fs_handle),
                   rsbac_list_lol_all_subcount(process_fs_handle));
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;
#endif

    /* 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;
        for(i=0 ; i < RSBAC_AUTH_NR_CAP_FD_LISTS; i++)
          {
            cap_set_count += rsbac_list_lol_count(device_p->handles[i]);
            member_count += rsbac_list_lol_all_subcount(device_p->handles[i]);
          }
        len += sprintf(buffer + len, "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);
        pos = begin + len;
        if (pos < offset)
          {
            len = 0;
            begin = pos;
          }
        if (pos > offset+length)
          goto out_unlock;

#ifdef CONFIG_RSBAC_AUTH_DAC_OWNER
        cap_set_count = 0;
        member_count = 0;
        for(i=0 ; i < RSBAC_AUTH_NR_CAP_EFF_FD_LISTS; i++)
          {
            cap_set_count += rsbac_list_lol_count(device_p->eff_handles[i]);
            member_count += rsbac_list_lol_all_subcount(device_p->eff_handles[i]);
          }
        len += sprintf(buffer + len, "device %02u:%02u has %u file eff cap set items, sum of %u members\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;
        cap_set_count = 0;
        member_count = 0;
        for(i=0 ; i < RSBAC_AUTH_NR_CAP_FS_FD_LISTS; i++)
          {
            cap_set_count += rsbac_list_lol_count(device_p->fs_handles[i]);
            member_count += rsbac_list_lol_all_subcount(device_p->fs_handles[i]);
          }
        len += sprintf(buffer + len, "device %02u:%02u has %u file fs cap set items, sum of %u members\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;
#endif

        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;

    u_int                                      count = 0;
    u_int                                      member_count = 0;
    u_long                                     all_member_count;
    u_long dflags;
    int i,j,list;
    struct rsbac_auth_device_list_item_t   * device_p;
    rsbac_pid_t * p_list;
    rsbac_inode_nr_t * f_list;
    struct rsbac_auth_cap_range_t * cap_list;

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

    all_member_count = 0;
    count = rsbac_list_lol_get_all_desc(process_handle,
                                        (void **) &p_list);
    if(count > 0)
      {
        for(i=0; i<count; i++)
          {
            member_count = rsbac_list_lol_get_all_subdesc(process_handle,
                                                          &p_list[i],
                                                          (void **) &cap_list);
            len += sprintf(buffer + len, "\n %u\t%u\t",
                           p_list[i],
                           member_count);
            if(member_count > 0)
              {
                for(j=0; j<member_count; j++)
                  {
                    if(cap_list[j].first != cap_list[j].last)
                      len += sprintf(buffer + len, "%u:%u ",
                                     cap_list[j].first,
                                     cap_list[j].last);
                    else
                      len += sprintf(buffer + len, "%u ",
                                     cap_list[j].first);
                    pos = begin + len;
                    if (pos < offset)
                      {
                        len = 0;
                        begin = pos;
                      }
                    if (pos > offset+length)
                      {
                        vfree(cap_list);
                        vfree(p_list);
                        goto out;
                      }
                  }
                vfree(cap_list);
                all_member_count += member_count;
              }
            pos = begin + len;
            if (pos < offset)
              {
                len = 0;
                begin = pos;
              }
            if (pos > offset+length)
              {
                vfree(p_list);
                goto out;
              }
          }
        vfree(p_list);
      }
    len += sprintf(buffer + len, "\n%u process cap set items, sum of %lu members\n",
                   count,all_member_count);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

#ifdef CONFIG_RSBAC_AUTH_DAC_OWNER
    len += sprintf(buffer + len, "\nProcess eff capabilities:\nset-id  count   cap-members");
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    all_member_count = 0;
    count = rsbac_list_lol_get_all_desc(process_eff_handle,
                                        (void **) &p_list);
    if(count > 0)
      {
        for(i=0; i<count; i++)
          {
            member_count = rsbac_list_lol_get_all_subdesc(process_eff_handle,
                                                          &p_list[i],
                                                          (void **) &cap_list);
            len += sprintf(buffer + len, "\n %u\t%u\t",
                           p_list[i],
                           member_count);
            if(member_count > 0)
              {
                for(j=0; j<member_count; j++)
                  {
                    if(cap_list[j].first != cap_list[j].last)
                      len += sprintf(buffer + len, "%u:%u ",
                                     cap_list[j].first,
                                     cap_list[j].last);
                    else
                      len += sprintf(buffer + len, "%u ",
                                     cap_list[j].first);
                    pos = begin + len;
                    if (pos < offset)
                      {
                        len = 0;
                        begin = pos;
                      }
                    if (pos > offset+length)
                      {
                        vfree(cap_list);
                        vfree(p_list);
                        goto out;
                      }
                  }
                vfree(cap_list);
                all_member_count += member_count;
              }
            pos = begin + len;
            if (pos < offset)
              {
                len = 0;
                begin = pos;
              }
            if (pos > offset+length)
              {
                vfree(p_list);
                goto out;
              }
          }
        vfree(p_list);
      }
    len += sprintf(buffer + len, "\n%u process eff cap set items, sum of %lu members\n",
                   count,all_member_count);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;
    len += sprintf(buffer + len, "\nProcess fs capabilities:\nset-id  count   cap-members");
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;

    all_member_count = 0;
    count = rsbac_list_lol_get_all_desc(process_fs_handle,
                                        (void **) &p_list);
    if(count > 0)
      {
        for(i=0; i<count; i++)
          {
            member_count = rsbac_list_lol_get_all_subdesc(process_fs_handle,
                                                          &p_list[i],
                                                          (void **) &cap_list);
            len += sprintf(buffer + len, "\n %u\t%u\t",
                           p_list[i],
                           member_count);
            if(member_count > 0)
              {
                for(j=0; j<member_count; j++)
                  {
                    if(cap_list[j].first != cap_list[j].last)
                      len += sprintf(buffer + len, "%u:%u ",
                                     cap_list[j].first,
                                     cap_list[j].last);
                    else
                      len += sprintf(buffer + len, "%u ",
                                     cap_list[j].first);
                    pos = begin + len;
                    if (pos < offset)
                      {
                        len = 0;
                        begin = pos;
                      }
                    if (pos > offset+length)
                      {
                        vfree(cap_list);
                        vfree(p_list);
                        goto out;
                      }
                  }
                vfree(cap_list);
                all_member_count += member_count;
              }
            pos = begin + len;
            if (pos < offset)
              {
                len = 0;
                begin = pos;
              }
            if (pos > offset+length)
              {
                vfree(p_list);
                goto out;
              }
          }
        vfree(p_list);
      }
    len += sprintf(buffer + len, "\n\n%u process fs cap set items, sum of %lu members\n",
                   count,all_member_count);
    pos = begin + len;
    if (pos < offset)
      {
        len = 0;
        begin = pos;
      }
    if (pos > offset+length)
      goto out;
#endif

    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 */
        all_member_count = 0;
        for(list=0 ; list < RSBAC_AUTH_NR_CAP_FD_LISTS; list++)
          {
            count = rsbac_list_lol_get_all_desc(device_p->handles[list],
                                                (void **) &f_list);
            if(count > 0)
              {
                for(i=0; i<count; i++)
                  {
                    member_count = rsbac_list_lol_get_all_subdesc(device_p->handles[list],
                                                                  &f_list[i],
                                                                  (void **) &cap_list);
                    len += sprintf(buffer + len, "\n %u\t%u\t",
                                   f_list[i],
                                   member_count);
                    if(member_count > 0)
                      {
                        for(j=0; j<member_count; j++)
                          {
                            if(cap_list[j].first != cap_list[j].last)
                              len += sprintf(buffer + len, "%u:%u ",
                                             cap_list[j].first,
                                             cap_list[j].last);
                            else
                              len += sprintf(buffer + len, "%u ",
                                             cap_list[j].first);
                            pos = begin + len;
                            if (pos < offset)
                              {
                                len = 0;
                                begin = pos;
                              }
                            if (pos > offset+length)
                              {
                                vfree(cap_list);
                                vfree(f_list);
                                goto out;
                              }
                          }
                        vfree(cap_list);
                        all_member_count += member_count;
                      }
                    pos = begin + len;
                    if (pos < offset)
                      {
                        len = 0;
                        begin = pos;
                      }
                    if (pos > offset+length)
                      {
                        vfree(f_list);
                        goto out;
                      }
                  }
                vfree(f_list);
              }
          }
        len += sprintf(buffer + len, "\ndevice %02u:%02u has %u file cap set items, sum of %lu members, list is clean\n",
                       MAJOR(device_p->id),
                       MINOR(device_p->id),
                       count, all_member_count);
        pos = begin + len;
        if (pos < offset)
          {
            len = 0;
            begin = pos;
          }
        if (pos > offset+length)
          goto out_unlock;
#ifdef CONFIG_RSBAC_AUTH_DAC_OWNER
        all_member_count = 0;
        for(list=0 ; list < RSBAC_AUTH_NR_CAP_EFF_FD_LISTS; list++)
          {
            count = rsbac_list_lol_get_all_desc(device_p->eff_handles[list],
                                                (void **) &f_list);
            if(count > 0)
              {
                for(i=0; i<count; i++)
                  {
                    member_count = rsbac_list_lol_get_all_subdesc(device_p->eff_handles[list],
                                                                  &f_list[i],
                                                                  (void **) &cap_list);
                    len += sprintf(buffer + len, "\n %u\t%u\t",
                                   f_list[i],
                                   member_count);
                    if(member_count > 0)
                      {
                        for(j=0; j<member_count; j++)
                          {
                            if(cap_list[j].first != cap_list[j].last)
                              len += sprintf(buffer + len, "%u:%u ",
                                             cap_list[j].first,
                                             cap_list[j].last);
                            else
                              len += sprintf(buffer + len, "%u ",
                                             cap_list[j].first);
                            pos = begin + len;
                            if (pos < offset)
                              {
                                len = 0;
                                begin = pos;
                              }
                            if (pos > offset+length)
                              {
                                vfree(cap_list);
                                vfree(f_list);
                                goto out;
                              }
                          }
                        vfree(cap_list);
                        all_member_count += member_count;
                      }
                    pos = begin + len;
                    if (pos < offset)
                      {
                        len = 0;
                        begin = pos;
                      }
                    if (pos > offset+length)
                      {
                        vfree(f_list);
                        goto out;
                      }
                  }
                vfree(f_list);
              }
          }
        len += sprintf(buffer + len, "\ndevice %02u:%02u has %u file eff cap set items, sum of %lu members, list is clean\n",
                       MAJOR(device_p->id),
                       MINOR(device_p->id),
                       count, all_member_count);
        pos = begin + len;
        if (pos < offset)
          {
            len = 0;
            begin = pos;
          }
        if (pos > offset+length)
          goto out_unlock;

        all_member_count = 0;
        for(list=0 ; list < RSBAC_AUTH_NR_CAP_FS_FD_LISTS; list++)
          {
            count = rsbac_list_lol_get_all_desc(device_p->fs_handles[list],
                                                (void **) &f_list);
            if(count > 0)
              {
                for(i=0; i<count; i++)
                  {
                    member_count = rsbac_list_lol_get_all_subdesc(device_p->fs_handles[list],
                                                                  &f_list[i],
                                                                  (void **) &cap_list);
                    len += sprintf(buffer + len, "\n %u\t%u\t",
                                   f_list[i],
                                   member_count);
                    if(member_count > 0)
                      {
                        for(j=0; j<member_count; j++)
                          {
                            if(cap_list[j].first != cap_list[j].last)
                              len += sprintf(buffer + len, "%u:%u ",
                                             cap_list[j].first,
                                             cap_list[j].last);
                            else
                              len += sprintf(buffer + len, "%u ",
                                             cap_list[j].first);
                            pos = begin + len;
                            if (pos < offset)
                              {
                                len = 0;
                                begin = pos;
                              }
                            if (pos > offset+length)
                              {
                                vfree(cap_list);
                                vfree(f_list);
                                goto out;
                              }
                          }
                        vfree(cap_list);
                        all_member_count += member_count;
                      }
                    pos = begin + len;
                    if (pos < offset)
                      {
                        len = 0;
                        begin = pos;
                      }
                    if (pos > offset+length)
                      {
                        vfree(f_list);
                        goto out;
                      }
                  }
                vfree(f_list);
              }
          }
        len += sprintf(buffer + len, "\ndevice %02u:%02u has %u file fs cap set items, sum of %lu members, list is clean\n",
                       MAJOR(device_p->id),
                       MINOR(device_p->id),
                       count, all_member_count);
        pos = begin + len;
        if (pos < offset)
          {
            len = 0;
            begin = pos;
          }
        if (pos > offset+length)
          goto out_unlock;
#endif
        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;
}
#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.          */

#ifdef CONFIG_RSBAC_INIT_DELAY
int rsbac_init_auth(void)
#else
int __init rsbac_init_auth(void)
#endif
  {
    int  err = 0;
    struct rsbac_auth_device_list_item_t * device_p = NULL;
    u_long dflags;
    struct proc_dir_entry * tmp_entry_p;
    struct rsbac_list_lol_info_t lol_info;

    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");

    lol_info.version = RSBAC_AUTH_P_LIST_VERSION;
    lol_info.key = RSBAC_AUTH_LIST_KEY;
    lol_info.desc_size = sizeof(rsbac_pid_t);
    lol_info.data_size = 0;
    lol_info.subdesc_size = sizeof(struct rsbac_auth_cap_range_t);
    lol_info.subdata_size = 0;
    lol_info.max_age = 0;
    err = rsbac_list_lol_register(RSBAC_LIST_VERSION,
                                  &process_handle,
                                  lol_info,
                                  RSBAC_LIST_DEF_DATA,
                                  NULL,
                                  cap_compare,
                                  NULL,
                                  NULL,
                                  NULL,
                                  NULL,
                                  RSBAC_AUTH_P_LIST_NAME,
                                  0);
    if(err)
      {
        char * tmp = rsbac_kmalloc(RSBAC_MAXNAMELEN);

        if(tmp)
          {
            printk(KERN_WARNING
                   "rsbac_init_auth(): Registering AUTH process cap list failed with error %s\n",
                   get_error_name(tmp, err));
            rsbac_kfree(tmp);
          }
      }
#ifdef CONFIG_RSBAC_AUTH_DAC_OWNER
    lol_info.version = RSBAC_AUTH_P_LIST_VERSION;
    lol_info.key = RSBAC_AUTH_LIST_KEY;
    lol_info.desc_size = sizeof(rsbac_pid_t);
    lol_info.data_size = 0;
    lol_info.subdesc_size = sizeof(struct rsbac_auth_cap_range_t);
    lol_info.subdata_size = 0;
    lol_info.max_age = 0;
    err = rsbac_list_lol_register(RSBAC_LIST_VERSION,
                                  &process_eff_handle,
                                  lol_info,
                                  RSBAC_LIST_DEF_DATA,
                                  NULL,
                                  cap_compare,
                                  NULL,
                                  NULL,
                                  NULL,
                                  NULL,
                                  RSBAC_AUTH_P_EFF_LIST_NAME,
                                  0);
    if(err)
      {
        char * tmp = rsbac_kmalloc(RSBAC_MAXNAMELEN);

        if(tmp)
          {
            printk(KERN_WARNING
                   "rsbac_init_auth(): Registering AUTH process eff cap list failed with error %s\n",
                   get_error_name(tmp, err));
            rsbac_kfree(tmp);
          }
      }
    lol_info.version = RSBAC_AUTH_P_LIST_VERSION;
    lol_info.key = RSBAC_AUTH_LIST_KEY;
    lol_info.desc_size = sizeof(rsbac_pid_t);
    lol_info.data_size = 0;
    lol_info.subdesc_size = sizeof(struct rsbac_auth_cap_range_t);
    lol_info.subdata_size = 0;
    lol_info.max_age = 0;
    err = rsbac_list_lol_register(RSBAC_LIST_VERSION,
                                  &process_fs_handle,
                                  lol_info,
                                  RSBAC_LIST_DEF_DATA,
                                  NULL,
                                  cap_compare,
                                  NULL,
                                  NULL,
                                  NULL,
                                  NULL,
                                  RSBAC_AUTH_P_FS_LIST_NAME,
                                  0);
    if(err)
      {
        char * tmp = rsbac_kmalloc(RSBAC_MAXNAMELEN);

        if(tmp)
          {
            printk(KERN_WARNING
                   "rsbac_init_auth(): Registering AUTH process fs cap list failed with error %s\n",
                   get_error_name(tmp, err));
            rsbac_kfree(tmp);
          }
      }
#endif

    /* Init FD lists */
    device_list_head.lock = RW_LOCK_UNLOCKED;
    device_list_head.head = NULL;
    device_list_head.tail = NULL;
    device_list_head.curr = NULL;
    device_list_head.count = 0;

    /* read all data */
//#ifdef CONFIG_RSBAC_DEBUG
//    if (rsbac_debug_ds_auth)
      printk(KERN_INFO "rsbac_init_auth(): Registering FD lists\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 = auth_register_fd_lists(device_p,rsbac_root_dev)))
      {
        char tmp[RSBAC_MAXNAMELEN];

        printk(KERN_WARNING
               "rsbac_init_auth(): File/Dir ACL registration failed for dev %02u:%02u, err %s!\n",
               MAJOR(rsbac_root_dev), MINOR(rsbac_root_dev), get_error_name(tmp,err));
      }
    /* wait for write access to device_list_head */
    rsbac_write_lock_irq(&device_list_head.lock, &dflags);
    device_p = add_device_item(device_p);
    /* device was added, allow access */
    rsbac_write_unlock_irq(&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;

    /* register lists */
    if((err = auth_register_fd_lists(new_device_p, kdev)))
      {
        char tmp[RSBAC_MAXNAMELEN];

        printk(KERN_WARNING
               "rsbac_mount_auth(): File/Dir ACL registration failed for dev %02u:%02u, err %s!\n",
               MAJOR(kdev), MINOR(kdev), get_error_name(tmp,err));
      }

    /* 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_irq(&device_list_head.lock, &dflags);
        device_p = add_device_item(new_device_p);
        rsbac_write_unlock_irq(&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_irq(&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_irq(&device_list_head.lock, &flags);
    return(0);
  };

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

int rsbac_stats_auth(void)
  {
    u_int                                     cap_set_count = 0;
    u_int                                     member_count = 0;
    u_long dflags;
    struct rsbac_auth_device_list_item_t   * device_p;
    int i;
  
    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");

    printk(KERN_INFO "%lu process cap set items, sum of %lu members\n",
                   rsbac_list_lol_count(process_handle),
                   rsbac_list_lol_all_subcount(process_handle));

    /* 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;
        for(i=0 ; i < RSBAC_AUTH_NR_CAP_FD_LISTS; i++)
          {
            cap_set_count += rsbac_list_lol_count(device_p->handles[i]);
            member_count += rsbac_list_lol_all_subcount(device_p->handles[i]);
          }
        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;
    u_long                              f_count = 0, f_sum = 0, tmp_count,
                                        r_count, u_count, b_count, no_member_count;
    long                                desc_count;
    u_int                               i,list_no;
    u_long                              dflags;
    struct super_block                * sb_p;
    struct inode                      * inode_p;
    rsbac_inode_nr_t                  * fd_desc_p;
  
    if (!rsbac_is_initialized())
      {
        printk(KERN_WARNING "rsbac_check_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_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;
        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 */
        for(list_no = 0; list_no < RSBAC_AUTH_NR_CAP_FD_LISTS; list_no++)
          {
/*            printk(KERN_INFO "rsbac_check_auth(): list %u\n",
                   list_no); */
            tmp_count = 0;
            desc_count = rsbac_list_lol_get_all_desc(device_p->handles[list_no], (void **) &fd_desc_p);
            if(desc_count > 0)
              {
                for(i=0; i<desc_count; i++)
                  {
                    /* 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, fd_desc_p[i]);
                        if(is_bad_inode(inode_p))
                          { /* inode is bad -> remove */
                            b_count++;
                            if(correct)
                              {
                                printk(KERN_INFO
                                       "rsbac_check_auth(): fd_item for bad inode %u on device %02u:%02u, list %u, removing!\n",
                                        fd_desc_p[i], MAJOR(device_p->id), MINOR(device_p->id), list_no);
                                rsbac_list_lol_remove(device_p->handles[list_no], &fd_desc_p[i]);
                                continue;
                              }
                            else
                              {
                                printk(KERN_INFO
                                       "rsbac_check_auth(): fd_item for bad inode %u on device %02u:%02u, list %u!\n",
                                       fd_desc_p[i], MAJOR(device_p->id), MINOR(device_p->id), list_no);
                              }
                          } /* end of bad_inode */
                        else
                          { /* good inode */
                            /* currently only deletion checking of ext2 inodes is possible */
                            if(sb_p->s_magic == EXT2_SUPER_MAGIC)
                              {
                                if(inode_p->u.ext2_i.i_dtime)
                                  { /* inode has been deleted -> remove */
                                    r_count++;
                                    if(correct)
                                      {
                                        printk(KERN_INFO
                                               "rsbac_check_auth(): fd_item for deleted inode %u on device %02u:%02u, list %u, removing!\n",
                                               fd_desc_p[i], MAJOR(device_p->id), MINOR(device_p->id), list_no);
                                        rsbac_list_lol_remove(device_p->handles[list_no], &fd_desc_p[i]);
                                        continue;
                                      }
                                    else
                                      {
                                        printk(KERN_INFO
                                               "rsbac_check_auth(): fd_item for deleted inode %u on device %02u:%02u, list %u!\n",
                                               fd_desc_p[i], MAJOR(device_p->id), MINOR(device_p->id), list_no);
                                      }
                                  }
                                else
                                  {
                                    if(inode_p->i_nlink <= 0)
                                      { /* inode has been unlinked, but no dtime is set -> warn */
                                        u_count++;
                                        if(correct >= 2)
                                          {
                                            printk(KERN_INFO
                                                   "rsbac_check_auth(): fd_item for inode %u with nlink <= 0 on device %02u:%02u, list %u, removing!\n",
                                                   fd_desc_p[i], MAJOR(device_p->id), MINOR(device_p->id), list_no);
                                            rsbac_list_lol_remove(device_p->handles[list_no], &fd_desc_p[i]);
                                            continue;
                                          }
                                        else
                                          {
                                            printk(KERN_INFO
                                                   "rsbac_check_auth(): deleted inode %u on device %02u:%02u, list %u, has no dtime!\n",
                                                   fd_desc_p[i], MAJOR(device_p->id), MINOR(device_p->id), list_no);
                                          }
                                      }
                                  }
                              }
                          } /* end of is_good_inode */
                        iput(inode_p);
                      } /* end of sb_p */
                  }
                tmp_count++;
                vfree(fd_desc_p);
                f_count += desc_count;
              }
          } /* end of for-fd-list-array */

        switch(correct)
          {
            case 2:  printk(KERN_INFO
                            "rsbac_check_auth(): Device %02u:%02u has %lu file/dir AUTHs (%lu removed (%lu bad inodes, %lu dtimed inodes, %lu unlinked inodes, %lu had no members and default mask))\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);
                            break;
            case 1:  printk(KERN_INFO
                            "rsbac_check_auth(): Device %02u:%02u has %lu file/dir AUTHs (%lu removed (%lu bad inodes, %lu dtimed inodes, %lu had no members and default mask), %lu unlinked inodes)\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);
                            break;
            default: printk(KERN_INFO
                            "rsbac_check_auth(): Device %02u:%02u has %lu file/dir AUTHs (%lu with bad inodes, %lu with dtimed inodes, %lu unlinked inodes, %lu without members and with default mask)\n",
                            MAJOR(device_p->id), MINOR(device_p->id), f_count,
                            b_count, r_count, u_count, no_member_count);
          }
        f_sum += f_count;
        /* go on */
        device_p = device_p->next;
      }
    printk(KERN_INFO "rsbac_check_auth(): Sum of %u Devices with %lu file/dir AUTHs\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\n",
           f_sum);
    return(0);
  };

/************************************************* */
/*               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_pid_t pid,
                               enum rsbac_auth_cap_type_t cap_type,
                               struct rsbac_auth_cap_range_t cap_range,
                               rsbac_time_t ttl)
  {
    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(cap_range.first > cap_range.last)
      return(-RSBAC_EINVALIDVALUE);
    switch(cap_type)
      {
        case ACT_real:
          return rsbac_list_lol_subadd_ttl(process_handle, ttl, &pid, &cap_range, NULL);
#ifdef CONFIG_RSBAC_AUTH_DAC_OWNER
        case ACT_eff:
          return rsbac_list_lol_subadd_ttl(process_eff_handle, ttl, &pid, &cap_range, NULL);
        case ACT_fs:
          return rsbac_list_lol_subadd_ttl(process_fs_handle, ttl, &pid, &cap_range, NULL);
#endif
        default:
          return -RSBAC_EINVALIDTARGET;
      }
  }

int rsbac_auth_add_to_f_capset(rsbac_auth_file_t file,
                               enum rsbac_auth_cap_type_t cap_type,
                               struct rsbac_auth_cap_range_t cap_range,
                               rsbac_time_t ttl)
  {
    int                                     err=0;
    u_long 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(cap_range.first > cap_range.last)
      return(-RSBAC_EINVALIDVALUE);

    /* protect device list */
    rsbac_read_lock(&device_list_head.lock, &dflags);
    device_p = lookup_device(file.device);
    if(!device_p)
      {
        /* trigger rsbac_mount() */
        rsbac_read_unlock(&device_list_head.lock, &dflags);
        rsbac_get_super_block(file.device);
        /* retry */
        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);
          }
      }

    switch(cap_type)
      {
        case ACT_real:
          err = rsbac_list_lol_subadd_ttl(device_p->handles[fd_hash(file.inode)],
                                          ttl, &file.inode, &cap_range, NULL);
          break;
#ifdef CONFIG_RSBAC_AUTH_DAC_OWNER
        case ACT_eff:
          err = rsbac_list_lol_subadd_ttl(device_p->eff_handles[eff_fd_hash(file.inode)],
                                          ttl, &file.inode, &cap_range, NULL);
          break;
        case ACT_fs:
          err = rsbac_list_lol_subadd_ttl(device_p->fs_handles[fs_fd_hash(file.inode)],
                                          ttl, &file.inode, &cap_range, NULL);
          break;
#endif
        default:
          err = -RSBAC_EINVALIDTARGET;
      }
    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_pid_t pid,
                                    enum rsbac_auth_cap_type_t cap_type,
                                    struct rsbac_auth_cap_range_t cap_range)
  {
    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(cap_range.first > cap_range.last)
      return(-RSBAC_EINVALIDVALUE);
    switch(cap_type)
      {
        case ACT_real:
          return rsbac_list_lol_subremove(process_handle, &pid, &cap_range);
#ifdef CONFIG_RSBAC_AUTH_DAC_OWNER
        case ACT_eff:
          return rsbac_list_lol_subremove(process_handle, &pid, &cap_range);
        case ACT_fs:
          return rsbac_list_lol_subremove(process_handle, &pid, &cap_range);
#endif
        default:
          return -RSBAC_EINVALIDTARGET;
      }
  }

int rsbac_auth_remove_from_f_capset(rsbac_auth_file_t file,
                                    enum rsbac_auth_cap_type_t cap_type,
                                    struct rsbac_auth_cap_range_t cap_range)
  {
    int                                    err=0;
    u_long 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(cap_range.first > cap_range.last)
      return(-RSBAC_EINVALIDVALUE);

    /* protect device list */
    rsbac_read_lock(&device_list_head.lock, &dflags);
    device_p = lookup_device(file.device);
    if(!device_p)
      {
        /* trigger rsbac_mount() */
        rsbac_read_unlock(&device_list_head.lock, &dflags);
        rsbac_get_super_block(file.device);
        /* retry */
        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);
          }
      }
    switch(cap_type)
      {
        case ACT_real:
          err = rsbac_list_lol_subremove(device_p->handles[fd_hash(file.inode)], &file.inode, &cap_range);
          break;
#ifdef CONFIG_RSBAC_AUTH_DAC_OWNER
        case ACT_eff:
          err = rsbac_list_lol_subremove(device_p->eff_handles[eff_fd_hash(file.inode)], &file.inode, &cap_range);
          break;
        case ACT_fs:
          err = rsbac_list_lol_subremove(device_p->fs_handles[fs_fd_hash(file.inode)], &file.inode, &cap_range);
          break;
#endif
        default:
          err = -RSBAC_EINVALIDTARGET;
      }
    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_pid_t pid,
                              enum rsbac_auth_cap_type_t cap_type)
  {
    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");
      }
    switch(cap_type)
      {
        case ACT_real:
          return rsbac_list_lol_remove(process_handle, &pid);
#ifdef CONFIG_RSBAC_AUTH_DAC_OWNER
        case ACT_eff:
          return rsbac_list_lol_remove(process_handle, &pid);
        case ACT_fs:
          return rsbac_list_lol_remove(process_handle, &pid);
#endif
        default:
          return -RSBAC_EINVALIDTARGET;
      }
  }

int rsbac_auth_clear_f_capset(rsbac_auth_file_t file,
                              enum rsbac_auth_cap_type_t cap_type)
  {
    int                                    err=0;
    u_long 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)
      {
        /* trigger rsbac_mount() */
        rsbac_read_unlock(&device_list_head.lock, &dflags);
        rsbac_get_super_block(file.device);
        /* retry */
        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);
          }
      }
    switch(cap_type)
      {
        case ACT_real:
          err = rsbac_list_lol_remove(device_p->handles[fd_hash(file.inode)], &file.inode);
          break;
#ifdef CONFIG_RSBAC_AUTH_DAC_OWNER
        case ACT_eff:
          err = rsbac_list_lol_remove(device_p->eff_handles[eff_fd_hash(file.inode)], &file.inode);
          break;
        case ACT_fs:
          err = rsbac_list_lol_remove(device_p->fs_handles[fs_fd_hash(file.inode)], &file.inode);
          break;
#endif
        default:
          err = -RSBAC_EINVALIDTARGET;
      }
    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_pid_t pid,
                                    enum rsbac_auth_cap_type_t cap_type,
                                    rsbac_uid_t member)
  {
    if (!rsbac_is_initialized())
      {
        printk(KERN_WARNING "rsbac_auth_p_capset_member(): RSBAC not initialized\n");
        return FALSE;
      }
    if (in_interrupt())
      {
        printk(KERN_WARNING "rsbac_auth_p_capset_member(): called from interrupt!\n");
      }
    switch(cap_type)
      {
        case ACT_real:
          return rsbac_list_lol_subexist_compare(process_handle, &pid, &member, single_cap_compare);
#ifdef CONFIG_RSBAC_AUTH_DAC_OWNER
        case ACT_eff:
          return rsbac_list_lol_subexist_compare(process_eff_handle, &pid, &member, single_cap_compare);
        case ACT_fs:
          return rsbac_list_lol_subexist_compare(process_fs_handle, &pid, &member, single_cap_compare);
#endif
        default:
          return FALSE;
      }
  }

/* 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_capsets(rsbac_pid_t pid)
  {
    int err;

    err = rsbac_auth_clear_p_capset(pid, ACT_real);
#ifdef CONFIG_RSBAC_AUTH_DAC_OWNER
    if(!err)
      err = rsbac_auth_clear_p_capset(pid, ACT_eff);
    if(!err)
      err = rsbac_auth_clear_p_capset(pid, ACT_fs);
#endif
    return err;
  }

int rsbac_auth_remove_f_capsets(rsbac_auth_file_t file)
  {
    int err;

    err = rsbac_auth_clear_f_capset(file, ACT_real);
#ifdef CONFIG_RSBAC_AUTH_DAC_OWNER
    if(!err)
      err = rsbac_auth_clear_f_capset(file, ACT_eff);
    if(!err)
      err = rsbac_auth_clear_f_capset(file, ACT_fs);
#endif
    return err;
  }

int rsbac_auth_copy_fp_capset(rsbac_auth_file_t    file,
                              rsbac_pid_t p_cap_set_id)
  {
    u_long dflags;
    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)
      {
        /* trigger rsbac_mount() */
        rsbac_read_unlock(&device_list_head.lock, &dflags);
        rsbac_get_super_block(file.device);
        /* retry */
        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);
          }
      }
    /* call the copy function */
    err = copy_fp_cap_set_item(device_p,file.inode,p_cap_set_id);
    rsbac_read_unlock(&device_list_head.lock, &dflags);
    return(err);
  }

int rsbac_auth_copy_pp_capset(rsbac_pid_t old_p_set_id,
                              rsbac_pid_t new_p_set_id)
  {
    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
    /* call the copy function */
    return copy_pp_cap_set_item(old_p_set_id,new_p_set_id);
  }

int rsbac_auth_get_f_caplist(rsbac_auth_file_t file,
                             enum rsbac_auth_cap_type_t cap_type,
                             struct rsbac_auth_cap_range_t **caplist_p,
                             rsbac_time_t **ttllist_p)
  {
    u_long dflags;
    struct rsbac_auth_device_list_item_t * device_p;
    long count;

    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)
      {
        /* trigger rsbac_mount() */
        rsbac_read_unlock(&device_list_head.lock, &dflags);
        rsbac_get_super_block(file.device);
        /* retry */
        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);
          }
      }
    switch(cap_type)
      {
        case ACT_real:
          count = rsbac_list_lol_get_all_subdesc_ttl(device_p->handles[fd_hash(file.inode)],
                                                     &file.inode,
                                                     (void **) caplist_p,
                                                     ttllist_p);
          break;
#ifdef CONFIG_RSBAC_AUTH_DAC_OWNER
        case ACT_eff:
          count = rsbac_list_lol_get_all_subdesc_ttl(device_p->eff_handles[eff_fd_hash(file.inode)],
                                                     &file.inode,
                                                     (void **) caplist_p,
                                                     ttllist_p);
          break;
        case ACT_fs:
          count = rsbac_list_lol_get_all_subdesc_ttl(device_p->fs_handles[fs_fd_hash(file.inode)],
                                                     &file.inode,
                                                     (void **) caplist_p,
                                                     ttllist_p);
          break;
#endif
        default:
          count = -RSBAC_EINVALIDTARGET;
      }
    rsbac_read_unlock(&device_list_head.lock, &dflags);
    return(count);
  }

/* end of auth_data_structures.c */
