Logo Search packages:      
Sourcecode: dazuko-source version File versions

dazuko_linux.c

/* Dazuko Linux. Allow Linux file access control for 3rd-party applications.
   Copyright (c) 2002, 2003, 2004, 2005 H+BEDV Datentechnik GmbH
   Written by Martin Ritter <mritter@antivir.de>
              John Ogness <jogness@antivir.de>

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; either version 2
   of the License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

/*
TODO:
sys calls (cleanup compile warnings)
validate code
*/

#include "dazuko_linux.h"
#include "dazuko_core.h"

#ifdef DEVFS_SUPPORT
#include <linux/devfs_fs_kernel.h>
#endif

#ifndef DAZUKO_DM
#define DAZUKO_DM 0
#endif

#ifdef HIDDEN_SCT
void **sys_call_table;
extern asmlinkage long sys_exit(int error_code);
#else
extern void *sys_call_table[];
#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
int linux_dazuko_device_read(struct file *file, char *buffer, size_t length, loff_t *pos);
int linux_dazuko_device_write(struct file *file, const char *buffer, size_t length, loff_t *pos);
#else
ssize_t linux_dazuko_device_read(struct file *file, char *buffer, size_t length, loff_t *pos);
ssize_t linux_dazuko_device_write(struct file *file, const char *buffer, size_t length, loff_t *pos);
#endif
int linux_dazuko_device_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long param);
int linux_dazuko_device_open(struct inode *inode, struct file *file);
int linux_dazuko_device_release(struct inode *inode, struct file *file);

extern struct xp_atomic active;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
static struct vfsmount  *orig_rootmnt = NULL;
#endif
        
static struct dentry    *orig_root = NULL;
static int        dev_major = -1;

#if defined(ON_OPEN_SUPPORT)
static asmlinkage long          (*original_sys_open)(const char *filename, int flags, int mode);
static asmlinkage long          (*original_sys_dup)(unsigned int filedes);
#endif
#if defined(ON_OPEN_SUPPORT) || defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT)
static asmlinkage long          (*original_sys_dup2)(unsigned int oldfd, unsigned int newfd);
#endif
#if defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT)
static asmlinkage long          (*original_sys_close)(unsigned int fd);
#endif
#ifdef ON_EXEC_SUPPORT
static asmlinkage int           (*original_sys_execve)(struct pt_regs regs);
#endif
#ifdef ON_UNLINK_SUPPORT
static asmlinkage long        (*original_sys_unlink)(const char *pathname);
#endif
#ifdef ON_RMDIR_SUPPORT
static asmlinkage long        (*original_sys_rmdir)(const char *pathname);
#endif

static struct file_operations fops = {
                              read: linux_dazuko_device_read,           /* read */
                              write: linux_dazuko_device_write,   /* write */
                              ioctl: linux_dazuko_device_ioctl,   /* ioctl */
                              open: linux_dazuko_device_open,           /* open */
                              release: linux_dazuko_device_release,     /* release */
                        };

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)

/* The following code is taken directly from Linux in the file:
   include/linux/sched.h */

#ifndef __wait_event_interruptible
#define __wait_event_interruptible(wq, condition, ret)             \
do {                                             \
      struct wait_queue __wait;                        \
                                                 \
      __wait.task = current;                           \
      add_wait_queue(&wq, &__wait);                    \
      for (;;) {                                 \
            current->state = TASK_INTERRUPTIBLE;             \
            mb();                                \
            if (condition)                             \
                  break;                               \
            if (!signal_pending(current)) {                  \
                  schedule();                    \
                  continue;                      \
            }                                    \
            ret = -ERESTARTSYS;                        \
            break;                                     \
      }                                          \
      current->state = TASK_RUNNING;                         \
      remove_wait_queue(&wq, &__wait);                 \
} while (0)
#endif

#ifndef wait_event_interruptible
#define wait_event_interruptible(wq, condition)              \
({                                               \
      int __ret = 0;                                   \
      if (!(condition))                          \
            __wait_event_interruptible(wq, condition, __ret);\
      __ret;                                           \
})
#define wait_event(wq, condition)                      \
({                                               \
      int __ret = 0;                                   \
      if (!(condition))                          \
            __wait_event_interruptible(wq, condition, __ret);\
      __ret;                                           \
})
#endif

#endif


/* mutex */

inline void xp_init_mutex(struct xp_mutex *mutex)
{
      #ifdef init_MUTEX
            init_MUTEX(&(mutex->mutex));
      #else
            sema_init(&(mutex->mutex), 1);
      #endif
}

inline void xp_down(struct xp_mutex *mutex)
{
      down(&(mutex->mutex));
}

inline void xp_up(struct xp_mutex *mutex)
{
      up(&(mutex->mutex));
}

inline void xp_destroy_mutex(struct xp_mutex *mutex)
{
}


/* read-write lock */

inline void xp_init_rwlock(struct xp_rwlock *rwlock)
{
      #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
            rwlock_init(&(rwlock->rwlock));
      #else
            rwlock->rwlock = RW_LOCK_UNLOCKED;
      #endif
}

inline void xp_write_lock(struct xp_rwlock *rwlock)
{
      write_lock(&(rwlock->rwlock));
}

inline void xp_write_unlock(struct xp_rwlock *rwlock)
{
      write_unlock(&(rwlock->rwlock));
}

inline void xp_read_lock(struct xp_rwlock *rlock)
{
      read_lock(&(rlock->rwlock));
}

inline void xp_read_unlock(struct xp_rwlock *rlock)
{
      read_unlock(&(rlock->rwlock));
}

inline void xp_destroy_rwlock(struct xp_rwlock *rwlock)
{
}


/* wait-notify queue */

inline int xp_init_queue(struct xp_queue *queue)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
      init_waitqueue_head(&(queue->queue));
#else
      queue = NULL;
#endif

      return 0;
}

inline int xp_wait_until_condition(struct xp_queue *queue, int (*cfunction)(void *), void *cparam, int allow_interrupt)
{
      /* wait until cfunction(cparam) != 0 (condition is true) */

      if (allow_interrupt)
      {
            return wait_event_interruptible(queue->queue, cfunction(cparam) != 0);
      }
      else
      {
            wait_event(queue->queue, cfunction(cparam) != 0);
      }

      return 0;
}

inline int xp_notify(struct xp_queue *queue)
{
      wake_up(&(queue->queue));
      return 0;
}

inline int xp_destroy_queue(struct xp_queue *queue)
{
      return 0;
}


/* memory */

inline void* xp_malloc(size_t size)
{
      return kmalloc(size, GFP_KERNEL);
}

inline int xp_free(void *ptr)
{
      kfree(ptr);
      return 0;
}

inline int xp_copyin(const void *user_src, void *kernel_dest, size_t size)
{
      return copy_from_user(kernel_dest, user_src, size);
}

inline int xp_copyout(const void *kernel_src, void *user_dest, size_t size)
{
      return copy_to_user(user_dest, kernel_src, size);
}

inline int xp_verify_user_writable(const void *user_ptr, size_t size)
{
      #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
            return verify_area(VERIFY_WRITE, user_ptr, size);
      #else
            return 0;
      #endif
}

inline int xp_verify_user_readable(const void *user_ptr, size_t size)
{
      #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
            return verify_area(VERIFY_READ, user_ptr, size);
      #else
            return 0;
      #endif
}


/* path attribute */

inline int xp_is_absolute_path(const char *path)
{
      return (path[0] == '/');
}


/* atomic */

inline int xp_atomic_set(struct xp_atomic *atomic, int value)
{
      atomic_set(&(atomic->atomic), value);
      return 0;
}

inline int xp_atomic_inc(struct xp_atomic *atomic)
{
#ifdef MODULE
      if (atomic == &active)
            MOD_INC_USE_COUNT;
#endif

      atomic_inc(&(atomic->atomic));
      return 0;
}

inline int xp_atomic_dec(struct xp_atomic *atomic)
{
#ifdef MODULE
      if (atomic == &active)
            MOD_DEC_USE_COUNT;
#endif

      atomic_dec(&(atomic->atomic));
      return 0;
}

inline int xp_atomic_read(struct xp_atomic *atomic)
{
      return atomic_read(&(atomic->atomic));
}


/* file structure */

static int dazuko_get_filename_dentry(struct xp_file_struct *xfs, int follow_symlinks, const char *local_filename, int user_ptr)
{
      /* We get the appropriate structures in order
       * to acquire the inode and store them in the
       * dazuko_file_struct structure. */

      const char  *filename = NULL;
      int         putname_filename = 0;
      int         filename_length = 0;
      int         rc = 0;

      /* make sure we really need to get the filename */
      if (user_ptr)
      {
            /* grab filename from filename cache */
            filename = (char *)getname(local_filename);

            /* make sure it is a valid name */
            if (IS_ERR(filename))
            {
                  filename = NULL;
                  return 0;
            }

            /* the name will need to be put back */
            putname_filename = 1;
      }
      else
      {
            filename = local_filename;
      }

      filename_length = dazuko_get_filename_length(filename);

      #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
      {
            dazuko_bzero(&(xfs->nd), sizeof(struct nameidata));

            /* initialize nameidata structure for finding file data */
            if (!path_init(filename, (follow_symlinks ? LOOKUP_FOLLOW : 0) | LOOKUP_POSITIVE, &(xfs->nd)))
                  goto dentry_exit;

            if (!xfs->path_release_nd)
            {
                  /* find file data and fill it in nameidata structure */
                  if (path_walk(filename, &(xfs->nd)))  /* !=0 -> error */
                        goto dentry_exit;

                  /* the nameidata will need to be released */
                  xfs->path_release_nd = 1;
            }

            /* get a local copy of the dentry to make kernel version
             * compatibility code eaiser to read */

            /* make sure we don't already have a dentry */
            if (!xfs->dput_dentry)
            {
                  xfs->dentry = dget(xfs->nd.dentry);

                  /* the dentry will need to be put back */
                  xfs->dput_dentry = 1;
            }
      }
      #else
      {
            if (!xfs->dput_dentry)
            {
                  xfs->dentry = lookup_dentry(filename, NULL, (follow_symlinks ? LOOKUP_FOLLOW : 0));
                  if (IS_ERR(xfs->dentry))
                  {
                        xfs->dentry = NULL;
                        goto dentry_exit;
                  }

                  /* the dentry will need to be put back */
                  xfs->dput_dentry = 1;
            }
      }
      #endif

      /* check if this file has no inode */
      if (xfs->dentry->d_inode == NULL)
      {
            goto dentry_exit;
      }
      else
      {
            /* if we made it this far, we got the inode */
            rc = 1;
      }

dentry_exit:
      if (putname_filename)
            putname(filename);

      return rc;
}

static int dazuko_get_fd_dentry(struct xp_file_struct *xfs)
{
      if (current->files != NULL)
      {
            #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
            {
                  read_lock(&current->files->file_lock);
            }
            #endif

            if (current->files->fd != NULL)
            {
                  if (current->files->fd[xfs->fd] != NULL)
                  {
                        if (current->files->fd[xfs->fd]->f_dentry != NULL)
                        {
                              xfs->dentry = dget(current->files->fd[xfs->fd]->f_dentry);
                              xfs->dput_dentry = 1;

                              #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
                              {
                                    if (current->files->fd[xfs->fd]->f_vfsmnt != NULL)
                                    {
                                          xfs->vfsmount = mntget(current->files->fd[xfs->fd]->f_vfsmnt);
                                          xfs->mntput_vfsmount = 1;
                                    }
                                    else
                                    {
                                          dput(xfs->dentry);
                                          xfs->dentry = NULL;
                                          xfs->dput_dentry = 0;
                                    }
                              }
                              #endif
                        }
                  }
            }

            #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
            {
                  read_unlock(&current->files->file_lock);
            }
            #endif
      }

      /* check if we got the dentry */
      if (xfs->dentry == NULL)
            return 0;

      /* check if this file has no inode */
      if (xfs->dentry->d_inode == NULL)
            return 0;

      return 1;
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
static char * __d_path(struct dentry *dentry, struct dentry *root, char *buffer, int buflen)
{
      /* Copy of d_path from linux/dcache.c but using
       * a given root instead of the current root. */

      char * end = buffer+buflen;
      char * retval;

      *--end = '\0';
      buflen--;
      if (dentry->d_parent != dentry && list_empty(&dentry->d_hash)) {
            buflen -= 10;
            end -= 10;
            memcpy(end, " (deleted)", 10);
      }

      /* Get '/' right */
      retval = end-1;
      *retval = '/';

      for (;;) {
            struct dentry * parent;
            int namelen;

            if (dentry == root)
                  break;
            dentry = dentry->d_covers;
            parent = dentry->d_parent;
            if (dentry == parent)
                  break;
            namelen = dentry->d_name.len;
            buflen -= namelen + 1;
            if (buflen < 0)
                  break;
            end -= namelen;
            memcpy(end, dentry->d_name.name, namelen);
            *--end = '/';
            retval = end;
            dentry = parent;
      }
      return retval;
}
#endif

static int dazuko_get_full_filename(struct xp_file_struct *xfs)
{
      /* Get the filename with the full path appended
       * to the beginning. */

      char        *temp;
      struct dentry     *root;

      #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
            struct vfsmount   *rootmnt;
      #endif

      /* check if we need to allocate a buffer */
      if (!xfs->free_page_buffer)
      {
            /* get pre-requisites for d_path function */
            xfs->buffer = (char *)__get_free_page(GFP_USER);

            /* the buffer will need to be freed */
            xfs->free_page_buffer = 1;
      }

      root = dget(orig_root);

      #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
      {
            /* make sure we don't already have a vfsmount */
            if (!xfs->mntput_vfsmount)
            {
                  xfs->vfsmount = mntget(xfs->nd.mnt);

                  /* the vfsmount will need to be put back */
                  xfs->mntput_vfsmount = 1;
            }

            /* build new filename with path included, using temp */

            rootmnt = mntget(orig_rootmnt);

            spin_lock(&dcache_lock);
            temp = __d_path(xfs->dentry, xfs->vfsmount, root, rootmnt, xfs->buffer, PAGE_SIZE);
            spin_unlock(&dcache_lock);

            mntput(rootmnt);
      }
      #else
      {
            /* build new filename with path included, using temp */

            temp = __d_path(xfs->dentry, root, xfs->buffer, PAGE_SIZE);
      }
      #endif

      dput(root);

      /* make sure we really got a new filename */
      if (temp == NULL)
            return 0;

      /* make sure we don't already have a full_filename */
      if (!xfs->free_full_filename)
      {
            xfs->full_filename_length = dazuko_get_filename_length(temp);

            xfs->full_filename = (char *)xp_malloc(xfs->full_filename_length + 1);
            if (!xfs->full_filename)
                  return 0;

            /* the char array will need to be freed */
            xfs->free_full_filename = 1;

            memcpy(xfs->full_filename, temp, xfs->full_filename_length + 1);
      }

      /* we have a filename with the full path */

      return 1;
}

static inline int dazuko_fill_file_struct_cleanup(struct dazuko_file_struct *dfs)
{
      /* Delete all the flagged structures from the
       * given dazuko_file_struct and reset all critical
       * values back to 0. */

      if (dfs->extra_data == NULL)
            return 0;

      #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
      {
            if (dfs->extra_data->mntput_vfsmount)
            {
                  mntput(dfs->extra_data->vfsmount);
                  dfs->extra_data->mntput_vfsmount = 0;
            }
      }
      #endif

      if (dfs->extra_data->free_page_buffer)
      {
            free_page((unsigned long)dfs->extra_data->buffer);
            dfs->extra_data->free_page_buffer = 0;
      }

      if (dfs->extra_data->dput_dentry)
      {
            dput(dfs->extra_data->dentry);
            dfs->extra_data->dput_dentry = 0;
      }

      #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
      {
            if (dfs->extra_data->path_release_nd)
            {
                  path_release(&(dfs->extra_data->nd));
                  dfs->extra_data->path_release_nd = 0;
            }
      }
      #endif

      return 0;
}

inline int xp_fill_file_struct(struct dazuko_file_struct *dfs)
{
      struct dazuko_file_listnode   *listnode;
      int                     follow_symlinks = 0;
      int                     error = -1;
      int                     loopcount = 0;
      char                    *freeparentpath = NULL;
      char                    *parentpath = NULL;
      char                    *rawfilename = NULL;
      int                     i;

      if (dfs == NULL)
            return error;

      /* check if filenames have already been filled in */
      if (dfs->aliases != NULL)
            return 0;

      if (dfs->extra_data == NULL)
            return error;

      /* make sure we can get an inode */
      while (1)
      {
            loopcount++;

            if (dfs->extra_data->user_filename != NULL)
            {
                  if (!dazuko_get_filename_dentry(dfs->extra_data, follow_symlinks, dfs->extra_data->user_filename, 1))
                  {
                        /* we will try to build a "fake" name from the parent directory */

                        freeparentpath = getname(dfs->extra_data->user_filename);
                        /* make sure it is a valid name */
                        if (IS_ERR(freeparentpath))
                        {
                              freeparentpath = NULL;
                              break;
                        }

                        parentpath = freeparentpath;

                        i = dazuko_get_filename_length(parentpath);
                        if (i == 0)
                              break;

                        while (i > 0)
                        {
                              if (parentpath[i] == '/')
                              {
                                    rawfilename = parentpath + i + 1;
                                    parentpath[i] = 0;
                                    break;
                              }

                              i--;
                        }
                        if (i == 0)
                        {
                              if (parentpath[i] == '/')
                              {
                                    rawfilename = parentpath + 1;
                                    parentpath = "/";
                              }
                              else
                              {
                                    rawfilename = parentpath;
                                    parentpath = ".";
                              }
                        }

                        if (!dazuko_get_filename_dentry(dfs->extra_data, follow_symlinks, parentpath, 0))
                        {
                              putname(freeparentpath);
                              freeparentpath = NULL;
                              break;
                        }
                  }
            }
            else
            {
                  if (!dazuko_get_fd_dentry(dfs->extra_data))
                  {
                        break;
                  }
                  else
                  {
                        /* make sure we don't loop a 2nd time */
                        loopcount++;
                  }
            }

            /* make sure we can get the full path */
            if (!dazuko_get_full_filename(dfs->extra_data))
                  break;

            if (freeparentpath != NULL)
            {
                  /* we are working with a "fake" name */

                  parentpath = dfs->extra_data->full_filename;
                  i =  dazuko_get_filename_length(rawfilename);

                  dfs->extra_data->full_filename = (char *)xp_malloc(dfs->extra_data->full_filename_length + 1 + i + 1);
                  if (dfs->extra_data->full_filename == NULL)
                  {
                        /* put things back how they were and get out */
                        dfs->extra_data->full_filename = parentpath;
                        break;
                  }

                  /* copy parent path */
                  memcpy(dfs->extra_data->full_filename, parentpath, dfs->extra_data->full_filename_length);

                  /* possibly copy "/" */
                  if (dfs->extra_data->full_filename[dfs->extra_data->full_filename_length - 1] != '/')
                  {
                        dfs->extra_data->full_filename[dfs->extra_data->full_filename_length] = '/';
                        dfs->extra_data->full_filename_length++;
                  }

                  /* copy filename */
                  memcpy(dfs->extra_data->full_filename + dfs->extra_data->full_filename_length, rawfilename, i + 1);
                  dfs->extra_data->full_filename_length += i;

                  /* free allocated parent path */
                  xp_free(parentpath);
            }
            else
            {
                  dfs->file_p.size = dfs->extra_data->dentry->d_inode->i_size;
                  dfs->file_p.set_size = 1;
                  dfs->file_p.uid = dfs->extra_data->dentry->d_inode->i_uid;
                  dfs->file_p.set_uid = 1;
                  dfs->file_p.gid = dfs->extra_data->dentry->d_inode->i_gid;
                  dfs->file_p.set_gid = 1;
                  dfs->file_p.mode = dfs->extra_data->dentry->d_inode->i_mode;
                  dfs->file_p.set_mode = 1;
                  dfs->file_p.device_type = dfs->extra_data->dentry->d_inode->i_dev;
                  dfs->file_p.set_device_type = 1;
            }

            if (S_ISREG(dfs->extra_data->dentry->d_inode->i_mode))
            {
                  dfs->file_p.type = DAZUKO_REGULAR;
                  dfs->file_p.set_type = 1;
            }
            else if (S_ISLNK(dfs->extra_data->dentry->d_inode->i_mode))
            {
                  dfs->file_p.type = DAZUKO_LINK;
                  dfs->file_p.set_type = 1;
            }
            else if (S_ISDIR(dfs->extra_data->dentry->d_inode->i_mode))
            {
                  dfs->file_p.type = DAZUKO_DIRECTORY;
                  dfs->file_p.set_type = 1;
            }

            listnode = (struct dazuko_file_listnode *)xp_malloc(sizeof(struct dazuko_file_listnode));
            if (listnode == NULL)
                  break;

            dazuko_bzero(listnode, sizeof(struct dazuko_file_listnode));

            listnode->filename = dfs->extra_data->full_filename;
            listnode->filename_length = dfs->extra_data->full_filename_length;  /* the string length */

            dfs->extra_data->free_full_filename = 0;
            dfs->extra_data->full_filename = NULL;
            dfs->extra_data->full_filename_length = 0;

            if (dfs->aliases == NULL)
            {
                  listnode->next = dfs->aliases;
                  dfs->aliases = listnode;
            }
            else
            {
                  listnode->next = dfs->aliases->next;
                  dfs->aliases->next = listnode;
            }

            /* we successfully got the file information */
            error = 0;

            if (!follow_symlinks && dfs->file_p.set_type && dfs->file_p.type == DAZUKO_LINK && loopcount < 2)
            {
                  /* this is a link, we will grab the real path now */

                  follow_symlinks = 1;

                  /* clean up because we are going to fill it again */
                  dazuko_fill_file_struct_cleanup(dfs);
            }
            else
            {
                  /* we've grabbed the real path (or we already have 2 paths), so we are done */

                  break;
            }
      }

      if (freeparentpath != NULL)
            putname(freeparentpath);

      dazuko_fill_file_struct_cleanup(dfs);

      return error;
}

static int dazuko_file_struct_cleanup(struct dazuko_file_struct **dfs)
{
      struct dazuko_file_listnode   *cur;

      if (dfs == NULL)
            return 0;

      if (*dfs == NULL)
            return 0;

      while ((*dfs)->aliases != NULL)
      {
            cur = (*dfs)->aliases;
            (*dfs)->aliases = cur->next;

            if (cur->filename != NULL)
                  xp_free(cur->filename);

            xp_free(cur);
      }

      if ((*dfs)->extra_data != NULL)
            xp_free((*dfs)->extra_data);

      xp_free(*dfs);

      *dfs = NULL;

      return 0;
}


/* daemon id */

static inline int check_parent(struct task_struct *parent, struct task_struct *child)
{
      struct task_struct      *ts = child;

      if (parent == NULL || child == NULL)
            return -1;

      while (1)
      {
            if (ts == parent)
                  return 0;

#ifdef TASKSTRUCT_USES_PARENT
            if (ts->parent == NULL)
                  break;

            if (ts == ts->parent)
                  break;

            ts = ts->parent;
#else
            if (ts->p_pptr == NULL)
                  break;

            if (ts == ts->p_pptr)
                  break;

            ts = ts->p_pptr;
#endif
      }

      return -1;
}

inline int xp_id_compare(struct xp_daemon_id *id1, struct xp_daemon_id *id2, int check_related)
{
      if (id1 == NULL || id2 == NULL)
            return DAZUKO_DIFFERENT;

      /* if file's are available and they match,
       * then we say that the id's match
       * ("file" is only used to unregister daemons and
       * here we allow other processes to do this)
       * Note: this is a Linux-only "hack" */
      if (id1->file != NULL && id1->file == id2->file)
            return DAZUKO_SAME;

      if (id1->pid == id2->pid && id1->current_p == id2->current_p && id1->files == id2->files)
            return DAZUKO_SAME;

      if (check_related)
      {
            if (check_parent(id1->current_p, id2->current_p) == 0)
            {
                  return DAZUKO_CHILD;
            }
            else if (id1->pid == id2->pid || id1->current_p == id2->current_p || id1->files == id2->files)
            {
                  return DAZUKO_SUSPICIOUS;
            }
      }

      return DAZUKO_DIFFERENT;
}

inline int xp_id_free(struct xp_daemon_id *id)
{
      xp_free(id);

      return 0;
}

inline struct xp_daemon_id* xp_id_copy(struct xp_daemon_id *id)
{
      struct xp_daemon_id     *ptr;

      if (id == NULL)
            return NULL;

      ptr = (struct xp_daemon_id *)xp_malloc(sizeof(struct xp_daemon_id));

      if (ptr != NULL)
      {
            ptr->pid = id->pid;
            ptr->file = id->file;
            ptr->current_p = id->current_p;
            ptr->files = id->files;
      }

      return ptr;
}


/* event */

int xp_set_event_properties(struct event_properties *event_p, struct xp_daemon_id *xp_id)
{
      event_p->pid = xp_id->pid;
      event_p->set_pid = 1;

      return 0;
}


/* cache settings */

int xp_init_cache(unsigned long ttl)
{
      return -1;
}


/* system calls */

#if defined(ON_OPEN_SUPPORT)
asmlinkage long   linux_dazuko_sys_open(const char *filename, int flags, int mode)
{
      /* The kernel wants to open the given filename
       * with the given flags and mode. The dazuko_file_struct
       * is used to handle the tricky job of cleaning
       * up the many pieces of memory that may or may
       * not be allocated. */

      struct dazuko_file_struct     *dfs = NULL;
      int                     error = 0;
      int                     fd;
      int                     check_error = 0;
      struct event_properties       event_p;
      struct xp_daemon_id           xp_id;

      xp_id.pid = current->pid;
      xp_id.file = NULL;
      xp_id.current_p = current;
      xp_id.files = current->files;

      check_error = dazuko_check_access(DAZUKO_ON_OPEN, 1, &xp_id, NULL);

      if (!check_error)
      {
            dazuko_bzero(&event_p, sizeof(event_p));
            event_p.flags = flags;
            event_p.set_flags = 1;
            event_p.mode = mode;
            event_p.set_mode = 1;
            event_p.pid = current->pid;
            event_p.set_pid = 1;
            event_p.uid = current->uid;
            event_p.set_uid = 1;

            dfs = (struct dazuko_file_struct *)xp_malloc(sizeof(struct dazuko_file_struct));
            if (dfs != NULL)
            {
                  dazuko_bzero(dfs, sizeof(struct dazuko_file_struct));

                  dfs->extra_data = (struct xp_file_struct *)xp_malloc(sizeof(struct xp_file_struct));
                  if (dfs->extra_data != NULL)
                  {
                        dazuko_bzero(dfs->extra_data, sizeof(struct xp_file_struct));

                        dfs->extra_data->user_filename = filename;

                        error = dazuko_process_access(DAZUKO_ON_OPEN, dfs, &event_p, NULL);

                        dazuko_file_struct_cleanup(&dfs);
                  }
                  else
                  {
                        xp_free(dfs);
                        dfs = NULL;
                  }
            }
      }

      if (error)
      {
            /* access should be blocked */

            fd = -1;
      }
      else
      {
            /* call the standard open function */
            fd = original_sys_open(filename, flags, mode);
      }

      return fd;
}

asmlinkage long linux_dazuko_sys_dup(unsigned int filedes)
{
      struct dazuko_file_struct     *dfs = NULL;
      struct event_properties       event_p;
      struct xp_daemon_id           xp_id;
      int                     error = 0;
      int                     check_error = 0;

      xp_id.pid = current->pid;
      xp_id.file = NULL;
      xp_id.current_p = current;
      xp_id.files = current->files;

      check_error = dazuko_check_access(DAZUKO_ON_OPEN, 1, &xp_id, NULL);

      if (!check_error)
      {
            dazuko_bzero(&event_p, sizeof(event_p));

            if (filedes >= 0 && filedes < current->files->max_fds)
            {
                  event_p.pid = current->pid;
                  event_p.set_pid = 1;
                  event_p.uid = current->uid;
                  event_p.set_uid = 1;

                  dfs = (struct dazuko_file_struct *)xp_malloc(sizeof(struct dazuko_file_struct));
                  if (dfs != NULL)
                  {
                        dazuko_bzero(dfs, sizeof(struct dazuko_file_struct));

                        dfs->extra_data = (struct xp_file_struct *)xp_malloc(sizeof(struct xp_file_struct));
                        if (dfs->extra_data != NULL)
                        {
                              dazuko_bzero(dfs->extra_data, sizeof(struct xp_file_struct));

                              dfs->extra_data->fd = filedes;

                              error = dazuko_process_access(DAZUKO_ON_OPEN, dfs, &event_p, NULL);

                              dazuko_file_struct_cleanup(&dfs);
                        }
                        else
                        {
                              xp_free(dfs);
                              dfs = NULL;
                        }
                  }
            }
            else
            {
                  check_error = -1;
            }
      }

      if (error)
      {
            /* access should be blocked */

            error = -EPERM;
      }
      else
      {
            /* call the standard open function */
            error = original_sys_dup(filedes);
      }

      return error;
}
#endif

#if defined(ON_OPEN_SUPPORT) || defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT)
asmlinkage long linux_dazuko_sys_dup2(unsigned int oldfd, unsigned int newfd)
{
      struct dazuko_file_struct     *dfs = NULL;
      struct event_properties       open_event_p;
      struct xp_daemon_id           xp_id;
      int                     error = 0;
      int                     check_error = 0;
#if defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT)
      struct event_properties       close_event_p;
      int                     will_close_newfd = 0;
#endif

      xp_id.pid = current->pid;
      xp_id.file = NULL;
      xp_id.current_p = current;
      xp_id.files = current->files;

      check_error = dazuko_check_access(DAZUKO_ON_OPEN, 1, &xp_id, NULL);

      if (!check_error)
      {
            dazuko_bzero(&open_event_p, sizeof(open_event_p));

            if (oldfd != newfd && oldfd >= 0 && oldfd < current->files->max_fds)
            {
                  open_event_p.pid = current->pid;
                  open_event_p.set_pid = 1;
                  open_event_p.uid = current->uid;
                  open_event_p.set_uid = 1;

                  dfs = (struct dazuko_file_struct *)xp_malloc(sizeof(struct dazuko_file_struct));
                  if (dfs != NULL)
                  {
                        dazuko_bzero(dfs, sizeof(struct dazuko_file_struct));

                        dfs->extra_data = (struct xp_file_struct *)xp_malloc(sizeof(struct xp_file_struct));
                        if (dfs->extra_data != NULL)
                        {
                              dazuko_bzero(dfs->extra_data, sizeof(struct xp_file_struct));

                              dfs->extra_data->fd = oldfd;

                              error = dazuko_process_access(DAZUKO_ON_OPEN, dfs, &open_event_p, NULL);

                              dazuko_file_struct_cleanup(&dfs);
                        }
                        else
                        {
                              xp_free(dfs);
                              dfs = NULL;
                        }
                  }
            }
            else
            {
                  check_error = -1;
            }
      }

      if (error)
      {
            /* access should be blocked */

            error = -EPERM;
      }
      else
      {
            #if defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT)
            if (newfd >= 0 && newfd < current->files->max_fds)
            {
                  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
                  {
                        read_lock(&current->files->file_lock);
                  }
                  #endif

                  if (current->files->fd[newfd] != NULL)
                  {
                        will_close_newfd = 1;
                  }

                  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
                  {
                        read_unlock(&current->files->file_lock);
                  }
                  #endif
            }
            #endif

            /* call the standard open function */
            error = original_sys_dup2(oldfd, newfd);

            #if defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT)
            {
                  if (!check_error)
                  {
                        if (error >= 0 && will_close_newfd)
                        {
                              dazuko_bzero(&close_event_p, sizeof(close_event_p));

                              close_event_p.pid = current->pid;
                              close_event_p.set_pid = 1;
                              close_event_p.uid = current->uid;
                              close_event_p.set_uid = 1;

                              dfs = (struct dazuko_file_struct *)xp_malloc(sizeof(struct dazuko_file_struct));
                              if (dfs != NULL)
                              {
                                    dazuko_bzero(dfs, sizeof(struct dazuko_file_struct));

                                    dfs->extra_data = (struct xp_file_struct *)xp_malloc(sizeof(struct xp_file_struct));
                                    if (dfs->extra_data != NULL)
                                    {
                                          dazuko_bzero(dfs->extra_data, sizeof(struct xp_file_struct));

                                          dfs->extra_data->fd = newfd;

                                          dazuko_process_access(DAZUKO_ON_CLOSE, dfs, &close_event_p, NULL);

                                          dazuko_file_struct_cleanup(&dfs);
                                    }
                                    else
                                    {
                                          xp_free(dfs);
                                          dfs = NULL;
                                    }
                              }
                        }
                  }
            }
            #endif
      }

      return error;
}
#endif

#if defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT)
asmlinkage long   linux_dazuko_sys_close(unsigned int fd)
{
      /* The kernel wants to close the given file
       * descriptor. */

      struct dazuko_file_struct     *dfs = NULL;
      int                     error = 0;
      int                     check_error = 0;
      struct event_properties       event_p;
      struct xp_daemon_id           xp_id;

      xp_id.pid = current->pid;
      xp_id.file = NULL;
      xp_id.current_p = current;
      xp_id.files = current->files;

      check_error = dazuko_check_access(DAZUKO_ON_CLOSE, 1, &xp_id, NULL);

      if (!check_error && fd >= 0 && fd < current->files->max_fds)
      {
            dazuko_bzero(&event_p, sizeof(event_p));

            event_p.pid = current->pid;
            event_p.set_pid = 1;
            event_p.uid = current->uid;
            event_p.set_uid = 1;

            dfs = (struct dazuko_file_struct *)xp_malloc(sizeof(struct dazuko_file_struct));
            if (dfs != NULL)
            {
                  dazuko_bzero(dfs, sizeof(struct dazuko_file_struct));

                  dfs->extra_data = (struct xp_file_struct *)xp_malloc(sizeof(struct xp_file_struct));
                  if (dfs->extra_data != NULL)
                  {
                        dazuko_bzero(dfs->extra_data, sizeof(struct xp_file_struct));

                        dfs->extra_data->fd = fd;

                        check_error = xp_fill_file_struct(dfs);
                  }
                  else
                  {
                        xp_free(dfs);
                        dfs = NULL;
                  }
            }
      }

      error = original_sys_close(fd);

      if (dfs != NULL)
      {
            if (!check_error)
            {
                  dazuko_process_access(DAZUKO_ON_CLOSE, dfs, &event_p, NULL);
            }

            dazuko_file_struct_cleanup(&dfs);
      }

      return error;
}
#endif

#ifdef ON_EXEC_SUPPORT
asmlinkage int linux_dazuko_sys_execve(struct pt_regs regs)
{
      /* The kernel wants to execute the given file.
       * Because the given structure contains stack
       * address information, we can't simply call
       * the default standard execve. Instead we
       * have to manually inline the standard execve
       * call. */

      struct dazuko_file_struct     *dfs = NULL;
      char                    *filename;
      int                     error = 0;
      int                     check_error = 0;
      struct event_properties       event_p;
      struct xp_daemon_id           xp_id;
      struct slot_list        *sl = NULL;

      xp_id.pid = current->pid;
      xp_id.file = NULL;
      xp_id.current_p = current;
      xp_id.files = current->files;

      check_error = dazuko_check_access(DAZUKO_ON_EXEC, 0, &xp_id, &sl);

      if (!check_error)
      {
            dfs = (struct dazuko_file_struct *)xp_malloc(sizeof(struct dazuko_file_struct));
            if (dfs)
            {
                  dazuko_bzero(dfs, sizeof(struct dazuko_file_struct));

                  dfs->extra_data = (struct xp_file_struct *)xp_malloc(sizeof(struct xp_file_struct));
                  if (dfs->extra_data != NULL)
                  {
                        dazuko_bzero(dfs->extra_data, sizeof(struct xp_file_struct));

                        dfs->extra_data->user_filename = (char *)regs.ebx;

                        dazuko_bzero(&event_p, sizeof(event_p));
                        event_p.pid = current->pid;
                        event_p.set_pid = 1;
                        event_p.uid = current->uid;
                        event_p.set_uid = 1;

                        error = dazuko_process_access(DAZUKO_ON_EXEC, dfs, &event_p, sl);

                        dazuko_file_struct_cleanup(&dfs);
                  }
                  else
                  {
                        xp_free(dfs);
                        dfs = NULL;
                  }
            }
      }

      if (error)
      {
            return error;
      }

      /* call the standard execve function */

      /* We cannot simply call the original version of execvc
       * because the parameter contains stack information and
       * the call will push the execvc call onto a new stack
       * level and seg fault. :( */

      /* The following code only works on i386 machines.
       * It is directly copied from Linux in the file:
       * arch/i386/kernel/process.c */

      #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
      {
            filename = getname((char *) regs.ebx);
            error = PTR_ERR(filename);
            if (IS_ERR(filename))
            {
                  filename = NULL;
                  goto out;
            }
            error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, &regs);
            if (error == 0)
                  current->ptrace &= ~PT_DTRACE;
            putname(filename);
out:
            return error;
      }
      #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,20)
      {
            #ifdef __SMP__
                  lock_kernel();
            #endif
            filename = getname((char *) regs.ebx);
            error = PTR_ERR(filename);
            if (IS_ERR(filename))
            {
                  filename = NULL;
                  goto out;
            }
            error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, &regs);
            if (error == 0)
                  current->ptrace &= ~PT_DTRACE;
            putname(filename);
out:
            #ifdef __SMP__
                  unlock_kernel();
            #endif
            return error;
      }
      #else
      {
            #ifdef __SMP__
                  lock_kernel();
            #endif
            filename = getname((char *) regs.ebx);
            error = PTR_ERR(filename);
            if (IS_ERR(filename))
            {
                  filename = NULL;
                  goto out;
            }
            error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, &regs);
            if (error == 0)
                  current->flags &= ~PF_DTRACE;
            putname(filename);
out:
            #ifdef __SMP__
                  unlock_kernel();
            #endif
            return error;
      }
      #endif
}
#endif

static inline int linux_dazuko_sys_generic(int event, const char *user_pathname, int daemon_is_allowed)
{
      struct dazuko_file_struct     *dfs = NULL;
      int                     error = 0;
      int                     check_error = 0;
      struct event_properties       event_p;
      struct xp_daemon_id           xp_id;
      struct slot_list        *sl = NULL;

      xp_id.pid = current->pid;
      xp_id.file = NULL;
      xp_id.current_p = current;
      xp_id.files = current->files;

      check_error = dazuko_check_access(event, daemon_is_allowed, &xp_id, &sl);

      if (!check_error)
      {
            dfs = (struct dazuko_file_struct *)xp_malloc(sizeof(struct dazuko_file_struct));
            if (dfs)
            {
                  dazuko_bzero(dfs, sizeof(struct dazuko_file_struct));

                  dfs->extra_data = (struct xp_file_struct *)xp_malloc(sizeof(struct xp_file_struct));
                  if (dfs->extra_data != NULL)
                  {
                        dazuko_bzero(dfs->extra_data, sizeof(struct xp_file_struct));

                        dfs->extra_data->user_filename = user_pathname;

                        dazuko_bzero(&event_p, sizeof(event_p));
                        event_p.pid = current->pid;
                        event_p.set_pid = 1;
                        event_p.uid = current->uid;
                        event_p.set_uid = 1;

                        error = dazuko_process_access(event, dfs, &event_p, sl);

                        dazuko_file_struct_cleanup(&dfs);
                  }
                  else
                  {
                        xp_free(dfs);
                        dfs = NULL;
                  }
            }
      }

      return error;
}

#ifdef ON_UNLINK_SUPPORT
asmlinkage long linux_dazuko_sys_unlink(const char *pathname)
{
      int   error;

      error = linux_dazuko_sys_generic(DAZUKO_ON_UNLINK, pathname, 1);

      if (error)
            return error;

      return original_sys_unlink(pathname);
}
#endif

#ifdef ON_RMDIR_SUPPORT
asmlinkage long linux_dazuko_sys_rmdir(const char *pathname)
{
      int   error;

      error = linux_dazuko_sys_generic(DAZUKO_ON_RMDIR, pathname, 1);

      if (error)
            return error;

      return original_sys_rmdir(pathname);
}
#endif


/* system hook */

#ifdef HIDDEN_SCT
static void** dazuko_get_sct()
{
      unsigned long     ptr;
      extern int  loops_per_jiffy;
      unsigned long     *p;

      for (ptr=(unsigned long)&loops_per_jiffy ; ptr<(unsigned long)&boot_cpu_data ; ptr+=sizeof(void *))
      {
            p = (unsigned long *)ptr;
            if (p[6] == (unsigned long)sys_close)
            {
                  return (void **)p;
            }
      }

      return NULL;
}
#endif

#define DAZUKO_HOOK(syscall_func) do \
{ \
      original_sys_##syscall_func = sys_call_table[__NR_##syscall_func]; \
      sys_call_table[__NR_##syscall_func] = linux_dazuko_sys_##syscall_func; \
      DPRINT(("dazuko: hooked sys_" #syscall_func "\n")); \
} \
while (0)

inline int xp_sys_hook()
{
      /* Called insmod when inserting the module. */

#ifdef HIDDEN_SCT
      sys_call_table = dazuko_get_sct();
      if (sys_call_table == NULL)
      {
            xp_print("dazuko: panic (sys_call_table == NULL)\n");
            return -1;
      }
#endif

      /* Make sure we have a valid task_struct. */

      if (current == NULL)
      {
            xp_print("dazuko: panic (current == NULL)\n");
            return -1;
      }
      if (current->fs == NULL)
      {
            xp_print("dazuko: panic (current->fs == NULL)\n");
            return -1;
      }
      if (current->fs->root == NULL)
      {
            xp_print("dazuko: panic (current->fs->root == NULL)\n");
            return -1;
      }
      #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
      {
            if (current->fs->rootmnt == NULL)
            {
                  xp_print("dazuko: panic (current->fs->rootmnt == NULL)\n");
                  return -1;
            }
      }
      #endif

      /* register the dazuko device */
#ifdef DEVFS_SUPPORT
      dev_major = devfs_register_chrdev(0, DEVICE_NAME, &fops);
      devfs_register(NULL, DEVICE_NAME, DEVFS_FL_DEFAULT,
            dev_major, 0, S_IFCHR | S_IRUSR | S_IWUSR,
            &fops, NULL);
#else
      dev_major = register_chrdev(DAZUKO_DM, DEVICE_NAME, &fops);
#endif
      if (dev_major < 0)
      {
            xp_print("dazuko: unable to register device chrdev, err=%d\n", dev_major);
            return dev_major;
      }

      /* Grab the current root. This is assumed to be the real.
       * If it is not the real root, we could have problems
       * looking up filenames. */

      #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
      {
            read_lock(&current->fs->lock);
            orig_rootmnt = current->fs->rootmnt;
      }
      #endif

      orig_root = current->fs->root;

      #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
            read_unlock(&current->fs->lock);
      #endif

      /* do a file syncronization on all devices (IMPORTANT!) and replace system calls */
      #ifdef __SMP__
            lock_kernel();
      #endif

      fsync_dev(0);

#if defined(ON_OPEN_SUPPORT)
      DAZUKO_HOOK(open);
      DAZUKO_HOOK(dup);
#endif
#if defined(ON_OPEN_SUPPORT) || defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT)
      DAZUKO_HOOK(dup2);
#endif
      
#if defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT)
      DAZUKO_HOOK(close);
#endif

#ifdef ON_EXEC_SUPPORT
      DAZUKO_HOOK(execve);
#endif

#ifdef ON_UNLINK_SUPPORT
      DAZUKO_HOOK(unlink);
#endif

#ifdef ON_RMDIR_SUPPORT
      DAZUKO_HOOK(rmdir);
#endif

      #ifdef __SMP__
            unlock_kernel();
      #endif
      /* done syncing and replacing */

      /* initialization complete */

      return 0;
}

#define DAZUKO_UNHOOK(syscall_func) do \
{ \
      if (sys_call_table[__NR_##syscall_func] != linux_dazuko_sys_##syscall_func) \
            xp_print("dazuko: " #syscall_func " system call has been changed (system may be left in an unstable state!)\n"); \
      sys_call_table[__NR_##syscall_func] = original_sys_##syscall_func; \
      DPRINT(("dazuko: unhooked sys_" #syscall_func "\n")); \
} \
while (0)

inline int xp_sys_unhook()
{
      /* Called by rmmod when removing the module. */

      int   error;

      /* do a file syncronization on all devices (IMPORTANT!) and replace system calls */
      #ifdef __SMP__
            lock_kernel();
      #endif

      fsync_dev(0);

#if defined(ON_OPEN_SUPPORT)
      DAZUKO_UNHOOK(open);
      DAZUKO_UNHOOK(dup);
#endif
#if defined(ON_OPEN_SUPPORT) || defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT)
      DAZUKO_UNHOOK(dup2);
#endif

#if defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT)
      DAZUKO_UNHOOK(close);
#endif

#ifdef ON_EXEC_SUPPORT
      DAZUKO_UNHOOK(execve);
#endif

#ifdef ON_UNLINK_SUPPORT
      DAZUKO_UNHOOK(unlink);
#endif

#ifdef ON_RMDIR_SUPPORT
      DAZUKO_UNHOOK(rmdir);
#endif

      #ifdef __SMP__
            unlock_kernel();
      #endif
      /* done syncing and replacing */

#ifdef DEVFS_SUPPORT
      error = devfs_unregister_chrdev(dev_major, DEVICE_NAME);
      devfs_unregister(devfs_find_handle(NULL, DEVICE_NAME, dev_major, 0, DEVFS_SPECIAL_CHR, 0));
#else
      error = unregister_chrdev(dev_major, DEVICE_NAME);
#endif
      if (error < 0)
      {
            xp_print("dazuko: error unregistering chrdev, err=%d\n", error);
      }

      return 0;
}


/* output */

int xp_print(const char *fmt, ...)
{
      va_list args;
      char *p;
      size_t size = 1024;

      p = (char *)xp_malloc(size);
      if (!p)
            return -1;

      va_start(args, fmt);
      dazuko_vsnprintf(p, size-1, fmt, args);
      va_end(args);

      p[size-1] = 0;

      printk(p);

      xp_free(p);

      return 0;
}


/* ioctl's */

int linux_dazuko_device_open(struct inode *inode, struct file *file)
{
      DPRINT(("dazuko: linux_dazuko_device_open() [%d]\n", current->pid));

      return 0;
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
int linux_dazuko_device_read(struct file *file, char *buffer, size_t length, loff_t *pos)
#else
ssize_t linux_dazuko_device_read(struct file *file, char *buffer, size_t length, loff_t *pos)
#endif
{
      /* Reading from the dazuko device simply
       * returns the device number. This is to
       * help out the daemon. */

      char  tmp[20];
      size_t      dev_major_len;

      DPRINT(("dazuko: linux_dazuko_device_read() [%d]\n", current->pid));

      /* only one read is allowed */
      if (*pos != 0)
            return 0;

      if (dev_major < 0)
            return -ENODEV;

      /* print dev_major to a string
       * and get length (with terminator) */
      dazuko_bzero(tmp, sizeof(tmp));

      dev_major_len = dazuko_snprintf(tmp, sizeof(tmp), "%d", dev_major) + 1;

      if (tmp[sizeof(tmp)-1] != 0)
      {
            xp_print("dazuko: failing device_read, device number overflow for dameon %d (dev_major=%d)\n", current->pid, dev_major);
            return -EFAULT;
      }

      if (length < dev_major_len)
            return -EINVAL;

      /* copy dev_major string to userspace */
      if (xp_copyout(tmp, buffer, dev_major_len) != 0)
            return -EFAULT;

      *pos = dev_major_len;

      return dev_major_len;
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
int linux_dazuko_device_write(struct file *file, const char *buffer, size_t length, loff_t *pos)
{
      /* multiple device_write entries are not allowed
       * with older kernels, so we force compat1 mode */

      return length;
}
#else
ssize_t linux_dazuko_device_write(struct file *file, const char *buffer, size_t length, loff_t *pos)
{
      struct xp_daemon_id     xp_id;
      char              tmpbuffer[32];
      int               size;

      size = length;
      if (length >= sizeof(tmpbuffer))
            size = sizeof(tmpbuffer) -1;

      /* copy request pointer string to kernelspace */
      if (xp_copyin(buffer, tmpbuffer, size) != 0)
            return -EFAULT;

      tmpbuffer[size] = 0;

      xp_id.pid = current->pid;
      xp_id.file = file;
      xp_id.current_p = current;
      xp_id.files = current->files;

      if (dazuko_handle_user_request(tmpbuffer, &xp_id) == 0)
            return length;
      else
            return -EINTR;
}
#endif

int linux_dazuko_device_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long param)
{
      /* A daemon uses this function to interact with
       * the kernel. A daemon can set scanning parameters,
       * give scanning response, and get filenames to scan. */

      struct xp_daemon_id     xp_id;
      int               error = 0;

      if (param == 0)
      {
            xp_print("dazuko: error: linux_dazuko_device_ioctl(..., 0)\n");
            return -EFAULT;
      }

      xp_id.pid = current->pid;
      xp_id.file = file;
      xp_id.current_p = current;
      xp_id.files = current->files;

      error = dazuko_handle_user_request_compat1((void *)param, _IOC_NR(cmd), &xp_id);

      if (error != 0)
      {
            /* general error occurred */

            return -EPERM;
      }

      return error;
}

int linux_dazuko_device_release(struct inode *inode, struct file *file)
{
      struct xp_daemon_id     xp_id;

      DPRINT(("dazuko: dazuko_device_release() [%d]\n", current->pid));

      xp_id.pid = current->pid;
      xp_id.file = file;
      xp_id.current_p = current;
      xp_id.files = current->files;

      return dazuko_unregister_daemon(&xp_id);
}


/* init/exit */

int __init linux_dazuko_init(void)
{
      return dazuko_init();
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
void __exit linux_dazuko_exit(void)
#else
void linux_dazuko_exit(void)
#endif
{
      dazuko_exit();
}


#ifdef MODULE
        
MODULE_AUTHOR("H+BEDV Datentechnik GmbH <linux_support@antivir.de>");
MODULE_DESCRIPTION("allow 3rd-party file access control");
#ifdef MODULE_LICENSE
MODULE_LICENSE("GPL");
#else
static const char __module_license[] __attribute__((section(".modinfo"))) = "license=GPL";
#endif   

int init_module(void)
{
      return linux_dazuko_init();
}

void cleanup_module(void)
{
      linux_dazuko_exit();
}
                
EXPORT_NO_SYMBOLS;

#else

module_init(linux_dazuko_init);
module_exit(linux_dazuko_exit);
/* module_init(int linux_dazuko_init(void)); */
/* module_exit(void linux_dazuko_exit(void)); */

#endif

Generated by  Doxygen 1.6.0   Back to index