/************************************* */
/* Rule Set Based Access Control       */
/* Author and (c) 1999-2002:           */
/*   Amon Ott <ao@rsbac.org>           */
/* Generic lists for all parts         */
/* Last modified: 27/Feb/2002          */
/************************************* */

#include <linux/sched.h>
#include <linux/smp_lock.h>
#include <linux/module.h>
#include <asm/uaccess.h>
#include <rsbac/types.h>
#include <rsbac/error.h>
#include <rsbac/helpers.h>
#include <rsbac/getname.h>
#include <rsbac/debug.h>
#include <rsbac/adf.h>
#include <rsbac/aci_data_structures.h>
#include <rsbac/proc_fs.h>
#include <rsbac/rkmem.h>
#include <rsbac/lists.h>
#include <rsbac/gen_lists.h>

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

static struct rsbac_list_reg_head_t       reg_head;
static struct rsbac_list_lol_reg_head_t   lol_reg_head;
static        rsbac_list_handle_t         devicelist = NULL;
static        boolean                     list_initialized=FALSE;

/*********************************/
/* Data Structures               */
/*********************************/

static struct rsbac_list_item_t * lookup_item_compare(
    struct rsbac_list_reg_item_t * list,
    void * desc)
    {
      struct rsbac_list_item_t  * curr;

      if(!list || !desc || !list->compare)
        return NULL;

      curr = list->curr;
      if(!curr)
        {
          curr = list->head;
          if(!curr)
            return NULL;
        }
      /* if current item is not the right one, search... */
      /* note: item desc is behind official struct */
      if(list->compare(desc, &curr[1]))
        {
          if((list->compare(desc, &curr[1]) > 0))
            {
              curr = curr->next;
              while (   curr
                     && (list->compare(desc, &curr[1]) > 0)
                    )
                {
                  curr = curr->next;
                }
            }
          else
            {
              curr = curr->prev;
              while (   curr
                     && (list->compare(desc, &curr[1]) < 0)
                    )
                {
                  curr = curr->prev;
                }
            }
          if (curr)
            {
              /* keep for speedup */
              list->curr = curr;
              if(!list->compare(desc, &curr[1]))
                {
                  /* found */
                  return curr;
                }
            }
          /* NULL or not found */
          return NULL;
        }
      /* it is the current item -> return it */
      return (curr);
    };

static struct rsbac_list_item_t * lookup_item_memcmp(
    struct rsbac_list_reg_item_t * list,
    void * desc)
    {
      struct rsbac_list_item_t  * curr;

      if(!list || !desc)
        return NULL;

      curr = list->curr;
      if(!curr)
        {
          curr = list->head;
          if(!curr)
            return NULL;
        }
      /* if current item is not the right one, search... */
      /* note: item desc is behind official struct */
      if(memcmp(desc,
                &curr[1],
                list->info.desc_size))
        {
          if(memcmp(desc,
                    &curr[1],
                    list->info.desc_size) > 0)
            {
              curr = curr->next;
              while (   curr
                     && (memcmp(desc,
                                &curr[1],
                                list->info.desc_size) > 0)
                    )
                {
                  curr = curr->next;
                }
            }
          else
            {
              curr = curr->prev;
              while (   curr
                     && (memcmp(desc,
                                &curr[1],
                                list->info.desc_size) < 0)
                    )
                {
                  curr = curr->prev;
                }
            }
          if (curr)
            {
              /* keep for speedup */
              list->curr = curr;
              if(!memcmp(desc,
                         &curr[1],
                         list->info.desc_size))
                {
                  /* found */
                  return curr;
                }
            }
          /* not found */
          return NULL;
        }
      /* it is the current item -> return it */
      return (curr);
    };

static struct rsbac_list_item_t * lookup_item(
    struct rsbac_list_reg_item_t * list,
    void * desc)
    {
      if(!list || !desc)
        return NULL;

      if(list->compare)
        return lookup_item_compare(list, desc);
      else
        return lookup_item_memcmp(list, desc);
    }

static struct rsbac_list_item_t * lookup_item_data_compare(
    struct rsbac_list_reg_item_t * list,
    void * data,
    rsbac_list_data_compare_function_t compare)
    {
      struct rsbac_list_item_t  * curr;

      if(!list || !data || !compare)
        return NULL;

      curr = list->head;

      /* note: item desc is behind official struct */
      while (   curr
             && (   (curr->max_age && (curr->max_age <= CURRENT_TIME))
                 || compare((char *)curr + sizeof(*curr) + list->info.desc_size, data)
                )
            )
        {
          curr = curr->next;
        }
      /* it is the current item -> return it */
      return (curr);
    };

static struct rsbac_list_item_t * lookup_item_data_memcmp(
    struct rsbac_list_reg_item_t * list,
    void * data)
    {
      struct rsbac_list_item_t  * curr;

      if(!list || !data)
        return NULL;

      curr = list->head;

      /* note: item desc is behind official struct */
      while (   curr
             && (   (curr->max_age && (curr->max_age <= CURRENT_TIME))
                 || memcmp(data,
                           &curr[1] + list->info.desc_size,
                           list->info.data_size)
                )
            )
        {
          curr = curr->next;
        }
      /* it is the current item -> return it */
      return (curr);
    };

static struct rsbac_list_item_t * lookup_item_data(
    struct rsbac_list_reg_item_t * list,
    void * data,
    rsbac_list_data_compare_function_t compare)
    {
      if(!list || !data)
        return NULL;

      if(compare)
        return lookup_item_data_compare(list, data, compare);
      else
        return lookup_item_data_memcmp(list, data);
    }

/* list of lists - subitems */

static struct rsbac_list_item_t * lookup_lol_subitem_compare(
    struct rsbac_list_lol_reg_item_t * list,
    struct rsbac_list_lol_item_t * sublist,
    void * subdesc)
    {
      struct rsbac_list_item_t  * curr;

      if(!list || !sublist || !subdesc || !list->subcompare)
        return NULL;

      curr = sublist->curr;
      if(!curr)
        {
          curr = sublist->head;
          if(!curr)
            return NULL;
        }
      /* if current item is not the right one, search... */
      /* note: item desc is behind official struct */
      if(list->subcompare(subdesc, &curr[1]))
        {
          if((list->subcompare(subdesc, &curr[1]) > 0))
            {
              curr = curr->next;
              while (   curr
                     && (list->subcompare(subdesc, &curr[1]) > 0)
                    )
                {
                  curr = curr->next;
                }
            }
          else
            {
              curr = curr->prev;
              while (   curr
                     && (list->subcompare(subdesc, &curr[1]) < 0)
                    )
                {
                  curr = curr->prev;
                }
            }
          if (curr)
            {
              /* keep for speedup */
              sublist->curr = curr;
              if(!list->subcompare(subdesc, &curr[1]))
                {
                  /* found */
                  return curr;
                }
            }
          /* not found */
          return NULL;
        }
      /* it is the current item -> return it */
      return (curr);
    };

static struct rsbac_list_item_t * lookup_lol_subitem_memcmp(
    struct rsbac_list_lol_reg_item_t * list,
    struct rsbac_list_lol_item_t * sublist,
    void * subdesc)
    {
      struct rsbac_list_item_t  * curr;

      if(!list || !sublist || !subdesc)
        return NULL;

      curr = sublist->curr;
      if(!curr)
        {
          curr = sublist->head;
          if(!curr)
            return NULL;
        }
      /* if current item is not the right one, search... */
      /* note: item desc is behind official struct */
      if(memcmp(subdesc,
                &curr[1],
                list->info.subdesc_size))
        {
          if(memcmp(subdesc,
                    &curr[1],
                    list->info.subdesc_size) > 0)
            {
              curr = curr->next;
              while (   curr
                     && (memcmp(subdesc,
                                &curr[1],
                                list->info.subdesc_size) > 0)
                    )
                {
                  curr = curr->next;
                }
            }
          else
            {
              curr = curr->prev;
              while (   curr
                     && (memcmp(subdesc,
                                &curr[1],
                                list->info.subdesc_size) < 0)
                    )
                {
                  curr = curr->prev;
                }
            }
          if (curr)
            {
              /* keep for speedup */
              sublist->curr = curr;
              if(!memcmp(subdesc,
                         &curr[1],
                         list->info.subdesc_size))
                {
                  /* found */
                  return curr;
                }
            }
          /* not found */
          return NULL;
        }
      /* it is the current item -> return it */
      return (curr);
    };

static struct rsbac_list_item_t * lookup_lol_subitem(
    struct rsbac_list_lol_reg_item_t * list,
    struct rsbac_list_lol_item_t * sublist,
    void * subdesc)
    {
      if(!list || !sublist || !subdesc)
        return NULL;

      if(list->subcompare)
        return lookup_lol_subitem_compare(list, sublist, subdesc);
      else
        return lookup_lol_subitem_memcmp(list, sublist, subdesc);
    }

/* list of lists - items */

static struct rsbac_list_lol_item_t * lookup_lol_item_compare(
    struct rsbac_list_lol_reg_item_t * list,
    void * desc)
    {
      struct rsbac_list_lol_item_t  * curr;

      if(!list || !desc || !list->compare)
        return NULL;

      curr = list->curr;
      if(!curr)
        {
          curr = list->head;
          if(!curr)
            return NULL;
        }
      /* if current item is not the right one, search... */
      /* note: item desc is behind official struct */
      if(list->compare(desc, &curr[1]))
        {
          if((list->compare(desc, &curr[1]) > 0))
            {
              curr = curr->next;
              while (   curr
                     && (list->compare(desc, &curr[1]) > 0)
                    )
                {
                  curr = curr->next;
                }
            }
          else
            {
              curr = curr->prev;
              while (   curr
                     && (list->compare(desc, &curr[1]) < 0)
                    )
                {
                  curr = curr->prev;
                }
            }
          if (curr)
            {
              /* keep for speedup */
              list->curr = curr;
              if(!list->compare(desc, &curr[1]))
                {
                  /* found */
                  return curr;
                }
            }
          /* not found */
          return NULL;
        }
      /* it is the current item -> return it */
      return (curr);
    };

static struct rsbac_list_lol_item_t * lookup_lol_item_memcmp(
    struct rsbac_list_lol_reg_item_t * list,
    void * desc)
    {
      struct rsbac_list_lol_item_t  * curr;

      if(!list || !desc)
        return NULL;

      curr = list->curr;
      if(!curr)
        {
          curr = list->head;
          if(!curr)
            return NULL;
        }
      /* if current item is not the right one, search... */
      /* note: item desc is behind official struct */
      if(memcmp(desc,
                &curr[1],
                list->info.desc_size))
        {
          if(memcmp(desc,
                    &curr[1],
                    list->info.desc_size) > 0)
            {
              curr = curr->next;
              while (   curr
                     && (memcmp(desc,
                                &curr[1],
                                list->info.desc_size) > 0)
                    )
                {
                  curr = curr->next;
                }
            }
          else
            {
              curr = curr->prev;
              while (   curr
                     && (memcmp(desc,
                                &curr[1],
                                list->info.desc_size) < 0)
                    )
                {
                  curr = curr->prev;
                }
            }
          if (curr)
            {
              /* keep for speedup */
              list->curr = curr;
              if(!memcmp(desc,
                         &curr[1],
                         list->info.desc_size))
                {
                  /* found */
                  return curr;
                }
            }
          /* not found */
          return NULL;
        }
      /* it is the current item -> return it */
      return (curr);
    };

static struct rsbac_list_lol_item_t * lookup_lol_item(
    struct rsbac_list_lol_reg_item_t * list,
    void * desc)
    {
      if(!list || !desc)
        return NULL;

      if(list->compare)
        return lookup_lol_item_compare(list, desc);
      else
        return lookup_lol_item_memcmp(list, desc);
    }

/* Registration lookup */

static struct rsbac_list_reg_item_t * lookup_reg(struct rsbac_list_reg_item_t *  handle)
    {
      struct rsbac_list_reg_item_t  * curr = reg_head.curr;
      
      if(!handle)
        return NULL;
      /* if there is no current item or it is not the right one, search... */
      if(curr != handle)
        {
          curr = reg_head.head;
          while (curr && curr != handle)
            {
              curr = curr->next;
            }
          if (curr)
            reg_head.curr=curr;
#ifdef CONFIG_RSBAC_DEBUG
          else
            if(rsbac_debug_lists)
              printk(KERN_DEBUG "lookup_reg(): Lookup of unknown list handle %p\n",
                     handle);
#endif
        }
      /* it is the current item -> return it */
      return (curr);
    };

static struct rsbac_list_reg_item_t * lookup_reg_name(char * name, kdev_t device)
    {
      struct rsbac_list_reg_item_t  * curr = reg_head.curr;
      
      if(!name)
        return NULL;
      /* if there is no current item or it is not the right one, search... */
      if(   !curr
         || (   strncmp(curr->name, name, RSBAC_LIST_MAX_FILENAME)
             || (curr->device != device)
            )
        )
        {
          curr = reg_head.head;
          while (   curr
                 && (   strncmp(curr->name, name, RSBAC_LIST_MAX_FILENAME)
                     || (curr->device != device)
                    )
                )
            {
              curr = curr->next;
            }
          if (curr)
            reg_head.curr=curr;
#ifdef CONFIG_RSBAC_DEBUG
          else
            if(rsbac_debug_lists)
              printk(KERN_DEBUG "lookup_reg_name(): Lookup of unknown list name %s on device %02u:%02u\n",
                     name, MAJOR(device), MINOR(device));
#endif
        }
      /* it is the current item -> return it */
      return (curr);
    };

/* List of lists registration lookup */

static struct rsbac_list_lol_reg_item_t * lookup_lol_reg(struct rsbac_list_lol_reg_item_t *  handle)
    {
      struct rsbac_list_lol_reg_item_t  * curr = lol_reg_head.curr;
      
      if(!handle)
        return NULL;
      /* if there is no current item or it is not the right one, search... */
      if(curr != handle)
        {
          curr = lol_reg_head.head;
          while (curr && curr != handle)
            {
              curr = curr->next;
            }
          if (curr)
            lol_reg_head.curr=curr;
#ifdef CONFIG_RSBAC_DEBUG
          else
            if(rsbac_debug_lists)
              printk(KERN_DEBUG "lookup_lol_reg(): Lookup of unknown list handle %p\n",
                     handle);
#endif
        }
      /* it is the current item -> return it */
      return (curr);
    };

static struct rsbac_list_lol_reg_item_t * lookup_lol_reg_name(char * name, kdev_t device)
    {
      struct rsbac_list_lol_reg_item_t  * curr = lol_reg_head.curr;
      
      if(!name)
        return NULL;
      /* if there is no current item or it is not the right one, search... */
      if(   !curr
         || (   strncmp(curr->name, name, RSBAC_LIST_MAX_FILENAME)
             || (curr->device != device)
            )
        )
        {
          curr = lol_reg_head.head;
          while (   curr
                 && (   strncmp(curr->name, name, RSBAC_LIST_MAX_FILENAME)
                     || (curr->device != device)
                    )
                )
            {
              curr = curr->next;
            }
          if (curr)
            lol_reg_head.curr=curr;
#ifdef CONFIG_RSBAC_DEBUG
          else
            if(rsbac_debug_lists)
              printk(KERN_DEBUG "lookup_lol_reg_name(): Lookup of unknown list name %s on device %02u:%02u\n",
                     name, MAJOR(device), MINOR(device));
#endif
        }
      /* it is the current item -> return it */
      return (curr);
    };

/*************/
/* Add items */

static struct rsbac_list_item_t * insert_item_compare(
    struct rsbac_list_reg_item_t * list,
    void * desc,
    struct rsbac_list_item_t * new_item_p)
    {
      struct rsbac_list_item_t * curr;

      curr = list->curr;
      if(!curr)
        curr = list->head;
      if((list->compare(desc, &curr[1]) > 0))
        {
          curr = curr->next;
          while (   curr
                 && (list->compare(desc, &curr[1]) > 0)
                )
            {
              curr = curr->next;
            }
          if (curr)
            {
              /* insert before curr */
              new_item_p->prev=curr->prev;
              new_item_p->next=curr;
              curr->prev->next=new_item_p;
              curr->prev=new_item_p;
            }
          else
            {
              /* insert as last item */
              new_item_p->prev=list->tail;
              new_item_p->next=NULL;
              list->tail->next=new_item_p;
              list->tail=new_item_p;
            }
        }
      else
        {
          curr = curr->prev;
          while (   curr
                 && (list->compare(desc, &curr[1]) < 0)
                )
            {
              curr = curr->prev;
            }
          if (curr)
            {
              /* insert after curr */
              new_item_p->prev=curr;
              new_item_p->next=curr->next;
              curr->next->prev=new_item_p;
              curr->next=new_item_p;
            }
          else
            {
              /* insert as first item */
              new_item_p->prev=NULL;
              new_item_p->next=list->head;
              list->head->prev=new_item_p;
              list->head=new_item_p;
            }
        }
      list->count++;
      list->curr=new_item_p;
      return(new_item_p);
    }

static struct rsbac_list_item_t * insert_item_memcmp(
    struct rsbac_list_reg_item_t * list,
    void * desc,
    struct rsbac_list_item_t * new_item_p)
    {
      struct rsbac_list_item_t * curr;

      curr = list->curr;
      if(!curr)
        curr = list->head;
      if(memcmp(desc,
                &curr[1],
                list->info.desc_size) > 0)
        {
          curr = curr->next;
          while (   curr
                 && (memcmp(desc,
                           &curr[1],
                           list->info.desc_size) > 0
                    )
                )
            {
              curr = curr->next;
            }
          if (curr)
            {
              /* insert before curr */
              new_item_p->prev=curr->prev;
              new_item_p->next=curr;
              curr->prev->next=new_item_p;
              curr->prev=new_item_p;
            }
          else
            {
              /* insert as last item */
              new_item_p->prev=list->tail;
              new_item_p->next=NULL;
              list->tail->next=new_item_p;
              list->tail=new_item_p;
            }
        }
      else
        {
          curr = curr->prev;
          while (   curr
                 && (memcmp(desc,
                           &curr[1],
                           list->info.desc_size) < 0
                    )
                )
            {
              curr = curr->prev;
            }
          if (curr)
            {
              /* insert after curr */
              new_item_p->prev=curr;
              new_item_p->next=curr->next;
              curr->next->prev=new_item_p;
              curr->next=new_item_p;
            }
          else
            {
              /* insert as first item */
              new_item_p->prev=NULL;
              new_item_p->next=list->head;
              list->head->prev=new_item_p;
              list->head=new_item_p;
            }
        }
      list->count++;
      list->curr=new_item_p;
      return(new_item_p);
    }

static struct rsbac_list_item_t * add_item(
    struct rsbac_list_reg_item_t * list,
    rsbac_time_t max_age,
    void * desc,
    void * data)
    {
      struct rsbac_list_item_t * new_item_p = NULL;

      if(!list || !desc)
        return NULL;

      if(!list || !desc)
        return NULL;
      if(list->info.data_size && !data)
        return NULL;
      /* item desc and data are behind official struct */
      if ( !(new_item_p = rsbac_kmalloc(sizeof(*new_item_p) + list->info.desc_size + list->info.data_size)) )
        return(NULL);
      new_item_p->max_age = max_age;
      /* item desc is behind official struct */
      memcpy(&new_item_p[1],
             desc, list->info.desc_size);
      /* item data is behind official struct and desc */
      /* data might be empty! */
      if(data && list->info.data_size)
        memcpy(((__u8 *) new_item_p) + sizeof(*new_item_p) + list->info.desc_size,
               data,
               list->info.data_size);

      if (!list->head)
        {
          list->head=new_item_p;
          list->tail=new_item_p;
          list->curr=new_item_p;
          list->count = 1;
          new_item_p->prev=NULL;
          new_item_p->next=NULL;
          return new_item_p;
        }
      if(list->compare)
        return insert_item_compare(list, desc, new_item_p);
      else
        return insert_item_memcmp(list, desc, new_item_p);
    }


static struct rsbac_list_item_t * insert_lol_subitem_compare(
    struct rsbac_list_lol_reg_item_t * list,
    struct rsbac_list_lol_item_t * sublist,
    void * subdesc,
    struct rsbac_list_item_t * new_item_p)
    {
      struct rsbac_list_item_t * curr;

      curr = sublist->curr;
      if(!curr)
        curr = sublist->head;
      if((list->subcompare(subdesc, &curr[1]) > 0))
        {
          curr = curr->next;
          while (   curr
                 && (list->subcompare(subdesc, &curr[1]) > 0)
                )
            {
              curr = curr->next;
            }
          if (curr)
            {
              /* insert before curr */
              new_item_p->prev=curr->prev;
              new_item_p->next=curr;
              curr->prev->next=new_item_p;
              curr->prev=new_item_p;
            }
          else
            {
              /* insert as last item */
              new_item_p->prev=sublist->tail;
              new_item_p->next=NULL;
              sublist->tail->next=new_item_p;
              sublist->tail=new_item_p;
            }
        }
      else
        {
          curr = curr->prev;
          while (   curr
                 && (list->subcompare(subdesc, &curr[1]) < 0)
                )
            {
              curr = curr->prev;
            }
          if (curr)
            {
              /* insert after curr */
              new_item_p->prev=curr;
              new_item_p->next=curr->next;
              curr->next->prev=new_item_p;
              curr->next=new_item_p;
            }
          else
            {
              /* insert as first item */
              new_item_p->prev=NULL;
              new_item_p->next=sublist->head;
              sublist->head->prev=new_item_p;
              sublist->head=new_item_p;
            }
        }
      sublist->count++;
      sublist->curr=new_item_p;
      return(new_item_p);
    }

static struct rsbac_list_item_t * insert_lol_subitem_memcmp(
    struct rsbac_list_lol_reg_item_t * list,
    struct rsbac_list_lol_item_t * sublist,
    void * subdesc,
    struct rsbac_list_item_t * new_item_p)
    {
      struct rsbac_list_item_t * curr;

      curr = sublist->curr;
      if(!curr)
        curr = sublist->head;
      if(memcmp(subdesc,
                &curr[1],
                list->info.subdesc_size) > 0)
        {
          curr = curr->next;
          while (   curr
                 && (memcmp(subdesc,
                           &curr[1],
                           list->info.subdesc_size) > 0
                    )
                )
            {
              curr = curr->next;
            }
          if (curr)
            {
              /* insert before curr */
              new_item_p->prev=curr->prev;
              new_item_p->next=curr;
              curr->prev->next=new_item_p;
              curr->prev=new_item_p;
            }
          else
            {
              /* insert as last item */
              new_item_p->prev=sublist->tail;
              new_item_p->next=NULL;
              sublist->tail->next=new_item_p;
              sublist->tail=new_item_p;
            }
        }
      else
        {
          curr = curr->prev;
          while (   curr
                 && (memcmp(subdesc,
                           &curr[1],
                           list->info.subdesc_size) < 0
                    )
                )
            {
              curr = curr->prev;
            }
          if (curr)
            {
              /* insert after curr */
              new_item_p->prev=curr;
              new_item_p->next=curr->next;
              curr->next->prev=new_item_p;
              curr->next=new_item_p;
            }
          else
            {
              /* insert as first item */
              new_item_p->prev=NULL;
              new_item_p->next=sublist->head;
              sublist->head->prev=new_item_p;
              sublist->head=new_item_p;
            }
        }
      sublist->count++;
      sublist->curr=new_item_p;
      return(new_item_p);
    }

static struct rsbac_list_item_t * add_lol_subitem(
    struct rsbac_list_lol_reg_item_t * list,
    struct rsbac_list_lol_item_t     * sublist,
    rsbac_time_t max_age,
    void * subdesc,
    void * subdata)
    {
      struct rsbac_list_item_t * new_item_p = NULL;

      if(!list || !sublist || !subdesc)
        return NULL;
      if(list->info.data_size && !subdata)
        return NULL;
      /* item desc and data are behind official struct */
      if ( !(new_item_p = rsbac_kmalloc(sizeof(*new_item_p) + list->info.subdesc_size + list->info.subdata_size)) )
        return(NULL);
      new_item_p->max_age = max_age;
      /* item desc is behind official struct */
      memcpy(&new_item_p[1],
             subdesc,
             list->info.subdesc_size);
      /* item data is behind official struct and desc */
      /* subdata might be empty! */
      if(subdata && list->info.subdata_size)
        memcpy(((__u8 *) new_item_p) + sizeof(*new_item_p) + list->info.subdesc_size,
               subdata,
               list->info.subdata_size);

      if (!sublist->head)
        {
          sublist->head=new_item_p;
          sublist->tail=new_item_p;
          sublist->curr=new_item_p;
          sublist->count = 1;
          new_item_p->prev=NULL;
          new_item_p->next=NULL;          
          return(new_item_p);
        }  
      if(list->subcompare)
        return insert_lol_subitem_compare(list, sublist, subdesc, new_item_p);
      else
        return insert_lol_subitem_memcmp(list, sublist, subdesc, new_item_p);
    }

static struct rsbac_list_lol_item_t * insert_lol_item_compare(
    struct rsbac_list_lol_reg_item_t * list,
    void * desc,
    struct rsbac_list_lol_item_t * new_item_p)
    {
      struct rsbac_list_lol_item_t * curr;

      curr = list->curr;
      if(!curr)
        curr = list->head;
      if((list->compare(desc, &curr[1]) > 0))
        {
          curr = curr->next;
          while (   curr
                 && (list->compare(desc, &curr[1]) > 0)
                )
            {
              curr = curr->next;
            }
          if (curr)
            {
              /* insert before curr */
              new_item_p->prev=curr->prev;
              new_item_p->next=curr;
              curr->prev->next=new_item_p;
              curr->prev=new_item_p;
            }
          else
            {
              /* insert as last item */
              new_item_p->prev=list->tail;
              new_item_p->next=NULL;
              list->tail->next=new_item_p;
              list->tail=new_item_p;
            }
        }
      else
        {
          curr = curr->prev;
          while (   curr
                 && (list->compare(desc, &curr[1]) < 0)
                )
            {
              curr = curr->prev;
            }
          if (curr)
            {
              /* insert after curr */
              new_item_p->prev=curr;
              new_item_p->next=curr->next;
              curr->next->prev=new_item_p;
              curr->next=new_item_p;
            }
          else
            {
              /* insert as first item */
              new_item_p->prev=NULL;
              new_item_p->next=list->head;
              list->head->prev=new_item_p;
              list->head=new_item_p;
            }
        }
      list->count++;
      list->curr=new_item_p;
      return(new_item_p);
    }

static struct rsbac_list_lol_item_t * insert_lol_item_memcmp(
    struct rsbac_list_lol_reg_item_t * list,
    void * desc,
    struct rsbac_list_lol_item_t * new_item_p)
    {
      struct rsbac_list_lol_item_t * curr;

      curr = list->curr;
      if(!curr)
        curr = list->head;
      if(memcmp(desc,
                &curr[1],
                list->info.desc_size) > 0)
        {
          curr = curr->next;
          while (   curr
                 && (memcmp(desc,
                           &curr[1],
                           list->info.desc_size) > 0
                    )
                )
            {
              curr = curr->next;
            }
          if (curr)
            {
              /* insert before curr */
              new_item_p->prev=curr->prev;
              new_item_p->next=curr;
              curr->prev->next=new_item_p;
              curr->prev=new_item_p;
            }
          else
            {
              /* insert as last item */
              new_item_p->prev=list->tail;
              new_item_p->next=NULL;
              list->tail->next=new_item_p;
              list->tail=new_item_p;
            }
        }
      else
        {
          curr = curr->prev;
          while (   curr
                 && (memcmp(desc,
                           &curr[1],
                           list->info.desc_size) < 0
                    )
                )
            {
              curr = curr->prev;
            }
          if (curr)
            {
              /* insert after curr */
              new_item_p->prev=curr;
              new_item_p->next=curr->next;
              curr->next->prev=new_item_p;
              curr->next=new_item_p;
            }
          else
            {
              /* insert as first item */
              new_item_p->prev=NULL;
              new_item_p->next=list->head;
              list->head->prev=new_item_p;
              list->head=new_item_p;
            }
        }
      list->count++;
      list->curr=new_item_p;
      return(new_item_p);
    }

static struct rsbac_list_lol_item_t * add_lol_item(
    struct rsbac_list_lol_reg_item_t * list,
    rsbac_time_t max_age,
    void * desc,
    void * data)
    {
      struct rsbac_list_lol_item_t * new_item_p = NULL;

      if(!list || !desc)
        return NULL;
      if(list->info.data_size && !data)
        return NULL;
      /* item desc and data are behind official struct */
      if ( !(new_item_p = rsbac_kmalloc(sizeof(*new_item_p) + list->info.desc_size + list->info.data_size)) )
        return(NULL);
      /* Init sublist */
      new_item_p->head = NULL;
      new_item_p->tail = NULL;
      new_item_p->curr = NULL;
      new_item_p->count = 0;
      new_item_p->max_age = max_age;
      /* item desc is behind official struct */
      memcpy(&new_item_p[1],
             desc,
             list->info.desc_size);
      /* item data is behind official struct and desc */
      /* data might be empty! */
      if(data && list->info.data_size)
        memcpy(((__u8 *) new_item_p) + sizeof(*new_item_p) + list->info.desc_size,
               data,
               list->info.data_size);

      if (!list->head)
        {
          list->head=new_item_p;
          list->tail=new_item_p;
          list->curr=new_item_p;
          list->count = 1;
          new_item_p->prev=NULL;
          new_item_p->next=NULL;          
          return(new_item_p);
        }
      if(list->compare)
        return insert_lol_item_compare(list, desc, new_item_p);
      else
        return insert_lol_item_memcmp(list, desc, new_item_p);
    }

/* Add registration items */

/* no locking needed */
static struct rsbac_list_reg_item_t* 
         create_reg(struct rsbac_list_info_t             info,
                           u_int                         flags,
                           rsbac_list_compare_function_t * compare,
                           rsbac_list_get_conv_t       * get_conv,
                           void                        * def_data,
                           char                        * name,
                           kdev_t                        device)
    {
      struct rsbac_list_reg_item_t * new_item_p = NULL;

      if ( !(new_item_p = rsbac_kmalloc(sizeof(*new_item_p))) )
        return(NULL);
      new_item_p->info = info;
      if(!def_data)
        flags &= ~RSBAC_LIST_DEF_DATA;
      new_item_p->flags = flags;
      new_item_p->compare = compare;
      new_item_p->get_conv = get_conv;
      if(flags & RSBAC_LIST_DEF_DATA)
        {
          new_item_p->def_data = rsbac_kmalloc(info.data_size);
          if(new_item_p->def_data)
            memcpy(new_item_p->def_data, def_data, info.data_size);
          else
            {
              rsbac_kfree(new_item_p);
              return NULL;
            }
        }
      else
        new_item_p->def_data = NULL;
      if(name)
        {
          strncpy(new_item_p->name, name, RSBAC_LIST_MAX_FILENAME);
          new_item_p->name[RSBAC_LIST_MAX_FILENAME] = 0;
        }
      else
        {
          strcpy(new_item_p->name, RSBAC_LIST_NONAME);
        }
      new_item_p->device = device;
      new_item_p->head   = NULL;
      new_item_p->tail   = NULL;
      new_item_p->curr   = NULL;
      new_item_p->lock   = RW_LOCK_UNLOCKED;
      new_item_p->count  = 0;
      new_item_p->dirty  = FALSE;
      if(flags & RSBAC_LIST_NO_WRITE)
        new_item_p->no_write = TRUE;
      else
        new_item_p->no_write = FALSE;
      new_item_p->self = new_item_p;
      return(new_item_p);
    };

/* locking needed */
static struct rsbac_list_reg_item_t* 
         add_reg(struct rsbac_list_reg_item_t* new_item_p)
    {
      if (!reg_head.head)
        {
          reg_head.head=new_item_p;
          reg_head.tail=new_item_p;
          reg_head.curr=new_item_p;
          reg_head.count = 1;
          new_item_p->prev=NULL;
          new_item_p->next=NULL;          
        }  
      else
        {
          new_item_p->prev=reg_head.tail;
          new_item_p->next=NULL;
          reg_head.tail->next=new_item_p;
          reg_head.tail=new_item_p;
          reg_head.curr=new_item_p;
          reg_head.count++;
        };
      return(new_item_p);
    };

/* no locking needed */
static struct rsbac_list_lol_reg_item_t* 
     create_lol_reg(struct rsbac_list_lol_info_t         info,
                           u_int                         flags,
                           rsbac_list_compare_function_t * compare,
                           rsbac_list_compare_function_t * subcompare,
                           rsbac_list_get_conv_t       * get_conv,
                           rsbac_list_get_conv_t       * get_subconv,
                           void                        * def_data,
                           void                        * def_subdata,
                           char                        * name,
                           kdev_t                        device)
    {
      struct rsbac_list_lol_reg_item_t * new_item_p = NULL;

      if ( !(new_item_p = rsbac_kmalloc(sizeof(*new_item_p))) )
        return(NULL);
      new_item_p->info = info;
      if(info.data_size && !def_data)
        flags &= ~RSBAC_LIST_DEF_DATA;
      if(!def_subdata)
        flags &= ~RSBAC_LIST_DEF_SUBDATA;
      new_item_p->flags = flags;
      new_item_p->compare = compare;
      new_item_p->subcompare = subcompare;
      new_item_p->get_conv = get_conv;
      new_item_p->get_subconv = get_subconv;
      if(   (flags & RSBAC_LIST_DEF_DATA)
         && (info.data_size)
        )
        {
          new_item_p->def_data = rsbac_kmalloc(info.data_size);
          if(new_item_p->def_data)
            memcpy(new_item_p->def_data, def_data, info.data_size);
          else
            {
              rsbac_kfree(new_item_p);
              return NULL;
            }
        }
      else
        new_item_p->def_data = NULL;
      if(flags & RSBAC_LIST_DEF_SUBDATA)
        {
          new_item_p->def_subdata = rsbac_kmalloc(info.subdata_size);
          if(new_item_p->def_subdata)
            memcpy(new_item_p->def_subdata, def_subdata, info.subdata_size);
          else
            {
              if(new_item_p->def_data)
                rsbac_kfree(new_item_p->def_data);
              rsbac_kfree(new_item_p);
              return NULL;
            }
        }
      else
        new_item_p->def_subdata = NULL;
      if(name)
        {
          strncpy(new_item_p->name, name, RSBAC_LIST_MAX_FILENAME);
          new_item_p->name[RSBAC_LIST_MAX_FILENAME] = 0;
        }
      else
        {
          strcpy(new_item_p->name, RSBAC_LIST_NONAME);
        }
      new_item_p->device = device;
      new_item_p->head   = NULL;
      new_item_p->tail   = NULL;
      new_item_p->curr   = NULL;
      new_item_p->lock   = RW_LOCK_UNLOCKED;
      new_item_p->count  = 0;
      new_item_p->dirty  = FALSE;
      if(flags & RSBAC_LIST_NO_WRITE)
        new_item_p->no_write = TRUE;
      else
        new_item_p->no_write = FALSE;
      new_item_p->self = new_item_p;
      return(new_item_p);
    };

/* locking needed */
static struct rsbac_list_lol_reg_item_t* 
     add_lol_reg(struct rsbac_list_lol_reg_item_t * new_item_p)
    {
      if (!lol_reg_head.head)
        {
          lol_reg_head.head=new_item_p;
          lol_reg_head.tail=new_item_p;
          lol_reg_head.curr=new_item_p;
          lol_reg_head.count = 1;
          new_item_p->prev=NULL;
          new_item_p->next=NULL;          
        }  
      else
        {
          new_item_p->prev=lol_reg_head.tail;
          new_item_p->next=NULL;
          lol_reg_head.tail->next=new_item_p;
          lol_reg_head.tail=new_item_p;
          lol_reg_head.curr=new_item_p;
          lol_reg_head.count++;
        };
      return(new_item_p);
    };

/* Removing items */

static void do_remove_item(
    struct rsbac_list_reg_item_t * list,
    struct rsbac_list_item_t * item_p)
  {
    if(!list || !item_p)
      return;

    if ( (list->head == item_p) )
       { /* item is head */
         if ( (list->tail == item_p) )
           { /* item is head and tail = only item -> list will be empty*/
             list->head = NULL;
             list->tail = NULL;
           }
         else
           { /* item is head, but not tail -> next item becomes head */
             item_p->next->prev = NULL;
             list->head = item_p->next;
           };
       }
    else
       { /* item is not head */
         if ( (list->tail == item_p) )
           { /*item is not head, but tail -> previous item becomes tail*/
             item_p->prev->next = NULL;
             list->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 */
    list->curr=NULL;
    /* adjust counter */
    list->count--;
    /* now we can remove the item from memory */
    rsbac_kfree(item_p);    
  }; /* end of do_remove_item() */

static void remove_item(
    struct rsbac_list_reg_item_t * list,
    void * desc)
  {
    struct rsbac_list_item_t * item_p;

    if(!list || !desc)
      return;
    /* first we must locate the item. */
    if ( (item_p = lookup_item(list, desc)) )
      {
        do_remove_item(list, item_p);
      }
  }; /* end of remove_item() */

static void remove_all_items(struct rsbac_list_reg_item_t * list)
    {
      struct rsbac_list_item_t     * item_p;
      struct rsbac_list_item_t     * next_item_p;

      /* cleanup all items */
      item_p = list->head;
      while(item_p)
        {
          next_item_p = item_p->next;
          rsbac_kfree(item_p);
          item_p = next_item_p;
        }
      list->head = NULL;
      list->tail = NULL;
      list->curr = NULL;
      list->count = 0;
    }; /* end of remove_all_items() */


static void do_remove_lol_subitem(
    struct rsbac_list_lol_item_t * sublist,
    struct rsbac_list_item_t     * item_p)
  {
    if(!sublist || !item_p)
      return;

    if ( (sublist->head == item_p) )
      { /* item is head */
        if ( (sublist->tail == item_p) )
          { /* item is head and tail = only item -> list will be empty*/
            sublist->head = NULL;
            sublist->tail = NULL;
          }
        else
          { /* item is head, but not tail -> next item becomes head */
            item_p->next->prev = NULL;
            sublist->head = item_p->next;
          };
      }
    else
      { /* item is not head */
        if ( (sublist->tail == item_p) )
          { /*item is not head, but tail -> previous item becomes tail*/
            item_p->prev->next = NULL;
            sublist->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 */
    sublist->curr=NULL;
    /* adjust counter */
    sublist->count--;
    /* now we can remove the item from memory */
    rsbac_kfree(item_p);    
  }; /* end of do_remove_lol_subitem() */

static void remove_lol_subitem(
    struct rsbac_list_lol_reg_item_t * list,
    struct rsbac_list_lol_item_t     * sublist,
    void * subdesc)
  {
    struct rsbac_list_item_t     * item_p;

    if(!list || !sublist || !subdesc)
      return;

    /* first we must locate the item. */
    if ( (item_p = lookup_lol_subitem(list, sublist, subdesc)) )
      {
        do_remove_lol_subitem(sublist, item_p);
      }
  }; /* end of remove_lol_subitem() */


static void do_remove_lol_item(
    struct rsbac_list_lol_reg_item_t * list,
    struct rsbac_list_lol_item_t * item_p)
  {
    struct rsbac_list_item_t     * subitem_p;
    struct rsbac_list_item_t     * next_subitem_p;

    if(!list || !item_p)
      return;

    if ( (list->head == item_p) )
       { /* item is head */
         if ( (list->tail == item_p) )
           { /* item is head and tail = only item -> list will be empty*/
             list->head = NULL;
             list->tail = NULL;
           }
         else
           { /* item is head, but not tail -> next item becomes head */
             item_p->next->prev = NULL;
             list->head = item_p->next;
           };
       }
    else
       { /* item is not head */
         if ( (list->tail == item_p) )
           { /*item is not head, but tail -> previous item becomes tail*/
             item_p->prev->next = NULL;
             list->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 */
    list->curr=NULL;
    /* adjust counter */
    list->count--;
    /* first remove subitems */
    subitem_p = item_p->head;
    while(subitem_p)
      {
        next_subitem_p = subitem_p->next;
        rsbac_kfree(subitem_p);
        subitem_p = next_subitem_p;
      }
    /* now we can remove the item from memory */
    rsbac_kfree(item_p);    
  };

static void remove_lol_item(
    struct rsbac_list_lol_reg_item_t * list,
    void * desc)
  {
    struct rsbac_list_lol_item_t * item_p;

    if(!list || !desc)
      return;

    /* first we must locate the item. */
    if ( (item_p = lookup_lol_item(list, desc)) )
      {
        do_remove_lol_item(list, item_p);
      }
  }

static void remove_all_lol_subitems(
    struct rsbac_list_lol_item_t     * sublist)
    {
      struct rsbac_list_item_t     * subitem_p;
      struct rsbac_list_item_t     * next_subitem_p;

      /* cleanup all items */
      subitem_p = sublist->head;
      while(subitem_p)
        {
          next_subitem_p = subitem_p->next;
          rsbac_kfree(subitem_p);
          subitem_p = next_subitem_p;
        }
      sublist->head = NULL;
      sublist->tail = NULL;
      sublist->curr = NULL;
      sublist->count = 0;
    }; /* end of remove_all_items() */

static void remove_all_lol_items(struct rsbac_list_lol_reg_item_t * list)
    {
      struct rsbac_list_lol_item_t * item_p;
      struct rsbac_list_lol_item_t * next_item_p;
      struct rsbac_list_item_t     * subitem_p;
      struct rsbac_list_item_t     * next_subitem_p;

      /* cleanup all items */
      item_p = list->head;
      while(item_p)
        {
          /* first remove subitems */
          subitem_p = item_p->head;
          while(subitem_p)
            {
              next_subitem_p = subitem_p->next;
              rsbac_kfree(subitem_p);
              subitem_p = next_subitem_p;
            }
          next_item_p = item_p->next;
          rsbac_kfree(item_p);
          item_p = next_item_p;
        }
      list->head = NULL;
      list->tail = NULL;
      list->curr = NULL;
      list->count = 0;
    }; /* end of remove_all_items() */

/* Remove registration items */

/* no locking needed */
static void clear_reg(struct rsbac_list_reg_item_t * item_p)
    {
      if(item_p)
        {
          /* now we can remove the item from memory */
          remove_all_items(item_p);
          if(item_p->def_data)
            rsbac_kfree(item_p->def_data);
          rsbac_kfree(item_p);
        }
    };

/* locking needed */
static void remove_reg(struct rsbac_list_reg_item_t *  handle)
    {
      struct rsbac_list_reg_item_t * item_p;

      /* first we must locate the item. */
      if ( (item_p = lookup_reg(handle)) )
        { /* ok, item was found */
          /* protect against reuse */
          item_p->self = NULL;
          if ( (reg_head.head == item_p) )
             { /* item is head */
               if ( (reg_head.tail == item_p) )
                 { /* item is head and tail = only item -> list will be empty*/
                   reg_head.head = NULL;
                   reg_head.tail = NULL;
                 }
               else
                 { /* item is head, but not tail -> next item becomes head */
                   item_p->next->prev = NULL;
                   reg_head.head = item_p->next;
                 };
             }
          else
             { /* item is not head */
               if ( (reg_head.tail == item_p) )
                 { /*item is not head, but tail -> previous item becomes tail*/
                   item_p->prev->next = NULL;
                   reg_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 */
          reg_head.curr=NULL;
          /* adjust counter */
          reg_head.count--;
          /* now we can remove the item from memory */
          clear_reg(item_p);
        };  /* end of if: item was found */
    }; /* end of remove_reg() */

/* no locking needed */
static void clear_lol_reg(struct rsbac_list_lol_reg_item_t * item_p)
    {
      if(item_p)
        {
          /* now we can remove the item from memory */
          remove_all_lol_items(item_p);
          if(item_p->def_data)
            rsbac_kfree(item_p->def_data);
          if(item_p->def_subdata)
            rsbac_kfree(item_p->def_subdata);
          rsbac_kfree(item_p);    
        };
    };

/* locking needed */
static void remove_lol_reg(struct rsbac_list_lol_reg_item_t *  handle)
    {
      struct rsbac_list_lol_reg_item_t * item_p;

      /* first we must locate the item. */
      if ( (item_p = lookup_lol_reg(handle)) )
        { /* ok, item was found */
          /* protect against reuse */
          item_p->self = NULL;
          if ( (lol_reg_head.head == item_p) )
             { /* item is head */
               if ( (lol_reg_head.tail == item_p) )
                 { /* item is head and tail = only item -> list will be empty*/
                   lol_reg_head.head = NULL;
                   lol_reg_head.tail = NULL;
                 }
               else
                 { /* item is head, but not tail -> next item becomes head */
                   item_p->next->prev = NULL;
                   lol_reg_head.head = item_p->next;
                 };
             }
          else
             { /* item is not head */
               if ( (lol_reg_head.tail == item_p) )
                 { /*item is not head, but tail -> previous item becomes tail*/
                   item_p->prev->next = NULL;
                   lol_reg_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 */
          lol_reg_head.curr=NULL;
          /* adjust counter */
          lol_reg_head.count--;
          /* now we can remove the item from memory */
          clear_lol_reg(item_p);
        };  /* end of if: item was found */
    }; /* end of remove_lol_reg() */


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

static int read_list(struct rsbac_list_reg_item_t * list)
  {
    struct file                     file;
    int                             err = 0;
    int                             tmperr;
    int                             converr;
    rsbac_version_t                 list_version;
    u_long                          read_count = 0;
    u_long                          flags;
    char                          * old_buf;
    char                          * new_buf;
    char                          * old_data;
    char                          * new_data;
    struct rsbac_list_info_t        list_info;
    rsbac_list_count_t              list_count;
    rsbac_time_t                    timestamp;
    rsbac_time_t                    max_age = 0;
    rsbac_list_conv_function_t    * conv = NULL;
    boolean                         timeout = FALSE;
    mm_segment_t                    oldfs;

    /* open file */
    if ((err = rsbac_read_open(list->name,
                               &file,
                               list->device) ))
      return(err);

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

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

    /* check gen-list on-disk version */
    tmperr = file.f_op->read(&file,
                             (__u8 *) &list_version,
                             sizeof(list_version),
                             &file.f_pos);
    /* error? */
    if (tmperr < sizeof(list_version))
      {
        printk(KERN_WARNING
               "read_list(): read error from file!\n");
        err = -RSBAC_EREADFAILED;
        goto end_read;
      }
    /* if wrong list on-disk version, fail */
    switch(list_version)
      {
        case RSBAC_LIST_DISK_VERSION:
        case RSBAC_LIST_DISK_OLD_VERSION:
            break;
        default:
            printk(KERN_WARNING
                   "read_list(): wrong on-disk list version %u in file %s, expected %u - error!\n",
                   list_version,
                   list->name,
                   RSBAC_LIST_DISK_VERSION);
            err = -RSBAC_EREADFAILED;
            goto end_read;
      }

    /* get timestamp */
    tmperr = file.f_op->read(&file,
                             (__u8 *) &timestamp,
                             sizeof(timestamp),
                             &file.f_pos);
    /* error? */
    if (tmperr < sizeof(timestamp))
      {
        printk(KERN_WARNING
               "read_list(): timestamp read error from file %s!\n",
               list->name);
        err = -RSBAC_EREADFAILED;
        goto end_read;
      }

    /* get list info */
    tmperr = file.f_op->read(&file,
                             (__u8 *) &list_info,
                             sizeof(list_info),
                             &file.f_pos);
    /* error? */
    if (tmperr < sizeof(list_info))
      {
        printk(KERN_WARNING
               "read_list(): list info read error from file %s!\n",
               list->name);
        err = -RSBAC_EREADFAILED;
        goto end_read;
      }

    /* list timed out? System time is measured in seconds. */
    if(   list_info.max_age
       && (timestamp + list_info.max_age) <= CURRENT_TIME)
      timeout = TRUE;

    /* Valid key? */
    if (list_info.key != list->info.key)
      {
        if(timeout)
          {
            printk(KERN_WARNING
                   "read_list(): accessing timed out list %s with wrong key, ignoring old contents!\n",
                   list->name);
            goto end_read;
          }
        else
          {
            printk(KERN_WARNING
                   "read_list(): try to access list %s with wrong key!\n",
                   list->name);
            err = -EPERM;
            goto end_read;
          }
      }

    /* skip the rest, it ignore is requested */
    if(list->flags & RSBAC_LIST_IGNORE_OLD)
      goto end_read;

    /* if wrong list version, try to get_conv */
    if(list_info.version != list->info.version)
      {
        if(list->get_conv)
          conv = list->get_conv(list_info.version);
        if(!conv)
          {
            if(timeout)
              {
                printk(KERN_WARNING
                       "read_list(): accessing timed out list %s without conversion function, ignoring old contents!\n",
                       list->name);
                goto end_read;
              }
            else
              {
                /* complain and set error, if ignore is not requested */
                if(!(list->flags & RSBAC_LIST_IGNORE_UNSUPP_VERSION))
                  {
                    printk(KERN_WARNING
                           "read_list(): cannot convert list version %u of file %s to version %u!\n",
                           list_info.version,
                           list->name,
                           list->info.version);
                    err = -RSBAC_EINVALIDVERSION;
                  }
                goto end_read;
              }
          }
        else
          {
            printk(KERN_WARNING
                   "read_list(): converting list version %u of file %s on device %02u:%02u to version %u!\n",
                   list_info.version,
                   list->name,
                   MAJOR(list->device), MINOR(list->device),
                   list->info.version);
          }
      }
    else /* same version needs same sizes */
      {
        if(   (list_info.desc_size != list->info.desc_size)
           || (list_info.data_size != list->info.data_size)
          )
          {
            if(timeout)
              {
                printk(KERN_WARNING
                       "read_list(): accessing timed out list %s with wrong desc or data size, ignoring old contents!\n",
                       list->name);
                goto end_read;
              }
            else
              {
                printk(KERN_WARNING
                       "read_list(): desc or data size mismatch on list %s!\n",
                       list->name);
                err = -RSBAC_EINVALIDVALUE;
                goto end_read;
              }
          }
      }

    /* get list count */
    tmperr = file.f_op->read(&file,
                             (__u8 *) &list_count,
                             sizeof(list_count),
                             &file.f_pos);
    /* error? */
    if (tmperr < sizeof(list_count))
      {
        printk(KERN_WARNING
               "read_list(): list count read error from file %s!\n",
               list->name);
        err = -RSBAC_EREADFAILED;
        goto end_read;
      }

    /* alloc mem for old and converted item */
    old_buf = rsbac_kmalloc(list_info.desc_size + list_info.data_size);
    if(!old_buf)
      {
        printk(KERN_WARNING
               "read_list(): cannot allocate memory!\n");
        err = -RSBAC_ENOMEM;
        goto end_read;
      }
    new_buf = rsbac_kmalloc(list->info.desc_size + list->info.data_size);
    if(!new_buf)
      {
        printk(KERN_WARNING
               "read_list(): cannot allocate memory!\n");
        rsbac_kfree(old_buf);
        err = -RSBAC_ENOMEM;
        goto end_read;
      }
    /* calculate data pointers */
    if(list_info.data_size)
      old_data = old_buf + list_info.desc_size;
    else
      old_data = NULL;
    if(list->info.data_size)
      new_data = new_buf + list->info.desc_size;
    else
      new_data = NULL;

    /* actual reading */
    do
      {
        switch(list_version)
          {
            case RSBAC_LIST_DISK_VERSION:
                tmperr = file.f_op->read(&file,
                                         (char *) &max_age,
                                         sizeof(max_age),
                                         &file.f_pos);
                break;
            case RSBAC_LIST_DISK_OLD_VERSION:
                break;
            default:
                printk(KERN_WARNING
                       "read_lol_list(): wrong on-disk list version %u in file %s, expected %u - error!\n",
                       list_version,
                       list->name,
                       RSBAC_LIST_DISK_VERSION);
                err = -RSBAC_EREADFAILED;
                goto end_read;
          }
        if(conv)
          {
            tmperr = file.f_op->read(&file,
                                     old_buf,
                                     list_info.desc_size + list_info.data_size,
                                     &file.f_pos);
            if(tmperr > 0)
              { /* convert */
                converr = conv(old_buf, old_data,
                               new_buf, new_data);
                if(converr)
                  tmperr = converr;
              }
          }
        else
          {
            tmperr = file.f_op->read(&file,
                                     new_buf,
                                     list->info.desc_size + list->info.data_size,
                                     &file.f_pos);
          }
        /* if successful, add item */
        if (tmperr > 0)
          {
            /* wait for write access to list */
            rsbac_write_lock(&list->lock, &flags);
            add_item(list, max_age, new_buf, new_data);
            /* allow access */
            rsbac_write_unlock(&list->lock, &flags);
            read_count++;
/*
#ifdef CONFIG_RSBAC_DEBUG
                if (rsbac_debug_lists) printk(KERN_DEBUG "read_list(): read item %i\n",
                     user_aci.id);
#endif
*/
          }
      }
    while (tmperr > 0); /* end of do */

    if (tmperr < 0)
      {
        printk(KERN_WARNING "read_list(): read error from file %s!\n",
               list->name);
        err = -RSBAC_EREADFAILED;
      }
    rsbac_kfree(old_buf);
    rsbac_kfree(new_buf);

    if (read_count != list_count)
      {
        printk(KERN_WARNING "read_list(): read %lu, expected %u items from file %s!\n",
               read_count,
               list_count,
               list->name);
        err = -RSBAC_EREADFAILED;
      }

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

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


static int read_lol_list(struct rsbac_list_lol_reg_item_t * list)
  {
    struct file                     file;
    int                             err = 0;
    int                             tmperr;
    int                             converr;
    rsbac_version_t                 list_version;
    u_long                          read_count = 0;
    u_long                          flags;
    u_long                          sublen;
    u_long                          i;
    char                          * old_buf;
    char                          * new_buf;
    char                          * old_data;
    char                          * new_data;
    char                          * old_subbuf;
    char                          * new_subbuf;
    char                          * old_subdata;
    char                          * new_subdata;
    struct rsbac_list_lol_info_t    list_info;
    rsbac_list_count_t              list_count;
    rsbac_time_t                    timestamp;
    rsbac_time_t                    max_age = 0;
    rsbac_list_conv_function_t    * conv = NULL;
    rsbac_list_conv_function_t    * subconv = NULL;
    boolean                         timeout = FALSE;
    struct rsbac_list_lol_item_t  * item_p;
    mm_segment_t                    oldfs;

    /* open file */
    if ((err = rsbac_read_open(list->name,
                               &file,
                               list->device) ))
      return(err);

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

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

    /* check gen-list on-disk version */
    tmperr = file.f_op->read(&file,
                             (__u8 *) &list_version,
                             sizeof(list_version),
                             &file.f_pos);
    /* error? */
    if (tmperr < sizeof(list_version))
      {
        printk(KERN_WARNING
               "read_lol_list(): read error from file!\n");
        err = -RSBAC_EREADFAILED;
        goto end_read;
      }
    /* if wrong list on-disk version, fail */
    switch(list_version)
      {
        case RSBAC_LIST_DISK_VERSION:
            break;
        case RSBAC_LIST_DISK_OLD_VERSION:
            break;
        default:
            printk(KERN_WARNING
                   "read_lol_list(): wrong on-disk list version %u in file %s, expected %u - error!\n",
                   list_version,
                   list->name,
                   RSBAC_LIST_DISK_VERSION);
            err = -RSBAC_EREADFAILED;
            goto end_read;
      }

    /* get timestamp */
    tmperr = file.f_op->read(&file,
                             (__u8 *) &timestamp,
                             sizeof(timestamp),
                             &file.f_pos);
    /* error? */
    if (tmperr < sizeof(timestamp))
      {
        printk(KERN_WARNING
               "read_lol_list(): timestamp read error from file %s!\n",
               list->name);
        err = -RSBAC_EREADFAILED;
        goto end_read;
      }

    /* get list info */
    tmperr = file.f_op->read(&file,
                             (__u8 *) &list_info,
                             sizeof(list_info),
                             &file.f_pos);
    /* error? */
    if (tmperr < sizeof(list_info))
      {
        printk(KERN_WARNING
               "read_lol_list(): list info read error from file %s!\n",
               list->name);
        err = -RSBAC_EREADFAILED;
        goto end_read;
      }

    /* list timed out? System time is measured in seconds. */
    if(   list_info.max_age
       && (timestamp + list_info.max_age) <= CURRENT_TIME)
      timeout = TRUE;

    /* Valid key? */
    if (list_info.key != list->info.key)
      {
        if(timeout)
          {
            printk(KERN_WARNING
                   "read_lol_list(): accessing timed out list %s with wrong key, ignoring old contents!\n",
                   list->name);
            goto end_read;
          }
        else
          {
            printk(KERN_WARNING
                   "read_lol_list(): try to access list %s with wrong key!\n",
                   list->name);
            err = -EPERM;
            goto end_read;
          }
      }

    /* skip the rest, it ignore is requested */
    if(list->flags & RSBAC_LIST_IGNORE_OLD)
      goto end_read;

    /* if wrong list version, try to get_conv */
    if(list_info.version != list->info.version)
      {
        if(list->get_conv)
          conv = list->get_conv(list_info.version);
        if(list->get_subconv)
          subconv = list->get_subconv(list_info.version);
        if(!conv || !subconv)
          {
            if(timeout)
              {
                printk(KERN_WARNING
                       "read_lol_list(): accessing timed out list %s without both conversion functions, ignoring old contents!\n",
                       list->name);
                goto end_read;
              }
            else
              {
                /* complain and set error, if ignore is not requested */
                if(!(list->flags & RSBAC_LIST_IGNORE_UNSUPP_VERSION))
                  {
                    printk(KERN_WARNING
                           "read_lol_list(): cannot convert list version %u of file %s to version %u!\n",
                           list_info.version,
                           list->name,
                           list->info.version);
                    err = -RSBAC_EINVALIDVERSION;
                  }
                goto end_read;
              }
          }
        else
          {
            printk(KERN_WARNING
                   "read_lol_list(): converting list version %u of file %s on device %02u:%02u to version %u!\n",
                   list_info.version,
                   list->name,
                   MAJOR(list->device), MINOR(list->device),
                   list->info.version);
          }
      }
    else /* same version needs same sizes */
      {
        if(   (list_info.desc_size != list->info.desc_size)
           || (list_info.data_size != list->info.data_size)
           || (list_info.subdesc_size != list->info.subdesc_size)
           || (list_info.subdata_size != list->info.subdata_size)
          )
          {
            if(timeout)
              {
                printk(KERN_WARNING
                       "read_lol_list(): accessing timed out list %s with wrong desc or data size(s), ignoring old contents!\n",
                       list->name);
                goto end_read;
              }
            else
              {
                printk(KERN_WARNING
                       "read_lol_list(): desc or data size mismatch on list %s!\n",
                       list->name);
                err = -RSBAC_EINVALIDVALUE;
                goto end_read;
              }
          }
      }

    /* get list count */
    tmperr = file.f_op->read(&file,
                             (__u8 *) &list_count,
                             sizeof(list_count),
                             &file.f_pos);
    /* error? */
    if (tmperr < sizeof(list_count))
      {
        printk(KERN_WARNING
               "read_lol_list(): list count read error from file %s!\n",
               list->name);
        err = -RSBAC_EREADFAILED;
        goto end_read;
      }

    /* alloc mem for old and converted items */
    old_buf = rsbac_kmalloc(list_info.desc_size + list_info.data_size);
    if(!old_buf)
      {
        printk(KERN_WARNING
               "read_lol_list(): cannot allocate memory!\n");
        err = -RSBAC_ENOMEM;
        goto end_read;
      }
    new_buf = rsbac_kmalloc(list->info.desc_size + list->info.data_size);
    if(!new_buf)
      {
        printk(KERN_WARNING
               "read_lol_list(): cannot allocate memory!\n");
        rsbac_kfree(old_buf);
        err = -RSBAC_ENOMEM;
        goto end_read;
      }
    old_subbuf = rsbac_kmalloc(list_info.subdesc_size + list_info.subdata_size);
    if(!old_subbuf)
      {
        printk(KERN_WARNING
               "read_lol_list(): cannot allocate memory!\n");
        rsbac_kfree(old_buf);
        rsbac_kfree(new_buf);
        err = -RSBAC_ENOMEM;
        goto end_read;
      }
    new_subbuf = rsbac_kmalloc(list->info.subdesc_size + list->info.subdata_size);
    if(!new_subbuf)
      {
        printk(KERN_WARNING
               "read_lol_list(): cannot allocate memory!\n");
        rsbac_kfree(old_buf);
        rsbac_kfree(new_buf);
        rsbac_kfree(old_subbuf);
        err = -RSBAC_ENOMEM;
        goto end_read;
      }
    /* calculate data pointers */
    if(list_info.data_size)
      old_data = old_buf + list_info.desc_size;
    else
      old_data = NULL;
    if(list->info.data_size)
      new_data = new_buf + list->info.desc_size;
    else
      new_data = NULL;
    if(list_info.subdata_size)
      old_subdata = old_subbuf + list_info.subdesc_size;
    else
      old_subdata = NULL;
    if(list->info.subdata_size)
      new_subdata = new_subbuf + list->info.subdesc_size;
    else
      new_subdata = NULL;

    /* actual reading */
    do
      {
        switch(list_version)
          {
            case RSBAC_LIST_DISK_VERSION:
                tmperr = file.f_op->read(&file,
                                         (char *) &max_age,
                                         sizeof(max_age),
                                         &file.f_pos);
                break;
            case RSBAC_LIST_DISK_OLD_VERSION:
                break;
            default:
                printk(KERN_WARNING
                       "read_lol_list(): wrong on-disk list version %u in file %s, expected %u - error!\n",
                       list_version,
                       list->name,
                       RSBAC_LIST_DISK_VERSION);
                err = -RSBAC_EREADFAILED;
                goto end_read;
          }
        if(conv)
          {
            tmperr = file.f_op->read(&file,
                                     old_buf,
                                     list_info.desc_size + list_info.data_size,
                                     &file.f_pos);
            if(tmperr > 0)
              { /* convert */
                converr = conv(old_buf, old_data,
                               new_buf, new_data);
                if(converr)
                  tmperr = converr;
              }
          }
        else
          {
            tmperr = file.f_op->read(&file,
                                     new_buf,
                                     list->info.desc_size + list->info.data_size,
                                     &file.f_pos);
          }
        /* if successful, add item */
        if (tmperr > 0)
          {
            /* wait for write access to list */
            rsbac_write_lock(&list->lock, &flags);
            item_p = add_lol_item(list, max_age, new_buf, new_data);
            /* allow access */
            rsbac_write_unlock(&list->lock, &flags);
            if(!item_p)
              {
                err = -RSBAC_ENOMEM;
                goto end_read_free;
              }
            read_count++;
/*
#ifdef CONFIG_RSBAC_DEBUG
                if (rsbac_debug_lists) printk(KERN_DEBUG "read_lol_list(): read item %i\n",
                     user_aci.id);
#endif
*/
            tmperr = file.f_op->read(&file,
                                     (__u8 *) &sublen,
                                     sizeof(sublen),
                                     &file.f_pos);
            /* if successful, read and add sublen subitems */
            if (tmperr > 0)
              {
                for(i=0;i<sublen;i++)
                  {
                    switch(list_version)
                      {
                        case RSBAC_LIST_DISK_VERSION:
                            tmperr = file.f_op->read(&file,
                                                     (char *) &max_age,
                                                     sizeof(max_age),
                                                     &file.f_pos);
                            break;
                        case RSBAC_LIST_DISK_OLD_VERSION:
                            break;
                        default:
                            printk(KERN_WARNING
                                   "read_lol_list(): wrong on-disk list version %u in file %s, expected %u - error!\n",
                                   list_version,
                                   list->name,
                                   RSBAC_LIST_DISK_VERSION);
                                   err = -RSBAC_EREADFAILED;
                                   goto end_read;
                      }
                    if(subconv)
                      {
                        tmperr = file.f_op->read(&file,
                                                 old_subbuf,
                                                 list_info.subdesc_size + list_info.subdata_size,
                                                 &file.f_pos);
                        if(tmperr > 0)
                          { /* convert */
                            converr = subconv(old_subbuf, old_subdata,
                                              new_subbuf, new_subdata);
                            if(converr)
                              tmperr = converr;
                          }
                      }
                    else
                      {
                        tmperr = file.f_op->read(&file,
                                                 new_subbuf,
                                                 list->info.subdesc_size + list->info.subdata_size,
                                                 &file.f_pos);
                      }
                    if(tmperr > 0)
                      {
                        /* wait for write access to list */
                        rsbac_write_lock(&list->lock, &flags);
                        if (!add_lol_subitem(list,
                                             item_p,
                                             max_age,
                                             new_subbuf,
                                             new_subdata))
                          {
                            printk(KERN_WARNING
                                   "read_lol_list(): could not add subitem!\n");
                            i = sublen;
                            tmperr = -1;
                          }
                        /* allow access */
                        rsbac_write_unlock(&list->lock, &flags);
                      }
                    else
                      {
                        i = sublen;
                        tmperr = -1;
                      }
                  }
              }
          }
      }
    while (tmperr > 0); /* end of do */

    if (tmperr < 0)
      {
        printk(KERN_WARNING "read_lol_list(): read error from file %s!\n",
               list->name);
        err = -RSBAC_EREADFAILED;
      }

    if (read_count != list_count)
      {
        printk(KERN_WARNING "read_lol_list(): read %lu, expected %u items from file %s!\n",
               read_count,
               list_count,
               list->name);
        err = -RSBAC_EREADFAILED;
      }

end_read_free:
    rsbac_kfree(old_buf);
    rsbac_kfree(new_buf);
    rsbac_kfree(old_subbuf);
    rsbac_kfree(new_subbuf);

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

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


#ifndef CONFIG_RSBAC_NO_WRITE
/* returns 0, if list written, and negative error on error */
static int write_list(struct rsbac_list_reg_item_t * list)
  {
    struct file                file;
           int                 err = 0;
    struct rsbac_list_item_t * current_p;
           mm_segment_t        oldfs;
           u_long              write_count, written = 0, bytes;
           int                 tmperr = 0;
           u_long              flags;
           char              * buffer = NULL;
           u_long              buflen;
           boolean             vmalloc_used = FALSE;
           rsbac_version_t     list_version = RSBAC_LIST_DISK_VERSION;
           rsbac_time_t        timestamp = CURRENT_TIME;

    /* protect this list */
    rsbac_read_lock(&list->lock, &flags);
    /* allocate mem */
    buflen =   sizeof(list_version)
             + sizeof(timestamp)
             + sizeof(list->info)
             + sizeof(list->count)
             + list->count * (sizeof(current_p->max_age) + list->info.desc_size + list->info.data_size);
    /* try to rsbac_vkmalloc */
    buffer = (char *) rsbac_vkmalloc(buflen, &vmalloc_used);
    if(!buffer)
      {
        /* unprotect this list */
        rsbac_read_unlock(&list->lock, &flags);
        return(-RSBAC_ENOMEM);
      }
    /* copy version */
    memcpy(buffer,
           (char *) &list_version,
           sizeof(list_version));
    write_count = sizeof(list_version);
    /* copy version */
    memcpy(buffer+write_count,
           (char *) &timestamp,
           sizeof(timestamp));
    write_count += sizeof(timestamp);
    /* copy info */
    memcpy(buffer+write_count,
           (char *) &list->info,
           sizeof(list->info));
    write_count += sizeof(list->info);
    /* copy count */
    memcpy(buffer+write_count,
           (char *) &list->count,
           sizeof(list->count));
    write_count += sizeof(list->count);
    /* copy list */
    current_p = list->head;
    while (current_p) 
      {
        memcpy(buffer+write_count,
               &current_p->max_age,
	       sizeof(current_p->max_age));
	write_count += sizeof(current_p->max_age);
        memcpy(buffer+write_count,
               ((char *) current_p) + sizeof(*current_p),
	       list->info.desc_size + list->info.data_size);
        write_count += list->info.desc_size + list->info.data_size;
        current_p = current_p->next;
      }
    /* unprotect this list */
    rsbac_read_unlock(&list->lock, &flags);

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

    /* open file */
    if ((err = rsbac_write_open(list->name,
                                &file,
                                list->device) ))
      {
        up(&rsbac_write_sem);
        /* free buffer */
        rsbac_vkfree(buffer, vmalloc_used);
        return(err);
      }

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

    while ((written < write_count) && (tmperr >= 0)) 
      {
        bytes = rsbac_min(write_count - written, RSBAC_MAX_WRITE_CHUNK);
        tmperr = file.f_op->write(&file,
                                  buffer+written,
	                          bytes,
	                          &file.f_pos);
        if(tmperr > 0)
          {
            written += tmperr;
          }
      }
    if (tmperr < 0)
      {
        printk(KERN_WARNING
               "write_list(): write error %i on file %s!\n",
               tmperr,
               list->name);
        err |= -RSBAC_EWRITEFAILED;
      }

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

#ifdef CONFIG_RSBAC_DEBUG
    if (rsbac_debug_write)
      printk(KERN_DEBUG "write_list(): %u entries / %lu bytes written.\n",
             list->count, buflen);
#endif
    /* End of write access */
    rsbac_write_close(&file);
    /* free overall sem */
    up(&rsbac_write_sem);
    /* free buffer */
    rsbac_vkfree(buffer, vmalloc_used);
    /* update file timestamp list - but not for filelist itself to avoid looping */
    if(   !err
       && devicelist
      )
      {
        rsbac_list_handle_t filelist;

        if(   !rsbac_list_get_data(devicelist, &list->device, &filelist)
           && (list != filelist)
          )
          {
            struct rsbac_list_filelist_data_t data;

            data.timestamp = timestamp;
            data.max_age = list->info.max_age;
            rsbac_list_add(filelist, list->name, &data);
          }
      }
    /* Ready. */
    return(err);
  }; /* end of write_list() */

/* returns 0, if list written, and negative error on error */
static int write_lol_list(struct rsbac_list_lol_reg_item_t * list)
  {
    struct file                    file;
           int                     err = 0;
    struct rsbac_list_lol_item_t * current_p;
    struct rsbac_list_item_t     * sub_p;
           mm_segment_t            oldfs;
           u_long                  write_count, written = 0, bytes;
           int                     tmperr = 0;
           u_long                  flags;
           char                  * buffer = NULL;
           u_long                  buflen;
           boolean                 vmalloc_used = FALSE;
           rsbac_version_t         list_version = RSBAC_LIST_DISK_VERSION;
           rsbac_time_t            timestamp = CURRENT_TIME;

    /* protect this list */
    rsbac_read_lock(&list->lock, &flags);
    /* allocate mem */
    buflen =   sizeof(list_version)
             + sizeof(timestamp)
             + sizeof(list->info)
             + sizeof(list->count)
             + list->count * (sizeof(current_p->max_age) + list->info.desc_size + list->info.data_size);
    current_p = list->head;
    while(current_p)
      {
        buflen += sizeof(current_p->count);
        buflen += current_p->count * (sizeof(current_p->max_age) + list->info.subdesc_size + list->info.subdata_size);
        current_p = current_p->next;
      }
    /* try to rsbac_vkmalloc */
    buffer = (char *) rsbac_vkmalloc(buflen, &vmalloc_used);
    if(!buffer)
      {
        /* unprotect this list */
        rsbac_read_unlock(&list->lock, &flags);
        return(-RSBAC_ENOMEM);
      }
    /* copy version */
    memcpy(buffer,
           (char *) &list_version,
           sizeof(list_version));
    write_count = sizeof(list_version);
    /* copy version */
    memcpy(buffer+write_count,
           (char *) &timestamp,
           sizeof(timestamp));
    write_count += sizeof(timestamp);
    /* copy info */
    memcpy(buffer+write_count,
           (char *) &list->info,
           sizeof(list->info));
    write_count += sizeof(list->info);
    /* copy count */
    memcpy(buffer+write_count,
           (char *) &list->count,
           sizeof(list->count));
    write_count += sizeof(list->count);
    /* copy list */
    current_p = list->head;
    while (current_p) 
      {
        memcpy(buffer+write_count,
               &current_p->max_age,
	       sizeof(current_p->max_age));
	write_count += sizeof(current_p->max_age);
        memcpy(buffer+write_count,
               ((char *) current_p) + sizeof(*current_p),
	       list->info.desc_size + list->info.data_size);
        write_count += list->info.desc_size + list->info.data_size;
        memcpy(buffer+write_count,
               &current_p->count,
	       sizeof(current_p->count));
        write_count += sizeof(current_p->count);
        /* copy subitems */
        sub_p = current_p->head;
        while (sub_p) 
          {
            memcpy(buffer+write_count,
                   &sub_p->max_age,
	           sizeof(sub_p->max_age));
            write_count += sizeof(sub_p->max_age);
            memcpy(buffer+write_count,
                   ((char *) sub_p) + sizeof(*sub_p),
	           list->info.subdesc_size + list->info.subdata_size);
            write_count += list->info.subdesc_size + list->info.subdata_size;
            sub_p = sub_p->next;
          }
        current_p = current_p->next;
      }
    /* unprotect this list */
    rsbac_read_unlock(&list->lock, &flags);

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

    /* open file */
    if ((err = rsbac_write_open(list->name,
                                &file,
                                list->device) ))
      {
        up(&rsbac_write_sem);
        /* free buffer */
        rsbac_vkfree(buffer, vmalloc_used);
        return(err);
      }

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

    while ((written < write_count) && (tmperr >= 0)) 
      {
        bytes = rsbac_min(write_count - written, RSBAC_MAX_WRITE_CHUNK);
        tmperr = file.f_op->write(&file,
                                  buffer+written,
	                          bytes,
	                          &file.f_pos);
        if(tmperr > 0)
          {
            written += tmperr;
          }
      }
    if (tmperr < 0)
      {
        printk(KERN_WARNING
               "write_lol_list(): write error %i on file %s!\n",
               tmperr,
               list->name);
        err |= -RSBAC_EWRITEFAILED;
      }

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

#ifdef CONFIG_RSBAC_DEBUG
    if (rsbac_debug_write)
      printk(KERN_DEBUG "write_lol_list(): %u entries / %lu bytes written.\n",
             list->count, buflen);
#endif
    /* End of write access */
    rsbac_write_close(&file);
    /* free overall sem */
    up(&rsbac_write_sem);
    /* free buffer */
    rsbac_vkfree(buffer, vmalloc_used);
    /* update file timestamp list */
    if(   !err
       && devicelist
      )
      {
        rsbac_list_handle_t filelist;

        if(   !rsbac_list_get_data(devicelist, &list->device, &filelist)
           && (list != filelist)
          )
          {
            struct rsbac_list_filelist_data_t data;

            data.timestamp = timestamp;
            data.max_age = list->info.max_age;
            rsbac_list_add(filelist, list->name, &data);
          }
      }
    /* Ready. */
    return(err);
  }; /* end of write_list() */
#endif /* ifndef CONFIG_RSBAC_NO_WRITE */


static int unlink_list(struct rsbac_list_reg_item_t * list)
  {
/*
    rsbac_list_handle_t filelist;
*/
    /* Delete file */
    /* Remove filelist item */
/*
    if(!rsbac_list_get_data(devicelist, &list->device, &filelist))
      rsbac_list_remove(filelist, list->name);
*/
    /* Return */
    return 0;
  }

static int unlink_lol_list(struct rsbac_list_lol_reg_item_t * list)
  {
/*
    rsbac_list_handle_t filelist;
*/
    /* Delete file */
    /* Remove filelist item */
    /* rsbac_list_remove(filelist, list->name); */
/*
    if(!rsbac_list_get_data(devicelist, &list->device, &filelist))
      rsbac_list_remove(filelist, list->name);
*/
    /* Return */
    return 0;
  }

/************************************************* */
/*           PROC support                          */
/************************************************* */

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

  union rsbac_target_id_t            rsbac_target_id;
  union rsbac_attribute_value_t      rsbac_attribute_value;
  struct rsbac_list_reg_item_t     * item_p;
  struct rsbac_list_lol_reg_item_t * lol_item_p;
         u_long                      flags;

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

#ifdef CONFIG_RSBAC_DEBUG
  if (rsbac_debug_aef) printk(KERN_DEBUG "lists_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, "Registered Generic Lists\n------------------------\nName\t\tdevice\tcount\tdesc\tdata\tpersist\tnowrite\tflags\tdirty\n");
  pos = begin + len;
  if (pos < offset)
    {
      len = 0;
      begin = pos;
    }
  if (pos > offset+length)
    goto out;

  rsbac_read_lock(&reg_head.lock, &flags);
  item_p=reg_head.head;
  while(item_p)
    {
      len += sprintf(buffer + len, "%-16s%02u:%02u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\n",
                     item_p->name,
                     MAJOR(item_p->device), MINOR(item_p->device),
                     item_p->count,
                     item_p->info.desc_size,
                     item_p->info.data_size,
                     item_p->flags & RSBAC_LIST_PERSIST,
                     item_p->no_write,
                     item_p->flags,
                     item_p->dirty & (item_p->flags & RSBAC_LIST_PERSIST));
      pos = begin + len;
      if (pos < offset)
        {
          len = 0;
          begin = pos;
        }
      if (pos > offset+length)
        {
          rsbac_read_unlock(&reg_head.lock, &flags);
          goto out;
        }
      item_p = item_p->next;
    }
  rsbac_read_unlock(&reg_head.lock, &flags);

  len += sprintf(buffer + len, "\n %u lists registered.\n\n",
                 reg_head.count);
  pos = begin + len;
  if (pos < offset)
    {
      len = 0;
      begin = pos;
    }
  if (pos > offset+length)
    goto out;

  len += sprintf(buffer + len, "Registered Generic Lists of Lists\n---------------------------------\nName\t\tdevice\tcount\tdesc\tdata\tpersist\tnowrite\tflags\tdirty\n");
  pos = begin + len;
  if (pos < offset)
    {
      len = 0;
      begin = pos;
    }
  if (pos > offset+length)
    goto out;

  rsbac_read_lock(&lol_reg_head.lock, &flags);
  lol_item_p=lol_reg_head.head;
  while(lol_item_p)
    {
      len += sprintf(buffer + len, "%-16s%02u:%02u\t%u\t%u+%u\t%u+%u\t%u\t%u\t%u\t%u\n",
                     lol_item_p->name,
                     MAJOR(lol_item_p->device), MINOR(lol_item_p->device),
                     lol_item_p->count,
                     lol_item_p->info.desc_size,
                     lol_item_p->info.subdesc_size,
                     lol_item_p->info.data_size,
                     lol_item_p->info.subdata_size,
                     lol_item_p->flags & RSBAC_LIST_PERSIST,
                     lol_item_p->no_write,
                     lol_item_p->flags,
                     lol_item_p->dirty & (lol_item_p->flags & RSBAC_LIST_PERSIST));
      pos = begin + len;
      if (pos < offset)
        {
          len = 0;
          begin = pos;
        }
      if (pos > offset+length)
        {
          rsbac_read_unlock(&lol_reg_head.lock, &flags);
          goto out;
        }
      lol_item_p = lol_item_p->next;
    }
  rsbac_read_unlock(&lol_reg_head.lock, &flags);

  len += sprintf(buffer + len, "\n %u lists of lists registered.\n",
                 lol_reg_head.count);
  pos = begin + len;
  if (pos < offset)
    {
      len = 0;
      begin = pos;
    }
  if (pos > offset+length)
    goto out;

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

/* Generic backup generation function */
static int backup_proc_read(char *page, char **start, off_t off,
  int count, int *eof, void *data)
  {
    int len = 0;
    off_t pos   = 0;
    off_t begin = 0;

    union  rsbac_target_id_t         rsbac_target_id;
    union  rsbac_attribute_value_t   rsbac_attribute_value;
    struct rsbac_list_reg_item_t   * list;
    struct rsbac_list_item_t       * current_p;
           rsbac_version_t           list_version = RSBAC_LIST_DISK_VERSION;
           rsbac_time_t              timestamp = CURRENT_TIME;
           u_long                    rflags, flags;

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

#ifdef CONFIG_RSBAC_DEBUG
    if (rsbac_debug_aef)
      printk(KERN_DEBUG "backup_proc_read(): 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;
      }

    rsbac_read_lock(&reg_head.lock, &rflags);
    list=lookup_reg(data);
    if(!list)
      {
        rsbac_read_unlock(&reg_head.lock, &rflags);
        return -ENOSYS;
      }
    /* lock list */
    rsbac_read_lock(&list->lock, &flags);
    /* copy version */
    memcpy(page,
           (char *) &list_version,
           sizeof(list_version));
    len = sizeof(list_version);
    /* copy version */
    memcpy(page+len,
           (char *) &timestamp,
           sizeof(timestamp));
    len += sizeof(timestamp);
    /* copy info */
    memcpy(page+len,
           (char *) &list->info,
           sizeof(list->info));
    len += sizeof(list->info);
    pos = begin + len;
    if (pos < off)
      {
        len = 0;
        begin = pos;
      }
    if (pos > off+count)
      {
        goto out;
      }

    /* copy list */
    current_p = list->head;
    while (current_p) 
      {
        memcpy(page+len,
               ((char *) current_p) + sizeof(*current_p),
	       list->info.desc_size + list->info.data_size);
        len += list->info.desc_size + list->info.data_size;
        pos = begin + len;
        if (pos < off)
          {
            len = 0;
            begin = pos;
          }
        if (pos > off+count)
          {
            goto out;
          }
        current_p = current_p->next;
      }

  out:
    /* unprotect this list */
    rsbac_read_unlock(&list->lock, &flags);
    rsbac_read_unlock(&reg_head.lock, &rflags);
    if(len <= off+count)
      *eof=1;
    *start = page + (off - begin);
    len -= (off - begin);
  
    if (len > count)
      len = count;
    return len;
  }

/* Generic lists of lists backup generation function */
static int lol_backup_proc_read(char *page, char **start, off_t off,
  int count, int *eof, void *data)
  {
    int len = 0;
    off_t pos   = 0;
    off_t begin = 0;

    union  rsbac_target_id_t           rsbac_target_id;
    union  rsbac_attribute_value_t     rsbac_attribute_value;
    struct rsbac_list_lol_reg_item_t * list;
    struct rsbac_list_lol_item_t     * current_p;
    struct rsbac_list_item_t         * sub_p;
           rsbac_version_t             list_version = RSBAC_LIST_DISK_VERSION;
           rsbac_time_t                timestamp = CURRENT_TIME;
           u_long                      rflags, flags;

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

#ifdef CONFIG_RSBAC_DEBUG
    if (rsbac_debug_aef)
      printk(KERN_DEBUG "lol_backup_proc_read(): 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;
      }

    rsbac_read_lock(&lol_reg_head.lock, &rflags);
    list=lookup_lol_reg(data);
    if(!list)
      {
        rsbac_read_unlock(&lol_reg_head.lock, &rflags);
        return -ENOSYS;
      }
    /* lock list */
    rsbac_read_lock(&list->lock, &flags);
    /* copy version */
    memcpy(page,
           (char *) &list_version,
           sizeof(list_version));
    len = sizeof(list_version);
    /* copy version */
    memcpy(page+len,
           (char *) &timestamp,
           sizeof(timestamp));
    len += sizeof(timestamp);
    /* copy info */
    memcpy(page+len,
           (char *) &list->info,
           sizeof(list->info));
    len += sizeof(list->info);
    pos = begin + len;
    if (pos < off)
      {
        len = 0;
        begin = pos;
      }
    if (pos > off+count)
      {
        goto out;
      }

    /* copy list */
    current_p = list->head;
    while (current_p) 
      {
        memcpy(page+len,
               ((char *) current_p) + sizeof(*current_p),
	       list->info.desc_size + list->info.data_size);
        len += list->info.desc_size + list->info.data_size;
        memcpy(page+len,
               &current_p->count,
	       sizeof(current_p->count));
        len += sizeof(current_p->count);
        pos = begin + len;
        if (pos < off)
          {
            len = 0;
            begin = pos;
          }
        if (pos > off+count)
          {
            goto out;
          }
        /* copy sublist */
        sub_p = current_p->head;
        while (sub_p)
          {
            memcpy(page+len,
                   ((char *) sub_p) + sizeof(*sub_p),
	           list->info.subdesc_size + list->info.subdata_size);
            len += list->info.subdesc_size + list->info.subdata_size;
            pos = begin + len;
            if (pos < off)
              {
                len = 0;
                begin = pos;
              }
            if (pos > off+count)
              {
                goto out;
              }
            sub_p = sub_p->next;
          }
        current_p = current_p->next;
      }

  out:
    /* unprotect this list */
    rsbac_read_unlock(&list->lock, &flags);
    rsbac_read_unlock(&lol_reg_head.lock, &rflags);
    if(len <= off+count)
      *eof=1;
    *start = page + (off - begin);
    len -= (off - begin);
  
    if (len > count)
      len = count;
    return len;
  }
#endif /* PROC */


/********************/
/* Init and general */
/********************/

int rsbac_list_compare_u32(void * desc1, void * desc2)
  {
    if( *((__u32*) desc1) < *((__u32*) desc2))
      return -1;
    return( *((__u32*) desc1) != *((__u32*) desc2));
  }

static int genlist_compare(void * desc1, void * desc2)
  {
    struct rsbac_list_filelist_desc_t * i_desc1 = desc1;
    struct rsbac_list_filelist_desc_t * i_desc2 = desc2;

    return strncmp(i_desc1->filename, i_desc2->filename, RSBAC_LIST_MAX_FILENAME);
  }

void __init rsbac_list_init(void)
  {
/* removed devicelist registration - it is not used anyway */
    struct rsbac_list_info_t fileinfo;
    int err=0;
    rsbac_list_handle_t filelist = NULL;

    reg_head.head  = NULL;
    reg_head.tail  = NULL;
    reg_head.curr  = NULL;
    reg_head.lock  = RW_LOCK_UNLOCKED;
    reg_head.count = 0;

    lol_reg_head.head  = NULL;
    lol_reg_head.tail  = NULL;
    lol_reg_head.curr  = NULL;
    lol_reg_head.lock  = RW_LOCK_UNLOCKED;
    lol_reg_head.count = 0;

    list_initialized = TRUE;

    /* register file device list */
    fileinfo.version = RSBAC_GEN_LIST_DISK_VERSION;
    fileinfo.key = RSBAC_GEN_LIST_KEY;
    fileinfo.desc_size = sizeof(kdev_t);
    fileinfo.data_size = sizeof(rsbac_list_handle_t);
    fileinfo.max_age = 0;
    err = rsbac_list_register(RSBAC_LIST_VERSION,
                              &devicelist,
                              fileinfo,
                              0,
                              NULL,
                              NULL,
                              NULL,
                              RSBAC_LIST_DEVICENAME,
                              0);
    if(err)
      {
        char * tmp = rsbac_kmalloc(RSBAC_MAXNAMELEN);

        if(tmp)
          {
            printk(KERN_DEBUG "rsbac_list_init: registering device list for filelists failed with error %s.\n",
                   get_error_name(tmp, err));
            rsbac_kfree(tmp);
          }
      }

    if(devicelist)
      {
        /* register file list */
        fileinfo.version = RSBAC_GEN_LIST_DISK_VERSION;
        fileinfo.key = RSBAC_GEN_LIST_KEY;
        fileinfo.desc_size = sizeof(struct rsbac_list_filelist_desc_t);
        fileinfo.data_size = sizeof(struct rsbac_list_filelist_data_t);
        fileinfo.max_age = 0;
        filelist = NULL;
        err = rsbac_list_register(RSBAC_LIST_VERSION,
                                  &filelist,
                                  fileinfo,
                                  RSBAC_LIST_PERSIST,
                                  genlist_compare,
                                  NULL,
                                  NULL,
                                  RSBAC_LIST_FILENAME,
                                  rsbac_root_dev);
        if(err)
          {
            char * tmp = rsbac_kmalloc(RSBAC_MAXNAMELEN);

            if(tmp)
              {
                printk(KERN_DEBUG "rsbac_list_init: registering filelist failed with error %s.\n",
                       get_error_name(tmp, err));
                rsbac_kfree(tmp);
              }
          }
        else
          {
            rsbac_list_add(devicelist, &rsbac_root_dev, &filelist);
          }
      }

    /* init proc entry */
    #if defined(CONFIG_RSBAC_PROC) && defined(CONFIG_PROC_FS)
    {
      struct proc_dir_entry * tmp_entry_p;

      tmp_entry_p = create_proc_entry(RSBAC_LIST_PROC_NAME,
                                      S_IFREG | S_IRUGO,
                                      proc_rsbac_root_p);
      if(tmp_entry_p)
        {
          tmp_entry_p->get_info = lists_proc_info;
        }
    }
    #endif
  }

int rsbac_list_mount(kdev_t kdev)
  {
    if(devicelist)
      {
        struct rsbac_list_info_t fileinfo;
        rsbac_list_handle_t filelist = NULL;
        int err;

        /* register file list */
        fileinfo.version = RSBAC_GEN_LIST_DISK_VERSION;
        fileinfo.key = RSBAC_GEN_LIST_KEY;
        fileinfo.desc_size = sizeof(struct rsbac_list_filelist_desc_t);
        fileinfo.data_size = sizeof(struct rsbac_list_filelist_data_t);
        fileinfo.max_age = 0;
        err = rsbac_list_register(RSBAC_LIST_VERSION,
                                  &filelist,
                                  fileinfo,
                                  RSBAC_LIST_PERSIST,
                                  genlist_compare,
                                  NULL,
                                  NULL,
                                  RSBAC_LIST_FILENAME,
                                  kdev);
        if(err)
          {
            char * tmp = rsbac_kmalloc(RSBAC_MAXNAMELEN);

            if(tmp)
              {
                printk(KERN_DEBUG "rsbac_list_init: registering filelist failed with error %s.\n",
                       get_error_name(tmp, err));
                rsbac_kfree(tmp);
              }
          }
        else
          {
            rsbac_list_add(devicelist, &kdev, &filelist);
          }
      }
    return 0;
  }

int rsbac_list_umount(kdev_t kdev)
  {
    if(devicelist)
      {
        rsbac_list_handle_t filelist = NULL;

        /* get file list handle, detach and unregister from device list */
        if(!rsbac_list_get_data(devicelist, &kdev, &filelist))
          {
            if(!rsbac_list_detach(&filelist, RSBAC_GEN_LIST_KEY))
              {
                rsbac_list_remove(devicelist, &kdev);
              }
          }
      }
    return 0;
  }

#if defined(CONFIG_RSBAC_AUTO_WRITE)
int rsbac_write_lists(boolean need_lock)
  {
    int count = 0;
    int subcount = 0;
    struct rsbac_list_reg_item_t     * item_p;
    struct rsbac_list_lol_reg_item_t * lol_item_p;
           u_long                      lock_flags;

/*
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_write_lists() called.\n");
#endif
*/
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;
    rsbac_read_lock(&reg_head.lock, &lock_flags);
    item_p=reg_head.head;
    while(item_p)
      {
        if(   (item_p->flags & RSBAC_LIST_PERSIST)
           && item_p->dirty
           && !item_p->no_write
          )
          {
            item_p->dirty = FALSE;
            if(need_lock)
              lock_kernel();
            /* list locking is done in write_list */
            subcount = write_list(item_p);
            if(need_lock)
              unlock_kernel();
            if(subcount)
              {
                if(subcount != -RSBAC_ENOTWRITABLE)
                  printk(KERN_WARNING
                         "rsbac_write_lists(): write_list() for list %s returned error %i\n",
                         item_p->name, subcount);
              }
            else
              count++;
          }
        item_p=item_p->next;
      }
    rsbac_read_unlock(&reg_head.lock, &lock_flags);

    rsbac_read_lock(&lol_reg_head.lock, &lock_flags);
    lol_item_p=lol_reg_head.head;
    while(lol_item_p)
      {
        if(   (lol_item_p->flags & RSBAC_LIST_PERSIST)
           && lol_item_p->dirty
           && !lol_item_p->no_write
          )
          {
            lol_item_p->dirty = FALSE;
            if(need_lock)
              lock_kernel();
            /* list locking is done in write_list */
            subcount = write_lol_list(lol_item_p);
            if(need_lock)
              unlock_kernel();
            if(subcount)
              {
                if(subcount != -RSBAC_ENOTWRITABLE)
                  printk(KERN_WARNING
                         "rsbac_write_lists(): write_list() for list of lists %s returned error %i\n",
                         item_p->name, subcount);
              }
            else
              count++;
          }
        lol_item_p=lol_item_p->next;
      }
    rsbac_read_unlock(&lol_reg_head.lock, &lock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if (rsbac_debug_write)
      printk(KERN_DEBUG "rsbac_write_lists(): %u lists written.\n",
             count);
#endif
    return count;
  }
#endif /* CONFIG_RSBAC_AUTO_WRITE */

/* Status checking */
int rsbac_check_lists(int correct)
  {
    struct rsbac_list_reg_item_t     * list;
    struct rsbac_list_lol_reg_item_t * lol_list;
    struct rsbac_list_item_t         * item_p;
    struct rsbac_list_item_t         * next_item_p;
    struct rsbac_list_lol_item_t     * lol_item_p;
    struct rsbac_list_lol_item_t     * next_lol_item_p;
    struct rsbac_list_item_t         * lol_subitem_p;
    struct rsbac_list_item_t         * next_lol_subitem_p;
           u_long                      lock_flags, rlock_flags;
           u_long                      tmp_count;
           u_long                      tmp_subcount;
           u_long                      subitem_count;
           u_long                      dirty = 0;

#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_check_lists() called.\n");
#endif
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;
    rsbac_read_lock(&reg_head.lock, &rlock_flags);
    list = reg_head.head;
    while(list)
      {
        /* check list */
        rsbac_write_lock(&list->lock, &lock_flags);
        tmp_count = 0;
        item_p = list->head;
        while(item_p)
          {
            if(   (   item_p->max_age
                   && (item_p->max_age <= CURRENT_TIME)
                  )
               || (   list->def_data
                   && !memcmp(((char *) item_p) + sizeof(*item_p) + list->info.desc_size,
                              list->def_data,
                              list->info.data_size)
                  )
              )
              {
                next_item_p = item_p->next;
                do_remove_item(list, item_p);
                item_p = next_item_p;
              }
            else
              {
                tmp_count++;
                item_p = item_p->next;
              }
          }
        if(tmp_count != list->count)
          {
            if(correct)
              {
                printk(KERN_WARNING
                       "rsbac_check_lists(): correcting count mismatch for list %s on device %02u:%02u - was %u, counted %lu!\n",
                       list->name, MAJOR(list->device), MINOR(list->device), list->count, tmp_count);
                list->count = tmp_count;
              }
            else
              {
                printk(KERN_WARNING
                       "rsbac_check_lists(): count mismatch for list %s on device %02u:%02u - is %u, counted %lu!\n",
                       list->name, MAJOR(list->device), MINOR(list->device), list->count, tmp_count);
              }
          }
        rsbac_write_unlock(&list->lock, &lock_flags);
        if(list->dirty && (list->flags & RSBAC_LIST_PERSIST))
          {
            dirty++;
#ifdef CONFIG_RSBAC_DEBUG
            if(rsbac_debug_lists)
              printk(KERN_DEBUG
                     "rsbac_check_lists(): %s on %02u:%02u has %u items (list is dirty)\n",
                     list->name, MAJOR(list->device), MINOR(list->device), list->count);
#endif
          }
#ifdef CONFIG_RSBAC_DEBUG
        else
          {
            if(rsbac_debug_lists)
              printk(KERN_INFO
                     "rsbac_check_lists(): %s on %02u:%02u has %u items\n",
                     list->name, MAJOR(list->device), MINOR(list->device), list->count);
          }
#endif
        list = list->next;
      }
    rsbac_read_unlock(&reg_head.lock, &rlock_flags);

    rsbac_read_lock(&lol_reg_head.lock, &rlock_flags);
    lol_list = lol_reg_head.head;
    while(lol_list)
      {
        /* check list */
        rsbac_write_lock(&lol_list->lock, &lock_flags);
        tmp_count = 0;
        subitem_count = 0;
        lol_item_p = lol_list->head;
        while(lol_item_p)
          {
            if(   (   lol_item_p->max_age
                   && (lol_item_p->max_age <= CURRENT_TIME)
                  )
               || (   lol_list->def_data
                   && !lol_item_p->count
                   && !memcmp(((char *) lol_item_p) + sizeof(*lol_item_p) + lol_list->info.desc_size,
                              lol_list->def_data,
                              lol_list->info.data_size)
                  )
               || (   !lol_list->info.data_size
                   && (lol_list->flags & RSBAC_LIST_DEF_DATA)
                   && !lol_item_p->count
                  )
              )
              {
                next_lol_item_p = lol_item_p->next;
                do_remove_lol_item(lol_list, lol_item_p);
                lol_item_p = next_lol_item_p;
              }
            else
              {
                tmp_count++;
                tmp_subcount = 0;
                lol_subitem_p = lol_item_p->head;
                while(lol_subitem_p)
                  {
                    if(   (   lol_subitem_p->max_age
                           && (lol_subitem_p->max_age <= CURRENT_TIME)
                          )
                       || (   lol_list->def_subdata
                           && !memcmp(((char *) lol_subitem_p) + sizeof(*lol_subitem_p) + lol_list->info.subdesc_size,
                                      lol_list->def_subdata,
                                      lol_list->info.subdata_size)
                          )
                      )
                      {
                        next_lol_subitem_p = lol_subitem_p->next;
                        do_remove_lol_subitem(lol_item_p, lol_subitem_p);
                        lol_subitem_p = next_lol_subitem_p;
                      }
                    else
                      {
                        tmp_subcount++;
                        lol_subitem_p = lol_subitem_p->next;
                      }
                  }
                if(tmp_subcount != lol_item_p->count)
                  {
                    if(correct)
                      {
                        printk(KERN_WARNING
                               "rsbac_check_lists(): correcting count mismatch for list of lists %s sublist on %02u:%02u - was %lu, counted %lu!\n",
                               lol_list->name, MAJOR(lol_list->device), MINOR(lol_list->device), lol_item_p->count, tmp_subcount);
                        lol_item_p->count = tmp_subcount;
                      }
                    else
                      {
                        printk(KERN_WARNING
                               "rsbac_check_lists(): count mismatch for list of lists %s sublist on %02u:%02u - is %lu, counted %lu!\n",
                               lol_list->name, MAJOR(lol_list->device), MINOR(lol_list->device), lol_item_p->count, tmp_subcount);
                      }
                  }
                subitem_count += lol_item_p->count;
                lol_item_p = lol_item_p->next;
              }
          }
        if(tmp_count != lol_list->count)
          {
            if(correct)
              {
                printk(KERN_WARNING
                       "rsbac_check_lists(): correcting count mismatch for list of lists %s on %02u:%02u - was %u, counted %lu!\n",
                       lol_list->name, MAJOR(lol_list->device), MINOR(lol_list->device), lol_list->count, tmp_count);
                list->count = tmp_count;
              }
            else
              {
                printk(KERN_WARNING
                       "rsbac_check_lists(): count mismatch for list of lists %s on %02u:%02u - is %u, counted %lu!\n",
                       lol_list->name, MAJOR(lol_list->device), MINOR(lol_list->device), lol_list->count, tmp_count);
              }
          }
        rsbac_write_unlock(&lol_list->lock, &lock_flags);
        if(lol_list->dirty && (lol_list->flags & RSBAC_LIST_PERSIST))
          {
            dirty++;
#ifdef CONFIG_RSBAC_DEBUG
            if(rsbac_debug_lists)
              printk(KERN_DEBUG
                     "rsbac_check_lists(): %s on %02u:%02u has %u items and %lu subitems (list is dirty)\n",
                     lol_list->name, MAJOR(lol_list->device), MINOR(lol_list->device), lol_list->count, subitem_count);
#endif
          }
#ifdef CONFIG_RSBAC_DEBUG
        else
          {
            if(rsbac_debug_lists)
              printk(KERN_DEBUG
                     "rsbac_check_lists(): %s on %02u:%02u has %u items and %lu subitems\n",
                     lol_list->name, MAJOR(lol_list->device), MINOR(lol_list->device), lol_list->count, subitem_count);
          }
#endif
        lol_list = lol_list->next;
      }
    rsbac_read_unlock(&lol_reg_head.lock, &rlock_flags);
    return 0;
  }


/********************/
/* Registration     */
/********************/

/* get generic list registration version */
rsbac_version_t rsbac_list_version(void)
  {
    return RSBAC_LIST_VERSION;
  }

/* register a new list */
/*
 * If list with same name exists in memory, error -RSBAC_EEXISTS is returned.
 * If list with same name and key exists on device, it is restored depending on the flags.
 * If list with same name, but different key exists, access is denied (error -EPERM).
 *
 * ds_version: for binary modules, must be RSBAC_LIST_VERSION. If version differs, return error.
 * handle_p: for all list accesses, an opaque handle is put into *handle_p.
 * key: positive, secret __u32 key, which must be the same as in on-disk version, if persistent
 * list_version: positive __u32 version number for the list. If old on-disk version is
   different, upconversion is tried (depending on flags and get_conv function)
 * flags: see flag values
 * desc_size: size of the descriptor (error is returned, if value is 0 or list exists and value differs)
 * data_size: size of data (error is returned, if list exists and value differs). Can be 0 for sets.
 * compare: for lookup and list optimization, can be NULL, then
   memcmp(desc1, desc2, desc_size) is used
 * def_data: default data value for flag RSBAC_LIST_DEF_DATA
   (if NULL, flag is cleared)
 * name: the on-disk name, must be distinct and max. 7 or 8.2 chars
   (only used for statistics, if non-persistent)
 * device: the device to read list from or to save list to - use 0 for root dev
   (ignored, if non-persistent)
 */

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_register);
#endif
int rsbac_list_register(
  rsbac_version_t ds_version,
  rsbac_list_handle_t *handle_p,
  struct rsbac_list_info_t info,
  u_int flags,
  rsbac_list_compare_function_t * compare,
  rsbac_list_get_conv_t * get_conv,
  void * def_data,
  char * name,
  kdev_t device)
  {
    struct rsbac_list_reg_item_t     * reg_item_p;
    struct rsbac_list_reg_item_t     * new_reg_item_p;
    u_long lock_flags;
    int    err = 0;

    if(ds_version != RSBAC_LIST_VERSION)
      {
        if(name)
          printk(KERN_WARNING
                 "rsbac_list_register: wrong ds_version %u for list %s, expected %u!\n",
                 ds_version,
                 name,
                 RSBAC_LIST_VERSION);
        return -RSBAC_EINVALIDVERSION;
      }
    if(!handle_p)
      return -RSBAC_EINVALIDPOINTER;
    *handle_p = NULL;
    if(!info.key || !info.version || !info.desc_size)
      return -RSBAC_EINVALIDVALUE;
    if(info.max_age > RSBAC_LIST_MAX_AGE_LIMIT)
      return -RSBAC_EINVALIDVALUE;
    if(info.desc_size + info.data_size > RSBAC_LIST_MAX_ITEM_SIZE)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;
    if(name)
      {
        struct rsbac_list_lol_reg_item_t * lol_reg_item_p;

        rsbac_read_lock(&reg_head.lock, &lock_flags);
        reg_item_p = lookup_reg_name(name, device);
        rsbac_read_unlock(&reg_head.lock, &lock_flags);
        if(reg_item_p)
          {
#ifdef CONFIG_RSBAC_DEBUG
            if(rsbac_debug_lists)
              printk(KERN_DEBUG "rsbac_list_register: list name %s already exists on device %02u:%02u!\n",
                     name, MAJOR(device), MINOR(device));
#endif
            return -RSBAC_EEXISTS;
          }
        rsbac_read_lock(&lol_reg_head.lock, &lock_flags);
        lol_reg_item_p = lookup_lol_reg_name(name, device);
        rsbac_read_unlock(&lol_reg_head.lock, &lock_flags);
        if(lol_reg_item_p)
          {
#ifdef CONFIG_RSBAC_DEBUG
            if(rsbac_debug_lists)
              printk(KERN_DEBUG "rsbac_list_register: list name %s already exists on device %02u:%02u!\n",
                     name, MAJOR(device), MINOR(device));
#endif
            return -RSBAC_EEXISTS;
          }
      }
    else
      if(flags & RSBAC_LIST_PERSIST)
        {
          printk(KERN_WARNING
                 "rsbac_list_register: try to register persistent list without name.\n");
          return -RSBAC_EINVALIDVALUE;
        }

    if(flags & RSBAC_LIST_PERSIST)
      {
        if(!device)
          device = rsbac_root_dev;
        if(!MAJOR(device))
          flags &= ~RSBAC_LIST_PERSIST;
      }
    else
      device = MKDEV(0,0);

#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_register: registering list %s for device %02u:%02u.\n",
             name, MAJOR(device),MINOR(device));
#endif
    new_reg_item_p = create_reg(info, flags, compare, get_conv, def_data, name, device);
    if(!new_reg_item_p)
      {
        return -RSBAC_ECOULDNOTADDITEM;
      }
    /* Restore from disk, but only for real device mounts */
    if(   (flags & RSBAC_LIST_PERSIST)
       && MAJOR(device)
      )
      {
#ifdef CONFIG_RSBAC_DEBUG
        if(rsbac_debug_lists)
          printk(KERN_DEBUG "rsbac_list_register: restoring list %s from device %02u:%02u.\n",
             name, MAJOR(device), MINOR(device));
#endif
        err = read_list(new_reg_item_p);
        /* not found is no error */
        if(err == -RSBAC_ENOTFOUND)
          err = 0;
        else
        if(err)
          {
#ifdef CONFIG_RSBAC_DEBUG
            if(rsbac_debug_lists)
              {
                char * tmp = rsbac_kmalloc(RSBAC_MAXNAMELEN);

                if(tmp)
                  {
                    printk(KERN_DEBUG "rsbac_list_register: restoring list %s from device %02u:%02u failed with error %s, unregistering list.\n",
                           name,
                           MAJOR(device),MINOR(device),
                           get_error_name(tmp, err));
                    rsbac_kfree(tmp);
                  }
              }
#endif
            clear_reg(new_reg_item_p);
            return err;
          }
        else
          {
#ifdef CONFIG_RSBAC_DEBUG
            if(rsbac_debug_lists)
               printk(KERN_DEBUG "rsbac_list_register: restoring list %s from device %02u:%02u was successful.\n",
                      name,
                      MAJOR(device),MINOR(device));
          }
#endif
      }

    rsbac_write_lock(&reg_head.lock, &lock_flags);
    reg_item_p = add_reg(new_reg_item_p);
    rsbac_write_unlock(&reg_head.lock, &lock_flags);
    if(!reg_item_p)
      {
        printk(KERN_WARNING
               "rsbac_list_register: inserting list %s failed!\n",
               name);
        /* cleanup */
        clear_reg(new_reg_item_p);
        return -RSBAC_ECOULDNOTADDITEM;
      }

    /* finish */
#if defined(CONFIG_RSBAC_PROC) && defined(CONFIG_PROC_FS)
    /* create proc entry, if requested */
    if(flags & RSBAC_LIST_BACKUP)
      {
        reg_item_p->proc_entry_p = create_proc_entry(reg_item_p->name,
                                                     S_IFREG | S_IRUGO,
                                                     proc_rsbac_backup_p);
        if(reg_item_p->proc_entry_p)
          {
            reg_item_p->proc_entry_p->read_proc = backup_proc_read;
            reg_item_p->proc_entry_p->data = reg_item_p;
          }
      }
    else
      {
        reg_item_p->proc_entry_p = NULL;
      }
#endif
    *handle_p = reg_item_p;
    return err;
  }

/* register a new list of lists */
/*
 * If list with same name exists in memory, error -RSBAC_EEXISTS is returned.
 * If list with same name and key exists on device, it is restored depending on the flags.
 * If list with same name, but different key exists, access is denied (error -EPERM).
 *
 * ds_version: for binary modules, must be RSBAC_LIST_VERSION. If version differs, return error.
 * handle_p: for all list accesses, an opaque handle is put into *handle_p.
 * key: positive, secret __u32 key, which must be the same as in on-disk version, if persistent
 * list_version: positive __u32 version number for the list. If old on-disk version is
   different, upconversion is tried (depending on flags and get_conv function)
 * flags: see flag values
 * desc_size: size of the descriptor (error is returned, if value is 0 or list exists and value differs)
 * subdesc_size: size of the sublist descriptor (error is returned, if value is 0 or list exists
   and value differs)
 * data_size: size of data (error is returned, if list exists and value differs). Can be 0 for sets.
 * subdata_size: size of sublist data (error is returned, if list exists and value differs).
   Can be 0 for sets.
 * compare: for lookup and list optimization, can be NULL, then
   memcmp(desc1, desc2, desc_size) is used
 * subcompare: for item lookup and optimization of sublist, can be NULL, then
   memcmp(desc1, desc2, desc_size) is used
 * def_data: default data value for flag RSBAC_LIST_DEF_DATA
   (if NULL, flag is cleared)
 * def_subdata: default subdata value for flag RSBAC_LIST_DEF_SUBDATA
   (if NULL, flag is cleared)
 * name: the on-disk name, must be distinct and max. 7 or 8.2 chars
   (only used for info, if non-persistent)
 * device: the device to read list from or to save list to - use 0 for root dev
   (ignored, if non-persistent)
 */

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_register);
#endif
int rsbac_list_lol_register(
  rsbac_version_t ds_version,
  rsbac_list_handle_t *handle_p,
  struct rsbac_list_lol_info_t info,
  u_int flags,
  rsbac_list_compare_function_t * compare,
  rsbac_list_compare_function_t * subcompare,
  rsbac_list_get_conv_t * get_conv,
  rsbac_list_get_conv_t * get_subconv,
  void * def_data,
  void * def_subdata,
  char * name,
  kdev_t device)
  {
    struct rsbac_list_lol_reg_item_t * reg_item_p;
    struct rsbac_list_lol_reg_item_t * new_reg_item_p;
    u_long lock_flags;
    int    err = 0;

    if(ds_version != RSBAC_LIST_VERSION)
      return -RSBAC_EINVALIDVERSION;
    if(!handle_p)
      return -RSBAC_EINVALIDPOINTER;
    *handle_p = NULL;
    if(!info.key || !info.version || !info.desc_size)
      return -RSBAC_EINVALIDVALUE;
    if(info.max_age > RSBAC_LIST_MAX_AGE_LIMIT)
      return -RSBAC_EINVALIDVALUE;
    if(info.desc_size + info.data_size > RSBAC_LIST_MAX_ITEM_SIZE)
      return -RSBAC_EINVALIDVALUE;
    if(info.subdesc_size + info.subdata_size > RSBAC_LIST_MAX_ITEM_SIZE)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;
    if(name)
      {
        struct rsbac_list_reg_item_t     * std_reg_item_p;

        rsbac_read_lock(&lol_reg_head.lock, &lock_flags);
        reg_item_p = lookup_lol_reg_name(name, device);
        rsbac_read_unlock(&lol_reg_head.lock, &lock_flags);
        if(reg_item_p)
          {
#ifdef CONFIG_RSBAC_DEBUG
            if(rsbac_debug_lists)
              printk(KERN_DEBUG "rsbac_list_lol_register: list name %s already exists on device %02u:%02u!\n",
                     name, MAJOR(device), MINOR(device));
#endif
            return -RSBAC_EEXISTS;
          }
        rsbac_read_lock(&reg_head.lock, &lock_flags);
        std_reg_item_p = lookup_reg_name(name, device);
        rsbac_read_unlock(&reg_head.lock, &lock_flags);
        if(std_reg_item_p)
          {
#ifdef CONFIG_RSBAC_DEBUG
            if(rsbac_debug_lists)
              printk(KERN_DEBUG "rsbac_list_register: list name %s already exists on device %02u:%02u!\n",
                     name, MAJOR(device), MINOR(device));
#endif
            return -RSBAC_EEXISTS;
          }
      }
    else
      if(flags & RSBAC_LIST_PERSIST)
        {
          printk(KERN_WARNING
                 "rsbac_list_lol_register: try to register persistent list of lists without name.\n");
          return -RSBAC_EINVALIDVALUE;
        }

    if(flags & RSBAC_LIST_PERSIST)
      {
        if(!device)
          device = rsbac_root_dev;
        if(!MAJOR(device))
          flags &= ~RSBAC_LIST_PERSIST;
      }
    else
      device = MKDEV(0,0);

#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_lol_register: registering list of lists %s.\n",
             name);
#endif
    new_reg_item_p = create_lol_reg(info, flags, compare, subcompare,
                                    get_conv, get_subconv,
                                    def_data, def_subdata,
                                    name, device);
    if(!new_reg_item_p)
      {
        return -RSBAC_ECOULDNOTADDITEM;
      }
    /* Restore from disk */
    if(flags & RSBAC_LIST_PERSIST)
      {
#ifdef CONFIG_RSBAC_DEBUG
        if(rsbac_debug_lists)
          printk(KERN_DEBUG "rsbac_list_lol_register: restoring list %s from device %02u:%02u.\n",
             name, MAJOR(device), MINOR(device));
#endif
        err = read_lol_list(new_reg_item_p);
        /* not found is no error */
        if(err == -RSBAC_ENOTFOUND)
          err = 0;
        else
        if(err)
          {
#ifdef CONFIG_RSBAC_DEBUG
            if(rsbac_debug_lists)
              {
                char * tmp = rsbac_kmalloc(RSBAC_MAXNAMELEN);

                if(tmp)
                  {
                    printk(KERN_DEBUG "rsbac_list_lol_register: restoring list %s from device %02u:%02u failed with error %s, unregistering list.\n",
                           name,
                           MAJOR(device),MINOR(device),
                           get_error_name(tmp, err));
                    rsbac_kfree(tmp);
                  }
              }
#endif
            clear_lol_reg(new_reg_item_p);
            return err;
          }
        else
          {
#ifdef CONFIG_RSBAC_DEBUG
            if(rsbac_debug_lists)
               printk(KERN_DEBUG "rsbac_list_lol_register: restoring list %s from device %02u:%02u was successful.\n",
                      name,
                      MAJOR(device),MINOR(device));
          }
#endif
      }

    rsbac_write_lock(&lol_reg_head.lock, &lock_flags);
    reg_item_p = add_lol_reg(new_reg_item_p);
    rsbac_write_unlock(&lol_reg_head.lock, &lock_flags);
    if(!reg_item_p)
      {
        printk(KERN_WARNING
               "rsbac_list_lol_register: inserting list %s failed!\n",
               name);
        /* cleanup */
        clear_lol_reg(new_reg_item_p);
        return -RSBAC_ECOULDNOTADDITEM;
      }

    /* finish */
#if defined(CONFIG_RSBAC_PROC) && defined(CONFIG_PROC_FS)
    /* create proc entry, if requested */
    if(flags & RSBAC_LIST_BACKUP)
      {
        reg_item_p->proc_entry_p = create_proc_entry(reg_item_p->name,
                                                     S_IFREG | S_IRUGO,
                                                     proc_rsbac_backup_p);
        if(reg_item_p->proc_entry_p)
          {
            reg_item_p->proc_entry_p->read_proc = lol_backup_proc_read;
            reg_item_p->proc_entry_p->data = reg_item_p;
          }
      }
    else
      {
        reg_item_p->proc_entry_p = NULL;
      }
#endif
    *handle_p = reg_item_p;
    return err;
  }

/* destroy list */
/* list is destroyed, disk file is deleted */
/* list must have been opened with register */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_destroy);
#endif
int rsbac_list_destroy(rsbac_list_handle_t * handle_p, rsbac_list_key_t key)
  {
    struct rsbac_list_reg_item_t * reg_item_p;
    u_long lock_flags;
    int err = 0;

    if(!handle_p)
      return -RSBAC_EINVALIDPOINTER;
    if(!*handle_p)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    rsbac_write_lock(&reg_head.lock, &lock_flags);
    reg_item_p = lookup_reg((struct rsbac_list_reg_item_t *) *handle_p);
    if(!reg_item_p)
      {
        rsbac_write_unlock(&reg_head.lock, &lock_flags);
        printk(KERN_WARNING "rsbac_list_destroy: destroying list failed due to invalid handle!\n");
        return -RSBAC_EINVALIDVALUE;
      }
    if(reg_item_p->info.key != key)
      {
        rsbac_write_unlock(&reg_head.lock, &lock_flags);
        printk(KERN_WARNING "rsbac_list_destroy: destroying list %s denied due to invalid key!\n",
               reg_item_p->name);
        return -EPERM;
      }
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_destroy: destroying list %s.\n",
             reg_item_p->name);
#endif
#if defined(CONFIG_RSBAC_PROC) && defined(CONFIG_PROC_FS)
    /* delete proc entry, if it exists */
    if(   (reg_item_p->flags & RSBAC_LIST_BACKUP)
       && reg_item_p->proc_entry_p
      )
      {
        remove_proc_entry(reg_item_p->name, proc_rsbac_backup_p);
        reg_item_p->proc_entry_p = NULL;
      }
#endif
    if(reg_item_p->flags & RSBAC_LIST_PERSIST)
      err = unlink_list(reg_item_p);

    remove_reg(reg_item_p);
    *handle_p = NULL; 
    rsbac_write_unlock(&reg_head.lock, &lock_flags);
    return err;
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_destroy);
#endif
int rsbac_list_lol_destroy(rsbac_list_handle_t * handle_p, rsbac_list_key_t key)
  {
    struct rsbac_list_lol_reg_item_t * reg_item_p;
    u_long lock_flags;
    int err = 0;

    if(!handle_p)
      return -RSBAC_EINVALIDPOINTER;
    if(!*handle_p)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    rsbac_write_lock(&lol_reg_head.lock, &lock_flags);
    reg_item_p = lookup_lol_reg((struct rsbac_list_lol_reg_item_t *) *handle_p);
    if(!reg_item_p)
      {
        rsbac_write_unlock(&lol_reg_head.lock, &lock_flags);
        printk(KERN_WARNING "rsbac_list_lol_destroy: destroying list failed due to invalid handle!\n");
        return -RSBAC_EINVALIDVALUE;
      }
    if(reg_item_p->info.key != key)
      {
        rsbac_write_unlock(&lol_reg_head.lock, &lock_flags);
        printk(KERN_WARNING "rsbac_list_lol_destroy: destroying list %s denied due to invalid key %u!\n",
               reg_item_p->name,
               key);
        return -EPERM;
      }
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_lol_destroy: destroying list %s.\n",
             reg_item_p->name);
#endif
#if defined(CONFIG_RSBAC_PROC) && defined(CONFIG_PROC_FS)
    /* delete proc entry, if it exists */
    if(   (reg_item_p->flags & RSBAC_LIST_BACKUP)
       && reg_item_p->proc_entry_p
      )
      {
        remove_proc_entry(reg_item_p->name, proc_rsbac_backup_p);
        reg_item_p->proc_entry_p = NULL;
      }
#endif
    if(reg_item_p->flags & RSBAC_LIST_PERSIST)
      err = unlink_lol_list(reg_item_p);

    remove_lol_reg(reg_item_p);
    *handle_p = NULL; 
    rsbac_write_unlock(&lol_reg_head.lock, &lock_flags);
    return err;
  }

/* detach from list */
/* list is saved and removed from memory. Call register for new access. */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_detach);
#endif
int rsbac_list_detach(rsbac_list_handle_t * handle_p, rsbac_list_key_t key)
  {
    struct rsbac_list_reg_item_t * reg_item_p;
    u_long lock_flags;
    int err = 0;

    if(!handle_p)
      return -RSBAC_EINVALIDPOINTER;
    if(!*handle_p)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    rsbac_read_lock(&reg_head.lock, &lock_flags);
    reg_item_p = lookup_reg((struct rsbac_list_reg_item_t *) *handle_p);
    if(!reg_item_p)
      {
        rsbac_read_unlock(&reg_head.lock, &lock_flags);
        printk(KERN_WARNING "rsbac_list_detach: detaching list failed due to invalid handle!\n");
        return -RSBAC_EINVALIDVALUE;
      }
    if(reg_item_p->info.key != key)
      {
        rsbac_read_unlock(&reg_head.lock, &lock_flags);
        printk(KERN_WARNING "rsbac_list_detach: detaching list %s denied due to invalid key %u!\n",
               reg_item_p->name,
               key);
        return -EPERM;
      }
#if defined(CONFIG_RSBAC_PROC) && defined(CONFIG_PROC_FS)
    /* delete proc entry, if it exists */
    if(   (reg_item_p->flags & RSBAC_LIST_BACKUP)
       && reg_item_p->proc_entry_p
      )
      {
        remove_proc_entry(reg_item_p->name, proc_rsbac_backup_p);
        reg_item_p->proc_entry_p = NULL;
      }
#endif
#ifndef CONFIG_RSBAC_NO_WRITE
    /* final write, if dirty etc. */
    if(   (reg_item_p->flags & RSBAC_LIST_PERSIST)
       && reg_item_p->dirty
       && !reg_item_p->no_write
      )
      err = write_list(reg_item_p);
#endif
    rsbac_read_unlock(&reg_head.lock, &lock_flags);
    /* disable handle */
    *handle_p = NULL; 
    /* too bad that the list might have been changed again - we do not care anymore */
    rsbac_write_lock(&reg_head.lock, &lock_flags);
    remove_reg(reg_item_p);
    rsbac_write_unlock(&reg_head.lock, &lock_flags);
    return err;
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_detach);
#endif
int rsbac_list_lol_detach(rsbac_list_handle_t * handle_p, rsbac_list_key_t key)
  {
    struct rsbac_list_lol_reg_item_t * reg_item_p;
    u_long lock_flags;
    int err = 0;

    if(!handle_p)
      return -RSBAC_EINVALIDPOINTER;
    if(!*handle_p)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    rsbac_read_lock(&lol_reg_head.lock, &lock_flags);
    reg_item_p = lookup_lol_reg((struct rsbac_list_lol_reg_item_t *) *handle_p);
    if(!reg_item_p)
      {
        rsbac_read_unlock(&lol_reg_head.lock, &lock_flags);
        printk(KERN_WARNING "rsbac_list_lol_detach: detaching list failed due to invalid handle!\n");
        return -RSBAC_EINVALIDVALUE;
      }
    if(reg_item_p->info.key != key)
      {
        rsbac_read_unlock(&lol_reg_head.lock, &lock_flags);
        printk(KERN_WARNING "rsbac_list_lol_detach: detaching list %s denied due to invalid key %u!\n",
               reg_item_p->name,
               key);
        return -EPERM;
      }
#if defined(CONFIG_RSBAC_PROC) && defined(CONFIG_PROC_FS)
    /* delete proc entry, if it exists */
    if(   (reg_item_p->flags & RSBAC_LIST_BACKUP)
       && reg_item_p->proc_entry_p
      )
      {
        remove_proc_entry(reg_item_p->name, proc_rsbac_backup_p);
        reg_item_p->proc_entry_p = NULL;
      }
#endif
#ifndef CONFIG_RSBAC_NO_WRITE
    /* final write, if dirty etc. */
    if(   (reg_item_p->flags & RSBAC_LIST_PERSIST)
       && reg_item_p->dirty
       && !reg_item_p->no_write
      )
      err = write_lol_list(reg_item_p);
#endif
    rsbac_read_unlock(&lol_reg_head.lock, &lock_flags);
    /* disable handle */
    *handle_p = NULL; 
    /* too bad that the list might have been changed again - we do not care anymore */
    rsbac_write_lock(&lol_reg_head.lock, &lock_flags);
    remove_lol_reg(reg_item_p);
    rsbac_write_unlock(&lol_reg_head.lock, &lock_flags);
    return err;
  }

/* set list's no_write flag */
/* TRUE: do not write to disk, FALSE: writing allowed */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_no_write);
#endif
int rsbac_list_no_write(rsbac_list_handle_t handle, rsbac_list_key_t key, boolean no_write)
  {
    struct rsbac_list_reg_item_t * reg_item_p;
    u_long lock_flags;

    if(   !handle
       || (   (no_write != FALSE )
           && (no_write != TRUE )
          )
      )
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    rsbac_read_lock(&reg_head.lock, &lock_flags);
    reg_item_p = lookup_reg((struct rsbac_list_reg_item_t *) handle);
    if(!reg_item_p)
      {
        rsbac_read_unlock(&reg_head.lock, &lock_flags);
        printk(KERN_WARNING "rsbac_list_no_write: setting no_write for list denied due to invalid handle!\n");
        return -RSBAC_EINVALIDVALUE;
      }
    if(reg_item_p->info.key != key)
      {
        rsbac_read_unlock(&reg_head.lock, &lock_flags);
        printk(KERN_WARNING "rsbac_list_no_write: setting no_write for list %s denied due to invalid key %u!\n",
               reg_item_p->name,
               key);
        return -EPERM;
      }
    reg_item_p->no_write = no_write;
    rsbac_read_unlock(&reg_head.lock, &lock_flags);
    return 0;
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_no_write);
#endif
int rsbac_list_lol_no_write(rsbac_list_handle_t handle, rsbac_list_key_t key, boolean no_write)
  {
    struct rsbac_list_lol_reg_item_t * reg_item_p;
    u_long lock_flags;

    if(   !handle
       || (   (no_write != FALSE )
           && (no_write != TRUE )
          )
      )
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    rsbac_read_lock(&lol_reg_head.lock, &lock_flags);
    reg_item_p = lookup_lol_reg((struct rsbac_list_lol_reg_item_t *) handle);
    if(!reg_item_p)
      {
        rsbac_read_unlock(&lol_reg_head.lock, &lock_flags);
        printk(KERN_WARNING "rsbac_list_lol_no_write: setting no_write for list denied due to invalid handle!\n");
        return -RSBAC_EINVALIDVALUE;
      }
    if(reg_item_p->info.key != key)
      {
        rsbac_read_unlock(&lol_reg_head.lock, &lock_flags);
        printk(KERN_WARNING "rsbac_list_lol_no_write: setting no_write for list %s denied due to invalid key %u!\n",
               reg_item_p->name,
               key);
        return -EPERM;
      }
    reg_item_p->no_write = no_write;
    rsbac_read_unlock(&lol_reg_head.lock, &lock_flags);
    return 0;
  }


/********************/
/* List Access      */
/********************/

/* add item */
/* if item for desc exists, the data is updated */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_add_ttl);
#endif
int rsbac_list_add_ttl(
  rsbac_list_handle_t handle,
  rsbac_time_t ttl,
  void * desc,
  void * data)
  {
    struct rsbac_list_reg_item_t * list;
    struct rsbac_list_item_t     * item_p;
    u_long lock_flags, rlock_flags;

    if(!handle || !desc)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_reg_item_t *) handle;
    if(!list || (list->self != list))
      return -RSBAC_EINVALIDVALUE;

    rsbac_read_lock(&reg_head.lock, &rlock_flags);
    if(list->info.data_size && !data)
      {
        rsbac_read_unlock(&reg_head.lock, &rlock_flags);
        return -RSBAC_EINVALIDVALUE;
      }

/*
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_add: adding to list %s.\n",
             list->name);
#endif
*/
    rsbac_write_lock(&list->lock, &lock_flags);

    if(ttl && (ttl != RSBAC_LIST_TTL_KEEP))
      {
        if(ttl > RSBAC_LIST_MAX_AGE_LIMIT)
          ttl = RSBAC_LIST_MAX_AGE_LIMIT;
        ttl += CURRENT_TIME;
      }
    item_p = lookup_item(list, desc);
    if(item_p)
      { /* exists -> update data, if any */
        if(ttl != RSBAC_LIST_TTL_KEEP)
          item_p->max_age = ttl;
        if(data && list->info.data_size)
          {
            if(   list->def_data
               && !item_p->max_age
               && !memcmp(list->def_data, data, list->info.data_size)
              )
              do_remove_item(list, item_p);
            else
              memcpy(((char *) item_p) + sizeof(*item_p) + list->info.desc_size,
                     data, list->info.data_size);
          }
      }
    else
      {
        if(ttl == RSBAC_LIST_TTL_KEEP)
          ttl = 0;
        if(   !list->def_data
           || memcmp(list->def_data, data, list->info.data_size)
          )
          add_item(list, ttl, desc, data);
      }
    list->dirty = TRUE;
    rsbac_write_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&reg_head.lock, &rlock_flags);
    return 0;
  }

/* simple wrapper for 32Bit desc to allow using const values */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_add_ttl_u32);
#endif
int rsbac_list_add_ttl_u32(rsbac_list_handle_t handle, rsbac_time_t ttl, __u32 desc, void * data)
  {
    return rsbac_list_add_ttl(handle, ttl, &desc, data);
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_add);
#endif
int rsbac_list_add(
  rsbac_list_handle_t handle,
  void * desc,
  void * data)
  {
    return rsbac_list_add_ttl(handle, RSBAC_LIST_TTL_KEEP, desc, data);
  }

/* simple wrapper for 32Bit desc to allow using const values */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_add_u32);
#endif
int rsbac_list_add_u32(rsbac_list_handle_t handle, __u32 desc, void * data)
  {
    return rsbac_list_add_ttl(handle, RSBAC_LIST_TTL_KEEP, &desc, data);
  }

/* add list of lists sublist item */
/* if item for desc exists, the data is updated */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_subadd_ttl);
#endif
int rsbac_list_lol_subadd_ttl(
  rsbac_list_handle_t handle,
  rsbac_time_t ttl,
  void * desc,
  void * subdesc,
  void * subdata)
  {
    struct rsbac_list_lol_reg_item_t * list;
    struct rsbac_list_lol_item_t     * sublist;
    struct rsbac_list_item_t     * item_p;
    u_long lock_flags, rlock_flags;
    int err = 0;

    if(!handle || !desc || !subdesc)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_lol_reg_item_t *) handle;
    if(!list || (list->self != list))
      return -RSBAC_EINVALIDVALUE;

    rsbac_read_lock(&lol_reg_head.lock, &rlock_flags);
    if(list->info.subdata_size && !subdata)
      {
        rsbac_read_unlock(&lol_reg_head.lock, &rlock_flags);
        return -RSBAC_EINVALIDVALUE;
      }

/*
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_lol_subadd: adding to list %s.\n",
             list->name);
#endif
*/
    rsbac_write_lock(&list->lock, &lock_flags);

    sublist = lookup_lol_item(list, desc);
    if(   !sublist
       && (list->flags & RSBAC_LIST_DEF_DATA)
      )
      sublist = add_lol_item(list, 0, desc, list->def_data);
    if(sublist)
      {
        if(   sublist->max_age
           && (sublist->max_age <= CURRENT_TIME)
          )
          {
            remove_lol_item(list, desc);
            err = -RSBAC_EINVALIDTARGET;
          }
        else
          {
            /* exists -> lookup subitem */
            if(ttl && (ttl != RSBAC_LIST_TTL_KEEP))
              {
                if(ttl > RSBAC_LIST_MAX_AGE_LIMIT)
                  ttl = RSBAC_LIST_MAX_AGE_LIMIT;
                ttl += CURRENT_TIME;
              }
            item_p = lookup_lol_subitem(list, sublist, subdesc);
            if(item_p)
              { /* exists -> update data, if any */
                if(ttl != RSBAC_LIST_TTL_KEEP)
                  item_p->max_age = ttl;
                if(subdata && list->info.subdata_size)
                  {
                    if(   list->def_subdata
                       && !item_p->max_age
                       && !memcmp(list->def_subdata, subdata, list->info.subdata_size)
                      )
                      do_remove_lol_subitem(sublist, item_p);
                    else
                      memcpy(((char *) item_p) + sizeof(*item_p) + list->info.subdesc_size,
                             subdata,
                             list->info.subdata_size);
                  }
              }
            else
              {
                if(ttl == RSBAC_LIST_TTL_KEEP)
                  ttl = 0;
                if(   !list->def_subdata
                   || memcmp(list->def_subdata, subdata, list->info.subdata_size)
                  )
                  add_lol_subitem(list, sublist, ttl, subdesc, subdata);
              }
            list->dirty = TRUE;
          }
      }
    else
      {
        err = -RSBAC_EINVALIDTARGET;
      }
    rsbac_write_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&lol_reg_head.lock, &rlock_flags);
    return err;
  }

/* simple wrapper for 32Bit desc to allow using const values */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_subadd_ttl_u32);
#endif
int rsbac_list_lol_subadd_ttl_u32(rsbac_list_handle_t handle,
                              rsbac_time_t ttl,
                              __u32 desc,
                              __u32 subdesc,
                              void * subdata)
  {
    return rsbac_list_lol_subadd_ttl(handle, ttl, &desc, &subdesc, subdata);
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_subadd);
#endif
int rsbac_list_lol_subadd(
  rsbac_list_handle_t handle,
  void * desc,
  void * subdesc,
  void * subdata)
  {
    return rsbac_list_lol_subadd_ttl(handle, RSBAC_LIST_TTL_KEEP, desc, subdesc, subdata);
  }

/* simple wrapper for 32Bit desc to allow using const values */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_subadd_u32);
#endif
int rsbac_list_lol_subadd_u32(rsbac_list_handle_t handle,
                              __u32 desc,
                              __u32 subdesc,
                              void * subdata)
  {
    return rsbac_list_lol_subadd_ttl(handle, RSBAC_LIST_TTL_KEEP, &desc, &subdesc, subdata);
  }


/* add list of lists item */
/* if item for desc exists, the data is updated */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_add_ttl);
#endif
int rsbac_list_lol_add_ttl(
  rsbac_list_handle_t handle,
  rsbac_time_t ttl,
  void * desc,
  void * data)
  {
    struct rsbac_list_lol_reg_item_t * list;
    struct rsbac_list_lol_item_t     * item_p;
    u_long lock_flags, rlock_flags;

    if(!handle || !desc)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_lol_reg_item_t *) handle;
    if(!list || (list->self != list))
      return -RSBAC_EINVALIDVALUE;

    rsbac_read_lock(&lol_reg_head.lock, &rlock_flags);
    if(list->info.data_size && !data)
      {
        rsbac_read_unlock(&lol_reg_head.lock, &rlock_flags);
        return -RSBAC_EINVALIDVALUE;
      }

/*
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_lol_add: adding to list %s.\n",
             list->name);
#endif
*/
    rsbac_write_lock(&list->lock, &lock_flags);

    if(ttl && (ttl != RSBAC_LIST_TTL_KEEP))
      {
        if(ttl > RSBAC_LIST_MAX_AGE_LIMIT)
          ttl = RSBAC_LIST_MAX_AGE_LIMIT;
        ttl += CURRENT_TIME;
      }
    item_p = lookup_lol_item(list, desc);
    if(item_p)
      { /* exists -> update data, if any */
        if(ttl != RSBAC_LIST_TTL_KEEP)
          item_p->max_age = ttl;
        if(data && list->info.data_size)
          {
            if(   list->def_data
               && !item_p->max_age
               && !memcmp(list->def_data, data, list->info.data_size)
               && !item_p->count
              )
              do_remove_lol_item(list, item_p);
            else
              memcpy(((char *) item_p) + sizeof(*item_p) + list->info.desc_size,
                     data, list->info.data_size);
          }
      }
    else
      {
        if(ttl == RSBAC_LIST_TTL_KEEP)
          ttl = 0;
        if(   !list->def_data
           || memcmp(list->def_data, data, list->info.data_size)
          )
          add_lol_item(list, ttl, desc, data);
      }
    list->dirty = TRUE;
    rsbac_write_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&lol_reg_head.lock, &rlock_flags);
    return 0;
  }

/* simple wrapper for 32Bit desc to allow using const values */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_add_ttl_u32);
#endif
int rsbac_list_lol_add_ttl_u32(rsbac_list_handle_t handle,
                               rsbac_time_t ttl,
                               __u32 desc,
                               void * data)
  {
    return rsbac_list_lol_add_ttl(handle, ttl, &desc, data);
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_add);
#endif
int rsbac_list_lol_add(
  rsbac_list_handle_t handle,
  void * desc,
  void * data)
  {
    return rsbac_list_lol_add_ttl(handle, RSBAC_LIST_TTL_KEEP, desc, data);
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_add_u32);
#endif
int rsbac_list_lol_add_u32(rsbac_list_handle_t handle, __u32 desc, void * data)
  {
    return rsbac_list_lol_add_ttl(handle, RSBAC_LIST_TTL_KEEP, &desc, data);
  }


/* remove item */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_remove);
#endif
int rsbac_list_remove(
  rsbac_list_handle_t handle,
  void * desc)
  {
    struct rsbac_list_reg_item_t * list;
    u_long lock_flags, rlock_flags;

    if(!handle || !desc)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_reg_item_t *) handle;
    if(!list || (list->self != list))
      return -RSBAC_EINVALIDVALUE;

    rsbac_read_lock(&reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_remove: removing from list %s.\n",
             list->name);
#endif
    rsbac_write_lock(&list->lock, &lock_flags);
    if(lookup_item(list, desc))
      { /* exists -> remove */
        remove_item(list, desc);
        list->dirty = TRUE;
      }
    rsbac_write_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&reg_head.lock, &rlock_flags);
    return 0;
  }

/* simple wrapper for 32Bit desc to allow using const values */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_remove_u32);
#endif
int rsbac_list_remove_u32(rsbac_list_handle_t handle, __u32 desc)
  {
    return rsbac_list_remove(handle, &desc);
  }


/* remove all items */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_remove_all);
#endif
int rsbac_list_remove_all(rsbac_list_handle_t handle)
  {
    struct rsbac_list_reg_item_t * list;
    u_long lock_flags, rlock_flags;

    if(!handle)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;

    rsbac_read_lock(&reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_remove_all: removing all items from list %s.\n",
             list->name);
#endif
    rsbac_write_lock(&list->lock, &lock_flags);
    if(list->head)
      {
        remove_all_items(list);
        list->dirty = TRUE;
      }
    rsbac_write_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&reg_head.lock, &rlock_flags);
    return 0;
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_subremove);
#endif
int rsbac_list_lol_subremove(
  rsbac_list_handle_t handle,
  void * desc,
  void * subdesc)
  {
    struct rsbac_list_lol_reg_item_t * list;
    struct rsbac_list_lol_item_t     * sublist;
    u_long lock_flags, rlock_flags;

    if(!handle || !desc || !subdesc)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_lol_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;

    rsbac_read_lock(&lol_reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_lol_subremove: removing from list of lists %s, device %02u:%02u.\n",
             list->name,
             MAJOR(list->device), MINOR(list->device));
#endif
    rsbac_write_lock(&list->lock, &lock_flags);
    sublist = lookup_lol_item(list, desc);
    if(sublist)
      {
        if(   sublist->max_age
           && (sublist->max_age <= CURRENT_TIME)
          )
          {
            do_remove_lol_item(list, sublist);
          }
        else
          {
            if(lookup_lol_subitem(list, sublist, subdesc))
              { /* exists -> remove and set dirty */
                remove_lol_subitem(list, sublist, subdesc);
                list->dirty = TRUE;
              }
            if(   !sublist->count
               && (   (   list->def_data
                       && !memcmp(((char *) sublist) + sizeof(*sublist) + list->info.desc_size,
                                  list->def_data,
                                  list->info.data_size)
                      )
                   || (   !list->info.data_size
                       && (list->flags & RSBAC_LIST_DEF_DATA)
                      )
                  )
              )
              do_remove_lol_item(list, sublist);
          }
      }
    rsbac_write_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&lol_reg_head.lock, &rlock_flags);
    return 0;
  }

/* simple wrapper for 32Bit desc to allow using const values */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_subremove_u32);
#endif
int rsbac_list_lol_subremove_u32(rsbac_list_handle_t handle, __u32 desc, __u32 subdesc)
  {
    return rsbac_list_lol_subremove(handle, &desc, &subdesc);
  }


/* remove same subitem from all items */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_subremove_from_all);
#endif
int rsbac_list_lol_subremove_from_all(
  rsbac_list_handle_t handle,
  void * subdesc)
  {
    struct rsbac_list_lol_reg_item_t * list;
    struct rsbac_list_lol_item_t     * sublist;
    u_long lock_flags, rlock_flags;

    if(!handle || !subdesc)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_lol_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;

    rsbac_read_lock(&lol_reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_lol_subremove: removing from list of lists %s.\n",
             list->name);
#endif
    rsbac_write_lock(&list->lock, &lock_flags);

    sublist = list->head;
    while(sublist)
      {
        if(lookup_lol_subitem(list, sublist, subdesc))
          { /* exists -> remove and set dirty */
            remove_lol_subitem(list, sublist, subdesc);
            list->dirty = TRUE;
          }
        sublist = sublist->next;
      }
    rsbac_write_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&lol_reg_head.lock, &rlock_flags);
    return 0;
  }

/* simple wrapper for 32Bit desc to allow using const values */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_subremove_from_all_u32);
#endif
int rsbac_list_lol_subremove_from_all_u32(rsbac_list_handle_t handle, __u32 subdesc)
  {
    return rsbac_list_lol_subremove_from_all(handle, &subdesc);
  }


/* remove all subitems */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_subremove_all);
#endif
int rsbac_list_lol_subremove_all(rsbac_list_handle_t handle, void * desc)
  {
    struct rsbac_list_lol_reg_item_t * list;
    struct rsbac_list_lol_item_t     * sublist;
    u_long lock_flags, rlock_flags;

    if(!handle)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_lol_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;

    rsbac_read_lock(&lol_reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_lol_subremove_all: removing all subitems from list of lists %s.\n",
             list->name);
#endif
    rsbac_write_lock(&list->lock, &lock_flags);
    sublist = lookup_lol_item(list, desc);
    if(sublist && sublist->head)
      {
        remove_all_lol_subitems(sublist);
        list->dirty = TRUE;
      }
    rsbac_write_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&lol_reg_head.lock, &rlock_flags);
    return 0;
  }

/* simple wrapper for 32Bit desc to allow using const values */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_subremove_all_u32);
#endif
int rsbac_list_lol_subremove_all_u32(rsbac_list_handle_t handle, __u32 desc)
  {
    return rsbac_list_lol_subremove_all(handle, &desc);
  }


#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_remove);
#endif
int rsbac_list_lol_remove(
  rsbac_list_handle_t handle,
  void * desc)
  {
    struct rsbac_list_lol_reg_item_t * list;
    u_long lock_flags, rlock_flags;

    if(!handle || !desc)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_lol_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;

    rsbac_read_lock(&lol_reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_lol_remove: removing from list of lists %s.\n",
             list->name);
#endif
    rsbac_write_lock(&list->lock, &lock_flags);
    if(lookup_lol_item(list, desc))
      { /* exists -> remove */
        remove_lol_item(list, desc);
        list->dirty = TRUE;
      }
    rsbac_write_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&lol_reg_head.lock, &rlock_flags);
    return 0;
  }

/* simple wrapper for 32Bit desc to allow using const values */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_remove_u32);
#endif
int rsbac_list_lol_remove_u32(rsbac_list_handle_t handle, __u32 desc)
  {
    return rsbac_list_lol_remove(handle, &desc);
  }


/* remove all items */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_remove_all);
#endif
int rsbac_list_lol_remove_all(rsbac_list_handle_t handle)
  {
    struct rsbac_list_lol_reg_item_t * list;
    u_long lock_flags, rlock_flags;

    if(!handle)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_lol_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;

    rsbac_read_lock(&lol_reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_lol_remove_all: removing all items from list of lists %s.\n",
             list->name);
#endif
    rsbac_write_lock(&list->lock, &lock_flags);
    if(list->head)
      {
        remove_all_lol_items(list);
        list->dirty = TRUE;
      }
    rsbac_write_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&lol_reg_head.lock, &rlock_flags);
    return 0;
  }


/* get item data */
/* Item data is copied - we cannot give a pointer, because item could be
 * removed */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_get_data_ttl);
#endif
int rsbac_list_get_data_ttl(rsbac_list_handle_t handle,
                            rsbac_time_t * ttl_p,
                            void * desc,
                            void * data)
  {
    struct rsbac_list_reg_item_t * list;
    struct rsbac_list_item_t     * item_p;
    u_long lock_flags, rlock_flags;
    int err = 0;

    if(!handle || !desc)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;

    rsbac_read_lock(&reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_get_data: getting data from list %s.\n",
             list->name);
#endif
    if(data && !list->info.data_size)
      {
        rsbac_read_unlock(&reg_head.lock, &rlock_flags);
        return -RSBAC_EINVALIDREQUEST;
      }

    rsbac_read_lock(&list->lock, &lock_flags);

    item_p = lookup_item(list, desc);
    if(   item_p
       && (   !item_p->max_age
           || (item_p->max_age > CURRENT_TIME)
          )
      )
      { /* exists -> copy data, if any */
        if(ttl_p)
          {
            if(item_p->max_age)
              *ttl_p = item_p->max_age - CURRENT_TIME;
            else
              *ttl_p = 0;
          }
        if(data)
          {
            memcpy(data,
                   ((char *) item_p) + sizeof(*item_p) + list->info.desc_size,
                   list->info.data_size);
          }
      }
    else
      {
        if(!list->def_data)
          err = -RSBAC_ENOTFOUND;
        else
          {
            if(ttl_p)
              *ttl_p = 0;
            if(data)
              memcpy(data,
                     list->def_data,
                     list->info.data_size);
          }
      }
    rsbac_read_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&reg_head.lock, &rlock_flags);
    return err;
  }

/* simple wrapper for 32Bit desc to allow using const values */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_get_data_ttl_u32);
#endif
int rsbac_list_get_data_ttl_u32(rsbac_list_handle_t handle,
                                rsbac_time_t * ttl_p,
                                __u32 desc,
                                void * data)
  {
    return rsbac_list_get_data_ttl(handle, ttl_p, &desc, data);
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_get_data);
#endif
int rsbac_list_get_data(rsbac_list_handle_t handle, void * desc, void * data)
  {
    return rsbac_list_get_data_ttl(handle, NULL, desc, data);
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_get_data_u32);
#endif
int rsbac_list_get_data_u32(rsbac_list_handle_t handle, __u32 desc, void * data)
  {
    return rsbac_list_get_data_ttl(handle, NULL, &desc, data);
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_get_subdata_ttl);
#endif
int rsbac_list_lol_get_subdata_ttl(
  rsbac_list_handle_t handle,
  rsbac_time_t * ttl_p,
  void * desc,
  void * subdesc,
  void * subdata)
  {
    struct rsbac_list_lol_reg_item_t * list;
    struct rsbac_list_lol_item_t     * sublist;
    struct rsbac_list_item_t         * item_p;
    u_long lock_flags, rlock_flags;
    int err = 0;

    if(!handle || !desc || !subdesc)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_lol_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;

    rsbac_read_lock(&lol_reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_lol_get_subdata: getting data from list %s.\n",
             list->name);
#endif
    if(subdata && !list->info.subdata_size)
      {
        rsbac_read_unlock(&lol_reg_head.lock, &rlock_flags);
        return -RSBAC_EINVALIDREQUEST;
      }

    rsbac_read_lock(&list->lock, &lock_flags);

    sublist = lookup_lol_item(list, desc);
    if(sublist)
      { /* exists -> lookup subitem */
        item_p = lookup_lol_subitem(list, sublist, subdesc);
        if(   item_p
           && (   !item_p->max_age
               || (item_p->max_age > CURRENT_TIME)
              )
          )
          { /* exists -> copy data, if any */
            if(ttl_p)
              {
                if(item_p->max_age)
                  *ttl_p = item_p->max_age - CURRENT_TIME;
                else
                  *ttl_p = 0;
              }
            if(subdata)
              {
                memcpy(subdata,
                       ((char *) item_p) + sizeof(*item_p) + list->info.subdesc_size,
                       list->info.subdata_size);
              }
          }
        else
          {
            if(!list->def_subdata)
              err = -RSBAC_ENOTFOUND;
            else
              {
                if(ttl_p)
                  *ttl_p = 0;
                if(subdata)
                  memcpy(subdata,
                         list->def_subdata,
                         list->info.subdata_size);
              }
          }
      }
    else
      {
        if(!list->def_subdata)
          err = -RSBAC_ENOTFOUND;
        else
          {
            if(ttl_p)
              *ttl_p = 0;
            if(subdata)
              memcpy(subdata,
                     list->def_subdata,
                     list->info.subdata_size);
          }
      }
    rsbac_read_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&lol_reg_head.lock, &rlock_flags);
    return err;
  }

/* simple wrapper for 32Bit desc to allow using const values */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_get_subdata_ttl_u32);
#endif
int rsbac_list_lol_get_subdata_ttl_u32(rsbac_list_handle_t handle,
                                       rsbac_time_t * ttl_p,
                                       __u32 desc,
                                       __u32 subdesc,
                                       void * data)
  {
    return rsbac_list_lol_get_subdata_ttl(handle, ttl_p, &desc, &subdesc, data);
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_get_subdata);
#endif
int rsbac_list_lol_get_subdata(
  rsbac_list_handle_t handle,
  void * desc,
  void * subdesc,
  void * subdata)
  {
    return rsbac_list_lol_get_subdata_ttl(handle, NULL, desc, subdesc, subdata);
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_get_subdata_u32);
#endif
int rsbac_list_lol_get_subdata_u32(rsbac_list_handle_t handle, __u32 desc, __u32 subdesc, void * data)
  {
    return rsbac_list_lol_get_subdata_ttl(handle, NULL, &desc, &subdesc, data);
  }


#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_get_data_ttl);
#endif
int rsbac_list_lol_get_data_ttl(rsbac_list_handle_t handle,
                                rsbac_time_t * ttl_p,
                                void * desc,
                                void * data)
  {
    struct rsbac_list_lol_reg_item_t * list;
    struct rsbac_list_lol_item_t     * item_p;
    u_long lock_flags, rlock_flags;
    int err = 0;

    if(!handle || !desc)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_lol_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;

    rsbac_read_lock(&lol_reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_lol_get_data: getting data from list %s.\n",
             list->name);
#endif
    if(data && !list->info.data_size)
      {
        rsbac_read_unlock(&lol_reg_head.lock, &rlock_flags);
        return -RSBAC_EINVALIDREQUEST;
      }

    rsbac_read_lock(&list->lock, &lock_flags);

    item_p = lookup_lol_item(list, desc);
    if(   item_p
       && (   !item_p->max_age
           || (item_p->max_age > CURRENT_TIME)
          )
      )
      { /* exists -> copy data, if any */
        if(ttl_p)
          {
            if(item_p->max_age)
              *ttl_p = item_p->max_age - CURRENT_TIME;
            else
              *ttl_p = 0;
          }
        if(data)
          {
            memcpy(data,
                   ((char *) item_p) + sizeof(*item_p) + list->info.desc_size,
                   list->info.data_size);
          }
      }
    else
      {
        if(!list->def_data)
          err = -RSBAC_ENOTFOUND;
        else
          {
            if(ttl_p)
              *ttl_p = 0;
            if(data)
              memcpy(data,
                     list->def_data,
                     list->info.data_size);
          }
      }
    rsbac_read_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&lol_reg_head.lock, &rlock_flags);
    return err;
  }

/* simple wrapper for 32Bit desc to allow using const values */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_get_data_ttl_u32);
#endif
int rsbac_list_lol_get_data_ttl_u32(rsbac_list_handle_t handle,
                                    rsbac_time_t * ttl_p,
                                    __u32 desc,
                                    void * data)
  {
    return rsbac_list_lol_get_data_ttl(handle, ttl_p, &desc, data);
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_get_data);
#endif
int rsbac_list_lol_get_data(rsbac_list_handle_t handle,
                            void * desc,
                            void * data)
  {
    return rsbac_list_lol_get_data_ttl(handle, NULL, desc, data);
  }

/* simple wrapper for 32Bit desc to allow using const values */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_get_data_u32);
#endif
int rsbac_list_lol_get_data_u32(rsbac_list_handle_t handle, __u32 desc, void * data)
  {
    return rsbac_list_lol_get_data_ttl(handle, NULL, &desc, data);
  }


#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_get_max_desc);
#endif
int rsbac_list_get_max_desc(rsbac_list_handle_t handle, void * desc)
  {
    struct rsbac_list_reg_item_t * list;
    struct rsbac_list_item_t * item_p;
    u_long lock_flags, rlock_flags;

    if(!handle)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;

    rsbac_read_lock(&reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_get_max_desc: list %s.\n",
             list->name);
#endif
    rsbac_read_lock(&list->lock, &lock_flags);
    item_p = list->tail;
    while(   item_p
          && item_p->max_age
          && (item_p->max_age > CURRENT_TIME)
         )
      item_p = item_p->prev;
    if(item_p)
      memcpy(desc, (char *)item_p + sizeof(*item_p), list->info.desc_size);
    else
      memset(desc, 0, list->info.desc_size);
    rsbac_read_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&reg_head.lock, &rlock_flags);
    return 0;
  }

/* get item desc by data */
/* Item desc is copied - we cannot give a pointer, because item could be
 * removed.
 * If no compare function is provided (NULL value), memcmp is used.
 * Note: The data value given here is always used as second parameter to the
 *       compare function, so you can use different types for storage and
 *       lookup.
 */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_get_desc);
#endif
int rsbac_list_get_desc(rsbac_list_handle_t handle,
                        void * desc,
                        void * data,
                        rsbac_list_data_compare_function_t compare)
  {
    struct rsbac_list_reg_item_t * list;
    struct rsbac_list_item_t     * item_p;
    u_long lock_flags, rlock_flags;
    int err = 0;

    if(!handle || !desc || !data)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;

    rsbac_read_lock(&reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_get_desc: getting desc from list %s.\n",
             list->name);
#endif
    if(!list->info.data_size)
      {
        rsbac_read_unlock(&reg_head.lock, &rlock_flags);
        return -RSBAC_EINVALIDREQUEST;
      }

    rsbac_read_lock(&list->lock, &lock_flags);

    item_p = lookup_item_data(list, data, compare);
    if(item_p)
      { /* exists -> copy desc */
        memcpy(desc,
               ((char *) item_p) + sizeof(*item_p),
               list->info.desc_size);
      }
    else
      {
        err = -RSBAC_ENOTFOUND;
      }
    rsbac_read_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&reg_head.lock, &rlock_flags);
    return err;
  }


/* returns TRUE, if item exists or def_data is defined, FALSE, if not */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_exist);
#endif
int rsbac_list_exist(
  rsbac_list_handle_t handle,
  void * desc)
  {
    struct rsbac_list_reg_item_t * list;
    u_long lock_flags, rlock_flags;
    struct rsbac_list_item_t     * item_p;
    int result;

    if(!handle || !desc)
      return FALSE;
    if(!list_initialized)
      return FALSE;

    list = (struct rsbac_list_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;

    rsbac_read_lock(&reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_exist: testing on list %s.\n",
             list->name);
#endif
    rsbac_read_lock(&list->lock, &lock_flags);

    item_p = lookup_item(list, desc);
    if(   item_p
       && (   !item_p->max_age
           || (item_p->max_age > CURRENT_TIME)
          )
      )
      { /* exists -> TRUE */
        result = TRUE;
      }
    else
      {
        result = FALSE;
      }
    rsbac_read_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&reg_head.lock, &rlock_flags);
    return result;
  }

/* simple wrapper for 32Bit desc to allow using const values */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_exist_u32);
#endif
int rsbac_list_exist_u32(rsbac_list_handle_t handle, __u32 desc)
  {
    return rsbac_list_exist(handle, &desc);
  }

/* does item exist? */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_subexist);
#endif
int rsbac_list_lol_subexist(
  rsbac_list_handle_t handle,
  void * desc,
  void * subdesc)
  {
    struct rsbac_list_lol_reg_item_t * list;
    struct rsbac_list_lol_item_t     * sublist;
    u_long lock_flags, rlock_flags;
    struct rsbac_list_item_t     * item_p;
    int result;

    if(!handle || !desc || !subdesc)
      return FALSE;
    if(!list_initialized)
      return FALSE;

    list = (struct rsbac_list_lol_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;

    rsbac_read_lock(&lol_reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_lol_subexist: testing on list %s.\n",
             list->name);
#endif
    rsbac_read_lock(&list->lock, &lock_flags);

    sublist = lookup_lol_item(list, desc);
    if(sublist)
      { /* exists -> lookup subitem */
        item_p = lookup_lol_subitem(list, sublist, subdesc);
        if(   item_p
           && (   !item_p->max_age
               || (item_p->max_age > CURRENT_TIME)
              )
          )
          { /* exists -> TRUE */
            result = TRUE;
          }
        else
          {
            result = FALSE;
          }
      }
    else
      {
        result = FALSE;
      }
    rsbac_read_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&lol_reg_head.lock, &rlock_flags);
    return result;
  }

/* simple wrapper for 32Bit desc to allow using const values */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_subexist_u32);
#endif
int rsbac_list_lol_subexist_u32(rsbac_list_handle_t handle, __u32 desc, __u32 subdesc)
  {
    return rsbac_list_lol_subexist(handle, &desc, &subdesc);
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_exist);
#endif
int rsbac_list_lol_exist(
  rsbac_list_handle_t handle,
  void * desc)
  {
    struct rsbac_list_lol_reg_item_t * list;
    u_long lock_flags, rlock_flags;
    struct rsbac_list_lol_item_t     * item_p;
    int result;

    if(!handle || !desc)
      return FALSE;
    if(!list_initialized)
      return FALSE;

    list = (struct rsbac_list_lol_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;

    rsbac_read_lock(&lol_reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_lol_exist: testing on list %s.\n",
             list->name);
#endif
    rsbac_read_lock(&list->lock, &lock_flags);

    item_p = lookup_lol_item(list, desc);
    if(   item_p
       && (   !item_p->max_age
           || (item_p->max_age > CURRENT_TIME)
          )
      )
      { /* exists -> TRUE */
        result = TRUE;
      }
    else
      {
        result = FALSE;
      }
    rsbac_read_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&lol_reg_head.lock, &rlock_flags);
    return result;
  }

/* simple wrapper for 32Bit desc to allow using const values */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_exist_u32);
#endif
int rsbac_list_lol_exist_u32(rsbac_list_handle_t handle, __u32 desc)
  {
    return rsbac_list_lol_exist(handle, &desc);
  }


/* count number of elements */
/* returns number of elements or negative error code */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_subcount);
#endif
long rsbac_list_lol_subcount(rsbac_list_handle_t handle, void * desc)
  {
    struct rsbac_list_lol_reg_item_t * list;
    struct rsbac_list_lol_item_t     * sublist;
    u_long lock_flags, rlock_flags;
    long result;

    if(!handle)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_lol_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;

    rsbac_read_lock(&lol_reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_lol_subcount: list %s.\n",
             list->name);
#endif
    rsbac_read_lock(&list->lock, &lock_flags);

    sublist = lookup_lol_item(list, desc);
    if(sublist)
      {
        result = sublist->count;
      }
    else
      {
        result = -RSBAC_ENOTFOUND;
      }
    rsbac_read_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&lol_reg_head.lock, &rlock_flags);
    return result;
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_all_subcount);
#endif
long rsbac_list_lol_all_subcount(rsbac_list_handle_t handle)
  {
    struct rsbac_list_lol_reg_item_t * list;
    struct rsbac_list_lol_item_t     * sublist;
    u_long lock_flags, rlock_flags;
    long result = 0;

    if(!handle)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_lol_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;

    rsbac_read_lock(&lol_reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_lol_subcount: list %s.\n",
             list->name);
#endif
    rsbac_read_lock(&list->lock, &lock_flags);

    sublist = list->head;
    while(sublist)
      {
        result += sublist->count;
        sublist = sublist->next;
      }
    rsbac_read_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&lol_reg_head.lock, &rlock_flags);
    return result;
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_count);
#endif
long rsbac_list_lol_count(rsbac_list_handle_t handle)
  {
    struct rsbac_list_lol_reg_item_t * list;

    if(!handle)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_lol_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;

#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_lol_count: list %s.\n",
             list->name);
#endif
    return list->count;
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_count);
#endif
long rsbac_list_count(rsbac_list_handle_t handle)
  {
    struct rsbac_list_reg_item_t * list;

    if(!handle)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;

#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_count: list %s.\n",
             list->name);
#endif
    return list->count;
  }


/* Get array of all descriptors */
/* Returns number of elements or negative error code */
/* If return value > 0, *array_p contains a pointer to a vmalloc'd array of descs,
   otherwise *array_p is set to NULL. If *array_p has been set, caller must call
   vfree(*array_p) after use! */

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_get_all_desc);
#endif
long rsbac_list_get_all_desc(rsbac_list_handle_t handle, void ** array_p)
  {
    struct rsbac_list_reg_item_t * list;
    struct rsbac_list_item_t     * item_p;
           char                  * buffer;
    u_long lock_flags, rlock_flags;
    u_long offset = 0;
    long result = 0;
    u_int item_size;

    if(!handle)
      return -RSBAC_EINVALIDVALUE;
    if(!array_p)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;
    *array_p = NULL;

    rsbac_read_lock(&reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_get_all_desc: list %s.\n",
             list->name);
#endif
    rsbac_read_lock(&list->lock, &lock_flags);
    if(list->count)
      {
        item_size = list->info.desc_size;
        buffer = vmalloc(item_size * list->count);
        if(buffer)
          {
            item_p = list->head;
            while(item_p)
              {
                if(   !item_p->max_age
                   || (item_p->max_age > CURRENT_TIME)
                  )
                  {
                    memcpy(buffer + offset,
                           ((char *) item_p) + sizeof(*item_p),
                           item_size);
                    offset += item_size;
                    result++;
                  }
                item_p = item_p->next;
              }
            *array_p = buffer;
          }
        else
          {
            result = -RSBAC_ENOMEM;
          }
      }
    rsbac_read_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&reg_head.lock, &rlock_flags);
    return result;
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_get_all_subdesc_ttl);
#endif
long rsbac_list_lol_get_all_subdesc_ttl(rsbac_list_handle_t handle,
                                        void * desc,
                                        void ** array_p,
                                        rsbac_time_t ** ttl_array_p)
  {
    struct rsbac_list_lol_reg_item_t * list;
    struct rsbac_list_lol_item_t     * sublist;
    struct rsbac_list_item_t         * item_p;
           char                      * buffer;
           rsbac_time_t              * ttl_p = NULL;
    u_long lock_flags, rlock_flags;
    u_long offset = 0;
    long result = 0;
    u_int item_size;

    if(!handle)
      return -RSBAC_EINVALIDVALUE;
    if(!array_p)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_lol_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;
    *array_p = NULL;

    rsbac_read_lock(&lol_reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_lol_get_all_desc: list %s.\n",
             list->name);
#endif
    rsbac_read_lock(&list->lock, &lock_flags);
    sublist = lookup_lol_item(list, desc);
    if(sublist && sublist->count)
      {
        item_size = list->info.subdesc_size;
        buffer = vmalloc(item_size * sublist->count);
        if(buffer)
          {
            if(ttl_array_p)
              ttl_p = vmalloc(sizeof(**ttl_array_p) * sublist->count);
            item_p = sublist->head;
            while(item_p)
              {
                if(   !item_p->max_age
                   || (item_p->max_age > CURRENT_TIME)
                  )
                  {
                    memcpy(buffer + offset,
                           ((char *) item_p) + sizeof(*item_p),
                           item_size);
                    if(ttl_p)
                      {
                        if(item_p->max_age)
                          ttl_p[result] = item_p->max_age - CURRENT_TIME;
                        else
                          ttl_p[result] = 0;
                      }
                    offset += item_size;
                    result++;
                  }
                item_p = item_p->next;
              }
            *array_p = buffer;
            if(ttl_array_p)
              *ttl_array_p = ttl_p;
          }
        else
          {
            result = -RSBAC_ENOMEM;
          }
      }
    rsbac_read_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&lol_reg_head.lock, &rlock_flags);
    return result;
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_get_all_subdesc);
#endif
long rsbac_list_lol_get_all_subdesc(rsbac_list_handle_t handle, void * desc, void ** array_p)
  {
    return rsbac_list_lol_get_all_subdesc_ttl(handle, desc, array_p, NULL);
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_get_all_desc);
#endif
long rsbac_list_lol_get_all_desc(rsbac_list_handle_t handle, void ** array_p)
  {
    struct rsbac_list_lol_reg_item_t * list;
    struct rsbac_list_lol_item_t     * item_p;
           char                      * buffer;
    u_long lock_flags, rlock_flags;
    u_long offset = 0;
    long result = 0;
    u_int item_size;

    if(!handle)
      return -RSBAC_EINVALIDVALUE;
    if(!array_p)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_lol_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;
    *array_p = NULL;

    rsbac_read_lock(&lol_reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_lol_get_all_desc: list %s.\n",
             list->name);
#endif
    rsbac_read_lock(&list->lock, &lock_flags);
    if(list->count)
      {
        item_size = list->info.desc_size;
        buffer = vmalloc(item_size * list->count);
        if(buffer)
          {
            item_p = list->head;
            while(item_p)
              {
                if(   !item_p->max_age
                   || (item_p->max_age > CURRENT_TIME)
                  )
                  {
                    memcpy(buffer + offset,
                           ((char *) item_p) + sizeof(*item_p),
                           item_size);
                    offset += item_size;
                    result++;
                  }
                item_p = item_p->next;
              }
            *array_p = buffer;
          }
        else
          {
            result = -RSBAC_ENOMEM;
          }
      }
    rsbac_read_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&lol_reg_head.lock, &rlock_flags);
    return result;
  }

/* Get array of all data */
/* Returns number of elements or negative error code */
/* If return value > 0, *array_p contains a pointer to a vmalloc'd array of datas,
   otherwise *array_p is set to NULL. If *array_p has been set, caller must call
   vfree(*array_p) after use! */

long rsbac_list_get_all_data(rsbac_list_handle_t handle, void ** array_p)
  {
    struct rsbac_list_reg_item_t * list;
    struct rsbac_list_item_t     * item_p;
           char                  * buffer;
    u_long lock_flags, rlock_flags;
    u_long offset = 0;
    long result = 0;
    u_int item_size;
    u_int item_offset;

    if(!handle)
      return -RSBAC_EINVALIDVALUE;
    if(!array_p)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;
    *array_p = NULL;

    rsbac_read_lock(&reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_get_all_data: list %s.\n",
             list->name);
#endif
    rsbac_read_lock(&list->lock, &lock_flags);
    if(!list->info.data_size)
      {
        rsbac_read_unlock(&list->lock, &lock_flags);
        rsbac_read_unlock(&reg_head.lock, &rlock_flags);
        return -RSBAC_EINVALIDREQUEST;
      }
    if(list->count)
      {
        item_size = list->info.data_size;
        item_offset = list->info.desc_size;
        buffer = vmalloc(item_size * list->count);
        if(buffer)
          {
            item_p = list->head;
            while(item_p)
              {
                if(   !item_p->max_age
                   || (item_p->max_age > CURRENT_TIME)
                  )
                  {
                    memcpy(buffer + offset,
                           ((char *) item_p) + sizeof(*item_p) + item_offset,
                           item_size);
                    offset += item_size;
                    result++;
                  }
                item_p = item_p->next;
              }
            *array_p = buffer;
          }
        else
          {
            result = -RSBAC_ENOMEM;
          }
      }
    rsbac_read_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&reg_head.lock, &rlock_flags);
    return result;
  }

long rsbac_list_lol_get_all_subdata(rsbac_list_handle_t handle, void * desc, void ** array_p)
  {
    struct rsbac_list_lol_reg_item_t * list;
    struct rsbac_list_lol_item_t     * sublist;
    struct rsbac_list_item_t         * item_p;
           char                      * buffer;
    u_long lock_flags, rlock_flags;
    u_long offset = 0;
    long result = 0;
    u_int item_size;
    u_int item_offset;

    if(!handle)
      return -RSBAC_EINVALIDVALUE;
    if(!array_p)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_lol_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;
    *array_p = NULL;

    rsbac_read_lock(&lol_reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_lol_get_all_desc: list %s.\n",
             list->name);
#endif
    rsbac_read_lock(&list->lock, &lock_flags);
    if(!list->info.subdata_size)
      {
        rsbac_read_unlock(&list->lock, &lock_flags);
        rsbac_read_unlock(&lol_reg_head.lock, &rlock_flags);
        return -RSBAC_EINVALIDREQUEST;
      }
    sublist = lookup_lol_item(list, desc);
    if(sublist && sublist->count)
      {
        item_size = list->info.subdata_size;
        item_offset = list->info.subdesc_size;
        buffer = vmalloc(item_size * sublist->count);
        if(buffer)
          {
            item_p = sublist->head;
            while(item_p)
              {
                if(   !item_p->max_age
                   || (item_p->max_age > CURRENT_TIME)
                  )
                  {
                    memcpy(buffer + offset,
                           ((char *) item_p) + sizeof(*item_p) + item_offset,
                           item_size);
                    offset += item_size;
                    result++;
                  }
                item_p = item_p->next;
              }
            *array_p = buffer;
          }
        else
          {
            result = -RSBAC_ENOMEM;
          }
      }
    rsbac_read_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&lol_reg_head.lock, &rlock_flags);
    return result;
  }

long rsbac_list_lol_get_all_data(rsbac_list_handle_t handle, void ** array_p)
  {
    struct rsbac_list_lol_reg_item_t * list;
    struct rsbac_list_lol_item_t     * item_p;
           char                      * buffer;
    u_long lock_flags, rlock_flags;
    u_long offset = 0;
    long result = 0;
    u_int item_size;
    u_int item_offset;

    if(!handle)
      return -RSBAC_EINVALIDVALUE;
    if(!array_p)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_lol_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;
    *array_p = NULL;

    rsbac_read_lock(&lol_reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_lol_get_all_desc: list %s.\n",
             list->name);
#endif
    rsbac_read_lock(&list->lock, &lock_flags);
    if(!list->info.data_size)
      {
        rsbac_read_unlock(&list->lock, &lock_flags);
        rsbac_read_unlock(&lol_reg_head.lock, &rlock_flags);
        return -RSBAC_EINVALIDREQUEST;
      }
    if(list->count)
      {
        item_size = list->info.data_size;
        item_offset = list->info.desc_size;
        buffer = vmalloc(item_size * list->count);
        if(buffer)
          {
            item_p = list->head;
            while(item_p)
              {
                if(   !item_p->max_age
                   || (item_p->max_age > CURRENT_TIME)
                  )
                  {
                    memcpy(buffer + offset,
                           ((char *) item_p) + sizeof(*item_p) + item_offset,
                           item_size);
                    offset += item_size;
                    result++;
                  }
                item_p = item_p->next;
              }
            *array_p = buffer;
          }
        else
          {
            result = -RSBAC_ENOMEM;
          }
      }
    rsbac_read_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&lol_reg_head.lock, &rlock_flags);
    return result;
  }


/* Get item size */

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_get_item_size);
#endif
int rsbac_list_get_item_size(rsbac_list_handle_t handle)
  {
    struct rsbac_list_reg_item_t * list;

    if(!handle)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;
    return list->info.desc_size + list->info.data_size;
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_get_subitem_size);
#endif
int rsbac_list_lol_get_subitem_size(rsbac_list_handle_t handle)
  {
    struct rsbac_list_lol_reg_item_t * list;

    if(!handle)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_lol_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;
    return list->info.subdesc_size + list->info.subdata_size;
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_get_item_size);
#endif
int rsbac_list_lol_get_item_size(rsbac_list_handle_t handle)
  {
    struct rsbac_list_lol_reg_item_t * list;

    if(!handle)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_lol_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;
    return list->info.desc_size + list->info.data_size;
  }

/* Get array of all items */
/* Returns number of items or negative error code */
/* If return value > 0, *array_p contains a pointer to a vmalloc'd array of items,
   where desc and data are placed directly behind each other.
   If *array_p has been set (return value > 0), caller must call vfree(*array_p) after use! */

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_get_all_items_ttl);
#endif
long rsbac_list_get_all_items_ttl(rsbac_list_handle_t handle,
                                  void ** array_p,
                                  rsbac_time_t ** ttl_array_p)
  {
    struct rsbac_list_reg_item_t * list;
    struct rsbac_list_item_t     * item_p;
           char                  * buffer;
           rsbac_time_t              * ttl_p = NULL;
    u_long lock_flags, rlock_flags;
    u_long offset = 0;
    long result = 0;
    u_int item_size;

    if(!handle)
      return -RSBAC_EINVALIDVALUE;
    if(!array_p)
      return -RSBAC_EINVALIDPOINTER;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;
    *array_p = NULL;

    rsbac_read_lock(&reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_get_all_items: list %s.\n",
             list->name);
#endif
    rsbac_read_lock(&list->lock, &lock_flags);
    if(list->count)
      {
        item_size = list->info.desc_size + list->info.data_size;
        buffer = vmalloc(item_size * list->count);
        if(buffer)
          {
            if(ttl_array_p)
              ttl_p = vmalloc(sizeof(**ttl_array_p) * list->count);
            item_p = list->head;
            while(item_p)
              {
                if(   !item_p->max_age
                   || (item_p->max_age > CURRENT_TIME)
                  )
                  {
                    memcpy(buffer + offset,
                           ((char *) item_p) + sizeof(*item_p),
                           item_size);
                    if(ttl_p)
                      {
                        if(item_p->max_age)
                          ttl_p[result] = item_p->max_age - CURRENT_TIME;
                        else
                          ttl_p[result] = 0;
                      }
                    offset += item_size;
                    result++;
                  }
                item_p = item_p->next;
              }
            *array_p = buffer;
            if(ttl_array_p)
              *ttl_array_p = ttl_p;
          }
        else
          {
            result = -RSBAC_ENOMEM;
          }
      }
    rsbac_read_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&reg_head.lock, &rlock_flags);
    return result;
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_get_all_items);
#endif
long rsbac_list_get_all_items(rsbac_list_handle_t handle, void ** array_p)
  {
    return rsbac_list_get_all_items_ttl(handle, array_p, NULL);
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_get_all_subitems_ttl);
#endif
long rsbac_list_lol_get_all_subitems_ttl(rsbac_list_handle_t handle,
                                         void * desc,
                                         void ** array_p,
                                         rsbac_time_t ** ttl_array_p)
  {
    struct rsbac_list_lol_reg_item_t * list;
    struct rsbac_list_lol_item_t     * sublist;
    struct rsbac_list_item_t         * item_p;
           char                      * buffer;
           rsbac_time_t              * ttl_p = NULL;
    u_long lock_flags, rlock_flags;
    u_long offset = 0;
    long result = 0;
    u_int item_size;

    if(!handle)
      return -RSBAC_EINVALIDVALUE;
    if(!array_p)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_lol_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;
    *array_p = NULL;

    rsbac_read_lock(&lol_reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_lol_get_all_subitems: list %s.\n",
             list->name);
#endif
    rsbac_read_lock(&list->lock, &lock_flags);
    sublist = lookup_lol_item(list, desc);
    if(sublist && sublist->count)
      {
        item_size = list->info.subdesc_size + list->info.subdata_size;
        buffer = vmalloc(item_size * sublist->count);
        if(buffer)
          {
            if(ttl_array_p)
              ttl_p = vmalloc(sizeof(**ttl_array_p) * sublist->count);
            item_p = sublist->head;
            while(item_p)
              {
                if(   !item_p->max_age
                   || (item_p->max_age > CURRENT_TIME)
                  )
                  {
                    memcpy(buffer + offset,
                           ((char *) item_p) + sizeof(*item_p),
                           item_size);
                    if(ttl_p)
                      {
                        if(item_p->max_age)
                          ttl_p[result] = item_p->max_age - CURRENT_TIME;
                        else
                          ttl_p[result] = 0;
                      }
                    offset += item_size;
                    result++;
                  }
                item_p = item_p->next;
              }
            *array_p = buffer;
            if(ttl_array_p)
              *ttl_array_p = ttl_p;
          }
        else
          {
            result = -RSBAC_ENOMEM;
          }
      }
    rsbac_read_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&lol_reg_head.lock, &rlock_flags);
    return result;
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_get_all_subitems);
#endif
long rsbac_list_lol_get_all_subitems(rsbac_list_handle_t handle, void * desc, void ** array_p)
  {
    return rsbac_list_lol_get_all_subitems_ttl(handle, desc, array_p, NULL);
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_list_lol_get_all_items);
#endif
long rsbac_list_lol_get_all_items(rsbac_list_handle_t handle, void ** array_p)
  {
    struct rsbac_list_lol_reg_item_t * list;
    struct rsbac_list_lol_item_t     * item_p;
           char                      * buffer;
    u_long lock_flags, rlock_flags;
    u_long offset = 0;
    long result = 0;
    u_int item_size;

    if(!handle)
      return -RSBAC_EINVALIDVALUE;
    if(!array_p)
      return -RSBAC_EINVALIDVALUE;
    if(!list_initialized)
      return -RSBAC_ENOTINITIALIZED;

    list = (struct rsbac_list_lol_reg_item_t *) handle;
    if(list->self != list)
      return -RSBAC_EINVALIDVALUE;
    *array_p = NULL;

    rsbac_read_lock(&lol_reg_head.lock, &rlock_flags);
#ifdef CONFIG_RSBAC_DEBUG
    if(rsbac_debug_lists)
      printk(KERN_DEBUG "rsbac_list_lol_get_all_items: list %s.\n",
             list->name);
#endif
    rsbac_read_lock(&list->lock, &lock_flags);
    if(list->count)
      {
        item_size = list->info.desc_size + list->info.data_size;
        buffer = vmalloc(item_size * list->count);
        if(buffer)
          {
            item_p = list->head;
            while(item_p)
              {
                if(   !item_p->max_age
                   || (item_p->max_age > CURRENT_TIME)
                  )
                  {
                    memcpy(buffer + offset,
                           ((char *) item_p) + sizeof(*item_p),
                           item_size);
                    offset += item_size;
                    result++;
                  }
                item_p = item_p->next;
              }
            *array_p = buffer;
          }
        else
          {
            result = -RSBAC_ENOMEM;
          }
      }
    rsbac_read_unlock(&list->lock, &lock_flags);
    rsbac_read_unlock(&reg_head.lock, &rlock_flags);
    return result;
  }


/* end of gen_lists.c */
