home | list info | list archive | date index | thread index

Re: [OCLUG-Tech] LKM: redirect file operations

Bill Strosberg wrote:
> I can see the appeal of this, but this would certainly incur a
> performance penalty (checking the name/inode) on every file operation
> done on the system.  Would you not be better to write a daemon function
> to monitor the specific file, checking for alterations and performing
> whatever magic you wish on after the fact?  This way no performance
> penalty is added to everything else using the file system.
>
> Sounds like you want whatever is happening to be transparent to the
> user.  Or you don't want the user to see the magic performed.
>
> --
> Bill

I managed to write the following module and it works ok but needs to be
checked
for eventual bugs and to make the code robust:

Note that there is no overhead incurred for files that are not cloned; only
the cloned file
call to write is intercepted.

I appreciate any help to make the code robust and to complete what could be
missing
(ie. do I have to lock/unlock the inode before setting i_fop).

/*
 *  clone-1.c - intercept the write calls of a given file
*   usage:
*       > insmod clone.ko path=/tmp/test.dat
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/file.h>
#include <linux/highuid.h>
#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/security.h>
#include <linux/syscalls.h>
#include <linux/pagemap.h>

#include <asm/uaccess.h>
#include <asm/unistd.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Taoufik Dachraoui");

static char *path;

module_param(path, charp, 0000);
MODULE_PARM_DESC(path, "Path");

struct inode *inode;
struct file_operations *orig_fop;
struct file_operations *my_fop;

ssize_t my_write (struct file * filp, const char __user *buf, size_t len,
loff_t *ppos)
{
  printk(KERN_INFO "my_write len=%d\n", len);
    return orig_fop->write(filp, buf, len, ppos);
}

ssize_t my_aio_write (struct kiocb *iocb, const struct iovec *iov, unsigned
long nr_segs, loff_t pos)
{
  printk(KERN_INFO "my_aio_write\n");
    return orig_fop->aio_write(iocb, iov, nr_segs, pos);
}

static int __init clone_1_init(void)
//int init_module(void)
{
int error;
struct nameidata nd;
if (path==NULL) return 0;
        error = path_lookup(path, LOOKUP_FOLLOW, &nd);
if (error) {
  printk(KERN_INFO "Clone-1. Error %d\n", error);
  return error;
}
        inode=nd.path.dentry->d_inode;
        printk(KERN_INFO "Clone-1. inode %lu\n", inode->i_ino);
// copy inode->i_fop into my_fop
my_fop=kmalloc(sizeof(*my_fop), GFP_KERNEL);
memcpy(my_fop, inode->i_fop,sizeof(*my_fop));
// set my fop functions
my_fop->write=my_write;
my_fop->aio_write=my_aio_write;
// lock inode ?
orig_fop=inode->i_fop;
inode->i_fop=my_fop;
// unlock inode ?

return error;
}

static void __exit clone_1_exit(void)
//void cleanup_module(void)
{
  // lock inode ?
  if (orig_fop) {
    inode->i_fop=orig_fop;
  }
  // unlock inode ?
  kfree(my_fop);
  printk(KERN_INFO "Goodbye world 1.\n");
}

module_init(clone_1_init);
module_exit(clone_1_exit);