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: */