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