www.pudn.com > cryptfs030905.rar > attach.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: attach.c,v 1.17 2003/07/10 02:53:00 cwright Exp $
 */

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

/****************************************************************************
 *** ATTACH/DETACH UTILITY AND LIST FUNCTIONS				  ***
 ****************************************************************************/

/* delete an entry if it exists */
/* return true if deleted, false if not deleted */
static __inline__ int del_attached_ent(super_block_t *sb, const char *name, unsigned short namelen)
{
    attached_entry_t *ent;
    struct list_head *p, *t;
    int ret = 0;

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

    list_for_each_safe(p, t, &(stopd(sb)->attached)) {
	ent = list_entry(p, attached_entry_t, list);

	if (ent->e_dentry->d_name.len == namelen  &&
	    strncmp(ent->e_dentry->d_name.name, name, namelen) == 0) {
	    /* found entry: first unlink it from list, then dput, kfree*/
	    list_del(p);
	    fist_print_dentry("DAEd", ent->e_dentry);
	    d_drop(ent->e_dentry);  //CPW: FIXME: d_drop correct?
	    fist_print_dentry("DAEp", ent->e_dentry);
	    dput(ent->e_dentry);
            kfree(ent);

	    ret = 1;
	    goto out;
	}
    }

out:
    write_unlock(&(stopd(sb)->attachlock));
    return ret;
}

/* add a new entry to attached list, not checking for dups */
static __inline__ void add_attached(super_block_t *sb, struct dentry *d)
{
    attached_entry_t *ent = NULL;

    ASSERT(d);

    write_lock(&(stopd(sb)->attachlock));
    /* allocate and fill in entry */
    ent = kmalloc(sizeof(attached_entry_t), GFP_KERNEL);
    ASSERT(ent);
    ent->e_dentry = dget(d);

    //initialize the key for the attached entry under the mount point
    memset((void*)&ent->e_key,0,sizeof(BF_KEY));
    //assign the key to the attached entry under the mount point
    dtokey(d)=&ent->e_key;
    
    
    /*add the current uid to the added entry*/
    ent->e_uid=current->fsuid;   
    /* add to list */
    list_add_tail(&(ent->list), &(stopd(sb)->attached));

    write_unlock(&(stopd(sb)->attachlock));
}


/*
 * Attach (stack) a node named 'from', pointing to 'to' (hidden),
 * in the root of the mounted file system.
 */
int
do_attach(inode_t *inode, file_t *file, const char *from, const char *to)
{
    int err = -EINVAL;
    dentry_t *hidden_dentry = ERR_PTR(-EINVAL);
    dentry_t *this_dentry = NULL;
    struct nameidata to_nd;
    super_block_t *this_sb;

    print_entry_location();
    printk("XXX: ATTACH %s -> %s\n", from, to);

    // XXX: major issue: what inode number do we use?
    // hidden inums could be duplicate...

    /* get hidden (to) dentry */
    if (path_init(to, LOOKUP_FOLLOW, &to_nd))
	err = path_walk(to, &to_nd);
    if (err) {
	printk("%s: error accessing hidden directory '%s'\n", __FUNCTION__, to);
	goto outpath;
    }
    // hidden_dentry->d_inode must exist
    hidden_dentry = to_nd.dentry;
    if (IS_ERR(hidden_dentry)) {
	printk(KERN_WARNING "%s: lookup_dentry failed (err = %ld)\n",
           __FUNCTION__, PTR_ERR(hidden_dentry));
	err = -ENOENT;
	goto outpath;
    }
    dget(hidden_dentry);
    if (!hidden_dentry->d_inode) {
	printk(KERN_WARNING "%s: no directory to interpose on\n", __FUNCTION__);
	err = -ENOENT;
	goto outdput_hidden;
    }
    if (!S_ISDIR(hidden_dentry->d_inode->i_mode)) {
	printk(KERN_WARNING
	  "%s: can only interpose on top of directories\n", __FUNCTION__);
	err = -ENOTDIR;
	goto outdput_hidden;
    }
    if (vfs_permission(hidden_dentry->d_inode, MAY_READ | MAY_EXEC)) {
	printk(KERN_WARNING
	  "%s: can not interpose on directories without rx permission\n", __FUNCTION__);
	err = -EPERM;
	goto outdput_hidden;
    }
    if (vfs_permission(inode, MAY_WRITE | MAY_EXEC)) {
	printk(KERN_WARNING
	  "%s: can not interpose without wx permission on mountpoint\n", __FUNCTION__);
	err = -EPERM;
	goto outdput_hidden;
    }
	    
    this_sb = inode->i_sb;
    fist_print_dentry("TOd", hidden_dentry);

    /* XXX: mkdir the "from" dir??? */
    /* XXX: do we need to dget() file->f_dentry (parent dir)? */
    this_dentry = lookup_one_len(from, file->f_dentry, strlen(from));
    if (IS_ERR(this_dentry)) {
	err = PTR_ERR(this_dentry);
	printk("lookup_one_len %*s in %*s failed",
	       (int)strlen(from), from,
	       file->f_dentry->d_name.len, file->f_dentry->d_name.name);
	goto outdput_hidden;
    }
    /* must initialize dentry operations */
    this_dentry->d_op = &cryptfs_dops;
    fist_print_dentry("FROMd", this_dentry);

    //prevent the same dentry name under the mount point
    if(dtopd(this_dentry)){
    	printk("the directory already exists:%s\n",this_dentry->d_name.name);
	err=-ENOMEM;
	goto outdput;
    }
    // prepare hidden_dentry and then interpose both
//    ASSERT(dtopd(this_dentry) == NULL);
    dtopd(this_dentry) = (struct cryptfs_dentry_info *)
      kmalloc(sizeof(struct cryptfs_dentry_info), GFP_KERNEL);
    if (!dtopd(this_dentry)) {
	err = -ENOMEM;
	goto outdput;
    }

    dtohd(this_dentry) = hidden_dentry;

    err = cryptfs_interpose(hidden_dentry, this_dentry, this_sb, 0);
    if (err)
	goto outfree;
    fist_print_dentry("DAd", this_dentry);
    fist_print_dentry("DAp", this_dentry->d_parent);
    fist_print_inode("DAi", this_dentry->d_inode);


    //dtokey(this_dentry) is done in the function add_attached
	    
    add_attached(inode->i_sb, this_dentry);

    /* increment the reference count for our superblock */
    mntget(file->f_vfsmnt);

    // XXX: how do we link root dentry to attached ones?
    d_rehash(this_dentry);

    goto outdput;

 outfree:
    d_drop(this_dentry);
    kfree(dtopd(this_dentry));
    dtopd(this_dentry) = NULL;
 outdput_hidden:
    dput(hidden_dentry);
 outdput:
    dput(this_dentry);
 outpath:
    path_release(&to_nd);
 out:
    print_exit_status(err);
    return err;

}

/*
 * Detach (unstack) a node named 'from' in the root of the mounted file system.
 */
int
do_detach(inode_t *inode, file_t *file, const char *from)
{
    attached_entry_t *ent;
    struct list_head *p;
    int namelen;

    int err = 0;

    print_entry_location();

    printk("XXX: DETACH %s\n", from);

    namelen = strlen(from);

    if (!capable(CAP_SYS_ADMIN)) {
    	read_lock(&(stopd(inode->i_sb)->attachlock));

        list_for_each(p, &(stopd(inode->i_sb)->attached)) {
            ent = list_entry(p, attached_entry_t, list);
	    ASSERT(ent->e_dentry);
	    if (ent->e_dentry->d_name.len == namelen  &&
	      strncmp(ent->e_dentry->d_name.name, from, namelen) == 0) {
	        if (vfs_permission(ent->e_dentry->d_inode, MAY_WRITE|MAY_EXEC)) {
		    err = -EPERM;
            	    read_unlock(&(stopd(inode->i_sb)->attachlock));
		    goto out;
	        }
		break;
            }
        }

        read_unlock(&(stopd(inode->i_sb)->attachlock));
    }



    if (!del_attached_ent(file->f_dentry->d_inode->i_sb, from, namelen)) {
	err = -ENOENT;
	goto out;
    }

    /* decrement the reference count for our superblock */
    mntput(file->f_vfsmnt);

 out:
    print_exit_status(err);
    return err;
}

/****************************************************************************
 *** MOUNT-TIME OPS							  ***
 ****************************************************************************/

super_block_t *
cryptfs_read_super(super_block_t *sb, void *raw_data, int silent)
{
    super_block_t *ret_sb = NULL;
    inode_t *inode;

    print_entry_location();

    /* XXX: not a valid test any longer, b/c we're not forcing dir=XXX
       option */
    if (!raw_data) {
	printk(KERN_WARNING "cryptfs_read_super_attach: missing data argument\n");
	goto out;
    }

    /* initialize private data */
    stopd(sb) = kmalloc(sizeof(struct cryptfs_sb_info), GFP_KERNEL);
    if (!stopd(sb)) {
	printk(KERN_WARNING "%s (attach): no memory\n", __FUNCTION__);
        goto out;
    }

    stopd(sb)->hidden_mnt = NULL;
    stopd(sb)->wsi_sb = NULL;
    stopd(sb)->attachlock = RW_LOCK_UNLOCKED;

    INIT_LIST_HEAD(&(stopd(sb)->attached));

    /* hidden_root = */ cryptfs_parse_options(sb, raw_data);

    /*
     * XXX: problem: we could attach multiple directories from file systems
     * with differnet maxbytes.  Will it work to set it to MAX_NON_LFS or
     * "infinity"?
     */
    sb->s_maxbytes = MAX_NON_LFS;

    sb->s_op = &cryptfs_sops;
    /*
     * we can't use d_alloc_root if we want to use
     * our own interpose function unchanged,
     * so we simply replicate *most* of the code in d_alloc_root here
     */
    /*
     * XXX: for the "attach" version of this function, we might be able to
     * use d_alloc_root similarly to how proc_read_super() uses it.
     */
    sb->s_root = d_alloc(NULL, &(const struct qstr) { "/", 1, 0 });
    if (IS_ERR(sb->s_root)) {
	printk(KERN_WARNING "cryptfs_read_super_attach: d_alloc failed\n");
	goto out;
    }

    /* XXX: do we need an "attach" version of cryptfs_dops? */
    sb->s_root->d_op = &cryptfs_attach_dops;
    sb->s_root->d_sb = sb;
    sb->s_root->d_parent = sb->s_root;
    /* link the upper and lower dentries */
    dtopd(sb->s_root) = NULL;

    /* XXX: copied from cryptfs_interpose (code needs to be
       split/reorganized) */
    inode = iget(sb, 1);	/* XXX: use get_empty_inode() later */
    if (!inode) {
	printk(KERN_WARNING "cryptfs_read_super_attach: iget failed\n");
	goto out_dput2;
    }
    inode->i_op = &cryptfs_dir_attach_iops; /* XXX: need "attach" version of ops? */
    inode->i_fop = &cryptfs_dir_attach_fops; /* XXX: need "attach" version of ops? */

    d_instantiate(sb->s_root, inode); /* XXX: call d_add instead? */

    /*  initialize inode fields? */
    //    fist_copy_attr_all(inode, hidden_inode);
    inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUGO;
    inode->i_blksize = 1024;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,12)
    inode->i_blkbits = 10;	/* XXX: correct? */
#endif /* linux 2.4.12 and newer */
    inode->i_nlink = 2;
    inode->i_size = PAGE_SIZE;	/* XXX: correct? */
    inode->i_blocks = PAGE_SIZE / inode->i_blksize; /* XXX: what a hack */

    fist_print_inode("cryptfs_read_super OUT", inode);

    ret_sb = sb;
    goto out;

 out_dput2:
    dput(sb->s_root);
 out:
    fist_print_sb("cryptfs_read_super OUT", sb);
    print_exit_location();
    return ret_sb;
}


/****************************************************************************
 *** SUPERBLOCK OPS							  ***
 ****************************************************************************/

STATIC int
cryptfs_statfs(super_block_t *sb, struct statfs *buf)
{
    int err = 0;
    attached_entry_t *ent;
    struct list_head *p;

    print_entry_location();

    buf->f_type = CRYPTFS_SUPER_MAGIC;
    buf->f_bsize = PAGE_SIZE / sizeof(long);
    buf->f_bfree = 0;
    buf->f_bavail = 0;
    buf->f_ffree = 0;
    buf->f_namelen = NAME_MAX;

    read_lock(&(stopd(sb)->attachlock));

    list_for_each(p, &(stopd(sb)->attached)) {
	ent = list_entry(p, attached_entry_t, list);
	printk("ENT:%*s:\n", ent->e_dentry->d_name.len, ent->e_dentry->d_name.name);
    }

    read_unlock(&(stopd(sb)->attachlock));

    print_exit_status(err);
    return err;
}


/****************************************************************************
 *** INODE OPS								  ***
 ****************************************************************************/

STATIC dentry_t *
cryptfs_lookup_attach(inode_t *dir, dentry_t *dentry)
{
    int err = -ENOENT;
    print_entry_location();

    fist_print_inode("ALi", dir);
    fist_print_dentry("ALd", dentry);

    /* XXX: add code to lookup attached nodes in this attach node.
       look if node already exists and return that instead.
     */

    // For now, fake success.
    // Returning NULL means OK, this entry "exists", even if it's yet
    // to be created via do_attach().
    err = 0;

    print_exit_status(err);

    return ERR_PTR(err);
}


STATIC int
cryptfs_permission_attach(inode_t *inode, int mask)
{
    int err;

    print_entry_location();

    // XXX: here's where cryptfs might want to check permissions (and/or keys
    // on the attach node.

   err = vfs_permission(inode, mask);
    
    print_exit_status(err);
    return err;
}


STATIC int
cryptfs_inode_revalidate_attach(dentry_t *dentry)
{
    int err = 0;
    print_entry_location();

    // XXX: do we really need ->revalidate()?
    // Maybe only if we add/del attached nodes?

    print_exit_status(err);
    return err;
}


struct inode_operations cryptfs_dir_attach_iops =
{
    lookup:	cryptfs_lookup_attach,
    permission:	cryptfs_permission_attach,
    revalidate:	cryptfs_inode_revalidate_attach,
};


/****************************************************************************
 *** DENTRY OPS								  ***
 ****************************************************************************/

struct dentry_operations cryptfs_attach_dops = {
    //    d_revalidate:	cryptfs_d_revalidate,
    //    d_hash:		cryptfs_d_hash,
    //    d_compare:		cryptfs_d_compare,
    d_release:		cryptfs_d_release,
    d_delete:		cryptfs_d_delete,
    d_iput:		cryptfs_d_iput,
};


/****************************************************************************
 *** FILE OPS								  ***
 ****************************************************************************/

STATIC int
cryptfs_readdir_attach(file_t *file, void *dirent, filldir_t filldir)
{
    int err = 1;
    unsigned int ino;
    int i;
    struct inode *inode;
    attached_entry_t *ent;
    struct list_head *p;

    print_entry_location();

    inode = file->f_dentry->d_inode;  /* CPW: Moved after print_entry */

    ino = inode->i_ino;
    i = file->f_pos;
    switch (i) {
    case 0:
	if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0) {
	    err = 0;
	    goto out;
	}
	i++;
	file->f_pos++;
	/* fall through */
    case 1:
	if (filldir(dirent, "..", 2, i,
		    file->f_dentry->d_parent->d_inode->i_ino,
		    DT_DIR) < 0) {
	    err = 0;
	    goto out;
	}
	i++;
	file->f_pos++;
	/* fall through */
    default:
        read_lock(&(stopd(inode->i_sb)->attachlock));

	i -= 2;
	printk("I: %d\n", i);
	list_for_each(p, &(stopd(inode->i_sb)->attached)) {
	    if (i <= 0)  {
	        ent = list_entry(p, attached_entry_t, list);
	        fist_print_dentry("FD", ent->e_dentry);
	        if (filldir(dirent, ent->e_dentry->d_name.name, ent->e_dentry->d_name.len, file->f_pos, ent->e_dentry->d_inode->i_ino, DT_DIR) < 0) {
	  	    err = 0;
        	    read_unlock(&(stopd(inode->i_sb)->attachlock));
		    goto outlock;
	        }
	        file->f_pos++;
	    }
	    i--;
	}

	// printk("EZK: NOTHING TO DO YET HERE... %s\n", __FUNCTION__);
    }

 outlock:
    read_unlock(&(stopd(inode->i_sb)->attachlock));
 out:
    print_exit_status(err);
    return err;
}


STATIC int
cryptfs_ioctl_attach(inode_t *inode, file_t *file, unsigned int cmd, unsigned long arg)
{
    int err = -EINVAL;		/* don't fail by default */
    int ret;
    super_block_t *sb;
    char *from = NULL, *to = NULL;
    /* These structures are 8 and 4 bytes respectively and mutually exclusive */
    union {
	    struct fist_ioctl_attach_data ad;
	    struct fist_ioctl_detach_data dd;
    } u;

    uid_t uid;
    
    print_entry_location();

    sb = inode->i_sb;

    // XXX: verify we're only attaching to the top-level node!
    // XXX: not attaching to itself
    // only attaching to directories: done in do_attach

    /* check if asked for our commands */
    switch (cmd) {

    case FIST_IOCTL_ATTACH:
	if (copy_from_user(&(u.ad), (const char *) arg, sizeof(struct fist_ioctl_attach_data))) {
	    err = -EFAULT;
	    goto out;
	}
	from = getname(u.ad.from);
	err = PTR_ERR(from);
	if (IS_ERR(from)) {
	    goto out;
	}
	to = getname(u.ad.to);
	err = PTR_ERR(to);
	if (IS_ERR(to)) {
	    goto outputfrom;
	}
        
	printk("cryptfs_ioctl_attach:%d\n",u.ad.uid);

	memcpy(&uid,&(u.ad.uid),sizeof(uid_t));
	
/*	if(uid!=current->fsuid)
	{
		printk("FIST_IOCTL_ATTACH:input uid:%d\t current uid:%d\n",uid,current->fsuid);
	}
        else
		printk("FIST_IOCTL_ATTACH:input uid equal to current uid:%d\n",current->fsuid);
*/		
	err = do_attach(inode, file, from, to);
	break;

    case FIST_IOCTL_DETACH:
	if (copy_from_user(&(u.dd), (const char *) arg, sizeof(struct fist_ioctl_detach_data))) {
	    err = -EFAULT;
	    goto out;
	}
        printk("Got structure: %p!\n", u.dd.from);
	from = getname(u.dd.from);
        printk("Got name: %p!\n", from);
	if (IS_ERR(from)) {
   	    err = PTR_ERR(from);
	    goto out;
	}
	printk("XXX: DETACH %s\n", from);
	err = do_detach(inode, file, from);
	break;

    default:
	/* by default other ioctls are not allowed on the attach node */
	break;
    }

 outputto:
    if (to)
	putname(to);
 outputfrom:
    if (from)
	putname(from);
 out:
    print_exit_status(err);
    return err;
}


struct file_operations cryptfs_dir_attach_fops =
{
    // XXX: followed model from procfs
    //    llseek:	cryptfs_llseek,
    //    read:	cryptfs_read,
    read:	generic_file_read,
    //    write:	cryptfs_write,
    readdir:	cryptfs_readdir_attach,
    //    poll:	cryptfs_poll,
    ioctl:	cryptfs_ioctl_attach,
    //    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 */
};



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