www.pudn.com > cryptfs030905.rar > file.c


/*
 * Copyright (c) 1997-2003 Erez Zadok
 * Copyright (c) 2001-2003 Stony Brook University
 *
 * For specific licensing information, see the COPYING file distributed with
 * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING.
 *
 * This Copyright notice must be kept intact and distributed with all
 * fistgen sources INCLUDING sources generated by fistgen.
 */
/*
 *  $Id: file.c,v 1.27 2003/07/29 18:17:20 ezk Exp $
 */

#ifdef HAVE_CONFIG_H
# include 
#endif /* HAVE_CONFIG_H */
#ifdef FISTGEN
# include "fist_cryptfs.h"
#endif /* FISTGEN */
#include "fist.h"
#include "cryptfs.h"


void set_uid_key(const vfs_t  *this_vfs, uid_t uid, char * temp_buf, int buflen)
{
     attached_entry_t *ent;
     struct list_head *p;
     int find_uid=0;

     print_entry_location();
//     printk("XXX: setkey %s\n",temp_buf);


     write_lock(&(stopd(this_vfs)->attachlock));

     list_for_each(p, &(stopd(this_vfs)->attached)){
	     ent=list_entry(p,attached_entry_t,list);
	     if (ent->e_uid==uid){
		     BF_set_key (&(ent->e_key), buflen, temp_buf);
		     find_uid=1;
		     break;
	     }
     }


     write_unlock(&(stopd(this_vfs)->attachlock));

     if(!find_uid)
	     printk("the user(uid:%d) has no right to set the key\n",uid);

     return;
		     
}

/*******************
 * File Operations *
 *******************/


STATIC loff_t
cryptfs_llseek(file_t *file, loff_t offset, int origin)
{
    loff_t err;
    file_t *hidden_file = NULL;

    print_entry_location();
    if (ftopd(file) != NULL) {
	hidden_file = ftohf(file);
    }

    fist_dprint(6, "cryptfs_llseek: file=%p, offset=0x%x, origin=%d\n",
		file, offset, origin);

    /* always set hidden position to this one */
    hidden_file->f_pos = file->f_pos;
    if (file->f_reada) { /* update readahead information if needed */
	hidden_file->f_reada = file->f_reada;
	hidden_file->f_ramax = file->f_ramax;
	hidden_file->f_raend = file->f_raend;
	hidden_file->f_ralen = file->f_ralen;
	hidden_file->f_rawin = file->f_rawin;
    }
    ASSERT(hidden_file->f_reada == file->f_reada);
    ASSERT(hidden_file->f_ramax == file->f_ramax);
    ASSERT(hidden_file->f_raend == file->f_raend);
    ASSERT(hidden_file->f_ralen == file->f_ralen);
    ASSERT(hidden_file->f_rawin == file->f_rawin);

    if (hidden_file->f_op && hidden_file->f_op->llseek)
	err = hidden_file->f_op->llseek(hidden_file, offset, origin);
    else
	err = generic_file_llseek(hidden_file, offset, origin);

    if (err < 0)
	goto out;

    if (err != file->f_pos) {
	file->f_pos = err;
	// ION maybe this?
	// 	file->f_pos = hidden_file->f_pos;
	file->f_reada = 0;
	file->f_version = ++event;
    }
out:
    print_exit_status(err);
    return err;
}


/*
 * generic_file_read updates the atime of upper layer inode.
 * But, it doesn't give us a chance to update the atime of
 * the lower layer inode. This function is a wrapper to
 * generic_file_read. it Updates the atime of the lower level
 * inode if generic_file_read returns without any errors
 * This is to be used only for file reads and only if
 * FIST_FILTER_DATA is defined. The function to be
 * used for directory reads is cryptfs_read
 */
STATIC ssize_t
cryptfs_read_update_atime(file_t *file, char* buf, size_t count, loff_t *ppos)
{
    int err = 0;

    print_entry_location();

    err = generic_file_read(file, buf, count, ppos);

    if (err >= 0)
	UPDATE_ATIME(itohi(file->f_dentry->d_inode));

    print_exit_status(err);
    return err;
}

STATIC ssize_t
cryptfs_read(file_t *file, char *buf, size_t count, loff_t *ppos)
{
    int err = -EINVAL;
    file_t *hidden_file = NULL;
    loff_t pos = *ppos;

    print_entry_location();
    if (ftopd(file) != NULL)
	hidden_file = ftohf(file);

    if (!hidden_file->f_op || !hidden_file->f_op->read)
	goto out;

    err = hidden_file->f_op->read(hidden_file, buf, count, &pos);

    if (err >= 0) {
	/* atime should also be updated for reads of size zero or more */
	fist_copy_attr_atime(file->f_dentry->d_inode,
			     hidden_file->f_dentry->d_inode);
    }

    // MAJOR HACK
    /*
     * because pread() does not have any way to tell us that it is
     * our caller, then we don't know for sure if we have to update
     * the file positions.  This hack relies on read() having passed us
     * the "real" pointer of its struct file's f_pos field.
     */
    if (ppos == &file->f_pos)
	hidden_file->f_pos = *ppos = pos;
    if (hidden_file->f_reada) { /* update readahead information if needed */
	file->f_reada = hidden_file->f_reada;
	file->f_ramax = hidden_file->f_ramax;
	file->f_raend = hidden_file->f_raend;
	file->f_ralen = hidden_file->f_ralen;
	file->f_rawin = hidden_file->f_rawin;
    }

out:
    print_exit_status(err);
    return err;
}


/* this cryptfs_write() does not modify data pages! */
STATIC ssize_t
cryptfs_write(file_t *file, const char *buf, size_t count, loff_t *ppos)
{
    int err = -EINVAL;
    file_t *hidden_file = NULL;
    inode_t *inode;
    inode_t *hidden_inode;
    loff_t pos = *ppos;

    print_entry_location();
    if (ftopd(file) != NULL)
	hidden_file = ftohf(file);

    inode = file->f_dentry->d_inode;
    hidden_inode = itohi(inode);

    if (!hidden_file->f_op || !hidden_file->f_op->write)
	goto out;

    /* adjust for append -- seek to the end of the file */
    if (file->f_flags & O_APPEND)
	pos = inode->i_size;

    err = hidden_file->f_op->write(hidden_file, buf, count, &pos);

    /*
     * copy ctime and mtime from lower layer attributes
     * atime is unchanged for both layers
     */
    if (err >= 0)
	fist_copy_attr_times(inode, hidden_inode);

    /*
     * XXX: MAJOR HACK
     *
     * because pwrite() does not have any way to tell us that it is
     * our caller, then we don't know for sure if we have to update
     * the file positions.  This hack relies on write() having passed us
     * the "real" pointer of its struct file's f_pos field.
     */
    if (ppos == &file->f_pos)
	hidden_file->f_pos = *ppos = pos;

    /* update this inode's size */
    if (pos > inode->i_size)
	inode->i_size = pos;

out:
    print_exit_status(err);
    return err;
}


#if defined(FIST_FILTER_NAME) || defined(FIST_FILTER_SCA)
struct cryptfs_getdents_callback {
    void *dirent;
    dentry_t *dentry;
    filldir_t filldir;
};

/* copied from generic filldir in fs/readir.c */
STATIC int
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,9)
cryptfs_filldir(void *dirent, const char *name, int namlen, loff_t offset, ino_t ino, unsigned int d_type)
# else /* kernel older than 2.4.9 */
cryptfs_filldir(void *dirent, const char *name, int namlen, off_t offset, ino_t ino, unsigned int d_type)
# endif /* kernel older than 2.4.9 */
{
    struct cryptfs_getdents_callback *buf = (struct cryptfs_getdents_callback *) dirent;
    int err;
    char *decoded_name;
    int decoded_length;


    decoded_length = cryptfs_decode_filename(name, namlen, &decoded_name, SKIP_DOTS,
					    buf->dentry->d_inode, buf->dentry->d_sb);
    if (decoded_length < 0)
	return 0;			/* no error, just skip the entry */

    ;

    err = buf->filldir(buf->dirent, decoded_name, decoded_length, offset, ino, d_type);
    kfree(decoded_name);

    return err;
}
#endif /* FIST_FILTER_NAME || FIST_FILTER_SCA */


STATIC int
cryptfs_readdir(file_t *file, void *dirent, filldir_t filldir)
{
    int err = -ENOTDIR;
    file_t *hidden_file = NULL;
    inode_t *inode;
#if defined(FIST_FILTER_NAME) || defined(FIST_FILTER_SCA)
    struct cryptfs_getdents_callback buf;
#endif /* FIST_FILTER_NAME || FIST_FILTER_SCA */

    print_entry_location();
    if (ftopd(file) != NULL)
	hidden_file = ftohf(file);

    inode = file->f_dentry->d_inode;

    fist_checkinode(inode, "cryptfs_readdir");

#if defined(FIST_FILTER_NAME) || defined(FIST_FILTER_SCA)
    /* prepare for callback */
    buf.dirent = dirent;
    buf.dentry = file->f_dentry;
    buf.filldir = filldir;
    err = vfs_readdir(hidden_file, cryptfs_filldir, (void *) &buf);
#else /* not FIST_FILTER_NAME || FIST_FILTER_SCA */
    err = vfs_readdir(hidden_file, filldir, dirent);
#endif /* not FIST_FILTER_NAME || FIST_FILTER_SCA */

    file->f_pos = hidden_file->f_pos;
    if (err > 0)
	fist_copy_attr_atime(inode, hidden_file->f_dentry->d_inode);

    fist_checkinode(inode, "post cryptfs_readdir");
    print_exit_status(err);
    return err;
}


STATIC unsigned int
cryptfs_poll(file_t *file, poll_table *wait)
{
    unsigned int mask = DEFAULT_POLLMASK;
    file_t *hidden_file = NULL;

    print_entry_location();
    if (ftopd(file) != NULL)
	hidden_file = ftohf(file);

    if (!hidden_file->f_op || !hidden_file->f_op->poll)
	goto out;

    mask = hidden_file->f_op->poll(hidden_file, wait);

out:
    print_exit_status(mask);
    return mask;
}


STATIC int
cryptfs_ioctl(inode_t *inode, file_t *file, unsigned int cmd, unsigned long arg)
{
    int err = 0;		/* don't fail by default */
    file_t *hidden_file = NULL;
    vfs_t *this_vfs;
    vnode_t *this_vnode;
    int val;
#ifdef FIST_COUNT_WRITES
    extern unsigned long count_writes, count_writes_middle;
#endif /* FIST_COUNT_WRITES */

    print_entry_location();

    this_vfs = inode->i_sb;
    this_vnode = inode;

    /* check if asked for local commands */
    switch (cmd) {
    case FIST_IOCTL_GET_DEBUG_VALUE:
	val = fist_get_debug_value();
	printk("IOCTL GET: send arg %d\n", val);
	err = put_user(val, (int *) arg);
#ifdef FIST_COUNT_WRITES
	printk("COUNT_WRITES:%lu:%lu\n", count_writes, count_writes_middle);
# endif /* FIST_COUNT_WRITES */
	break;

    case FIST_IOCTL_SET_DEBUG_VALUE:
	err = get_user(val, (int *) arg);
	if (err)
	    break;
	fist_dprint(6, "IOCTL SET: got arg %d\n", val);
	if (val < 0 || val > 20) {
	    err = -EINVAL;
	    break;
	}
	fist_set_debug_value(val);
	break;

    /* add non-debugging fist ioctl's here */
    case FIST_IOCTL_SETKEY:
{
  char temp_buf[16];
  uid_t uid;

  if (fist_get_ioctl_data_SETKEY_ukey(temp_buf, sizeof(temp_buf),&uid, (void *)arg) < 0)
    err = -EFAULT;
  else{
	  set_uid_key(this_vfs, uid, temp_buf, 16);
  }
	  
}
    break;
    case FIST_IOCTL_GETIV:
{
  if (fist_set_ioctl_data_GETIV_outiv(global_iv, sizeof(global_iv), (void *)arg) < 0)
    err = -EFAULT;
}
    break;


    default:
        if (ftopd(file) != NULL) {
	    hidden_file = ftohf(file);
	}
	/* pass operation to hidden filesystem, and return status */
	if (hidden_file && hidden_file->f_op && hidden_file->f_op->ioctl)
	    err = hidden_file->f_op->ioctl(itohi(inode), hidden_file, cmd, arg);
	else
	    err = -ENOTTY;	/* this is an unknown ioctl */
    } /* end of outer switch statement */

    print_exit_status(err);
    return err;
}




STATIC int
cryptfs_open(inode_t *inode, file_t *file)
{
    int err = 0;
    int hidden_mode, hidden_flags;
    file_t *hidden_file = NULL;
    dentry_t *hidden_dentry = NULL;

    print_entry_location();
    ftopd(file) = kmalloc(sizeof(struct cryptfs_file_info *), GFP_KERNEL);
    if (!ftopd(file)) {
	err = -ENOMEM;
	goto out;
    }

    hidden_dentry = cryptfs_hidden_dentry(file->f_dentry); /* CPW: Moved after print_entry */
    fist_print_dentry("cryptfs_open IN hidden_dentry", hidden_dentry);

    dget(hidden_dentry);

    hidden_flags = file->f_flags;
    if ((hidden_flags & O_ACCMODE) == O_WRONLY)
	hidden_flags = (hidden_flags & O_ACCMODE) | O_RDWR;
    if (file->f_flags & O_APPEND) {
	fist_dprint(5, "file is opened in append-only mode\n");
	hidden_flags &= ~O_APPEND; /* turn off O_APPEND flag */
    }

    /*
     * dentry_open will decrement mnt refcnt if err.
     * otherwise fput() will do an mntput() for us upon file close.
     */
    mntget(stopd(inode->i_sb)->hidden_mnt);
    hidden_file = dentry_open(hidden_dentry, stopd(inode->i_sb)->hidden_mnt, hidden_flags);
    if (IS_ERR(hidden_file)) {
	err = PTR_ERR(hidden_file);
	goto out;
    }

    ftohf(file) = hidden_file;	/* link two files */

 out:
    if (err < 0 && ftopd(file)) {
	kfree(ftopd(file));
    }
    fist_print_dentry("cryptfs_open OUT hidden_dentry", hidden_dentry);
    print_exit_status(err);
    return err;
}


STATIC int
cryptfs_flush(file_t *file)
{
    int err = 0;		/* assume ok (see open.c:close_fp) */
    file_t *hidden_file = NULL;

    print_entry_location();

    if (ftopd(file) != NULL)
	hidden_file = ftohf(file); /* CPW: Moved after print_entry_location */

    if (!hidden_file->f_op || !hidden_file->f_op->flush)
	goto out;

    err = hidden_file->f_op->flush(hidden_file);

out:
    print_exit_status(err);
    return err;
}


STATIC int
cryptfs_release(inode_t *inode, file_t *file)
{
    int err = 0;
    file_t *hidden_file = NULL;
    dentry_t *hidden_dentry;

    print_entry_location();

    if (ftopd(file) != NULL)
	hidden_file = ftohf(file);
    kfree(ftopd(file));

    ASSERT(hidden_file != NULL);

    fist_print_dentry("cryptfs_release IN hidden_dentry", hidden_file->f_dentry);
    fist_checkinode(inode, "cryptfs_release");
    fist_dprint(6, "cryptfs_release IN, file->f_count=%d\n", file->f_count);
    /*
     * will decrement file refcount, and if 0, destroy the file,
     * which will call the lower file system's file release function.
     */
    hidden_dentry = hidden_file->f_dentry;
    fput(hidden_file);

    fist_dprint(6, "cryptfs_release done\n");
    fist_checkinode(inode, "post cryptfs_release");

    fist_print_dentry("cryptfs_release OUT hidden_dentry", hidden_dentry);
    print_exit_status(err);
    return err;
}


STATIC int
cryptfs_fsync(file_t *file, dentry_t *dentry, int datasync)
{
    int err = -EINVAL;
    file_t *hidden_file = NULL;
    dentry_t *hidden_dentry;

    print_entry_location();
    if (ftopd(file) != NULL)
	hidden_file = ftohf(file);
    hidden_dentry = cryptfs_hidden_dentry(dentry);

    if (hidden_file->f_op && hidden_file->f_op->fsync) {
	down(&hidden_dentry->d_inode->i_sem);
	err = hidden_file->f_op->fsync(hidden_file, hidden_dentry, datasync);
	up(&hidden_dentry->d_inode->i_sem);
    }

    print_exit_status(err);
    return err;
}


STATIC int
cryptfs_fasync(int fd, file_t *file, int flag)
{
    int err = 0;
    file_t *hidden_file = NULL;

    print_entry_location();
    if (ftopd(file) != NULL)
	hidden_file = ftohf(file);

    if (hidden_file->f_op && hidden_file->f_op->fasync)
	err = hidden_file->f_op->fasync(fd, hidden_file, flag);

    print_exit_status(err);
    return err;
}


STATIC int
cryptfs_lock(file_t *file, int cmd, struct file_lock *fl)
{
    int err = 0;
    file_t *hidden_file = NULL;

    print_entry_location();
    if (ftopd(file) != NULL)
	hidden_file = ftohf(file);

    if (hidden_file->f_op->lock) {
	err = hidden_file->f_op->lock(hidden_file, F_GETLK, fl);
    } else {
	posix_test_lock(hidden_file, fl);
    }

    print_exit_status(err);
    return err;
}


struct file_operations cryptfs_dir_fops =
{
    llseek:	cryptfs_llseek,
    read:	cryptfs_read,
    write:	cryptfs_write,
    readdir:	cryptfs_readdir,
    poll:	cryptfs_poll,
    ioctl:	cryptfs_ioctl,
    mmap:	generic_file_mmap,
    open:	cryptfs_open,
    flush:	cryptfs_flush,
    release:	cryptfs_release,
    fsync:	cryptfs_fsync,
    fasync:	cryptfs_fasync,
    lock:	cryptfs_lock,
    /* not needed: readv */
    /* not needed: writev */
    /* not implemented: sendpage */
    /* not implemented: get_unmapped_area */
};

struct file_operations cryptfs_main_fops =
{
    llseek:	cryptfs_llseek,
    read:	cryptfs_read_update_atime,
    write:	generic_file_write,
    readdir:	cryptfs_readdir,
    poll:	cryptfs_poll,
    ioctl:	cryptfs_ioctl,
    mmap:	generic_file_mmap,
    open:	cryptfs_open,
    flush:	cryptfs_flush,
    release:	cryptfs_release,
    fsync:	cryptfs_fsync,
    fasync:	cryptfs_fasync,
    lock:	cryptfs_lock,
    /* not needed: readv */
    /* not needed: writev */
    /* not implemented: sendpage */
    /* not implemented: get_unmapped_area */
};

/*
 * Local variables:
 * c-basic-offset: 4
 * End:
 */