| /** |
| * eCryptfs: Linux filesystem encryption layer |
| * |
| * Copyright (C) 2008 International Business Machines Corp. |
| * Author(s): Michael A. Halcrow <mahalcro@us.ibm.com> |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; either version 2 of the |
| * License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |
| * 02111-1307, USA. |
| */ |
| |
| #include <linux/kthread.h> |
| #include <linux/freezer.h> |
| #include <linux/slab.h> |
| #include <linux/wait.h> |
| #include <linux/mount.h> |
| #include "ecryptfs_kernel.h" |
| |
| struct ecryptfs_open_req { |
| struct file **lower_file; |
| struct dentry *lower_dentry; |
| struct vfsmount *lower_mnt; |
| struct completion done; |
| struct list_head kthread_ctl_list; |
| }; |
| |
| static struct ecryptfs_kthread_ctl { |
| #define ECRYPTFS_KTHREAD_ZOMBIE 0x00000001 |
| u32 flags; |
| struct mutex mux; |
| struct list_head req_list; |
| wait_queue_head_t wait; |
| } ecryptfs_kthread_ctl; |
| |
| static struct task_struct *ecryptfs_kthread; |
| |
| /** |
| * ecryptfs_threadfn |
| * @ignored: ignored |
| * |
| * The eCryptfs kernel thread that has the responsibility of getting |
| * the lower file with RW permissions. |
| * |
| * Returns zero on success; non-zero otherwise |
| */ |
| static int ecryptfs_threadfn(void *ignored) |
| { |
| set_freezable(); |
| while (1) { |
| struct ecryptfs_open_req *req; |
| |
| wait_event_freezable( |
| ecryptfs_kthread_ctl.wait, |
| (!list_empty(&ecryptfs_kthread_ctl.req_list) |
| || kthread_should_stop())); |
| mutex_lock(&ecryptfs_kthread_ctl.mux); |
| if (ecryptfs_kthread_ctl.flags & ECRYPTFS_KTHREAD_ZOMBIE) { |
| mutex_unlock(&ecryptfs_kthread_ctl.mux); |
| goto out; |
| } |
| while (!list_empty(&ecryptfs_kthread_ctl.req_list)) { |
| req = list_first_entry(&ecryptfs_kthread_ctl.req_list, |
| struct ecryptfs_open_req, |
| kthread_ctl_list); |
| list_del(&req->kthread_ctl_list); |
| dget(req->lower_dentry); |
| mntget(req->lower_mnt); |
| (*req->lower_file) = dentry_open( |
| req->lower_dentry, req->lower_mnt, |
| (O_RDWR | O_LARGEFILE), current_cred()); |
| complete(&req->done); |
| } |
| mutex_unlock(&ecryptfs_kthread_ctl.mux); |
| } |
| out: |
| return 0; |
| } |
| |
| int __init ecryptfs_init_kthread(void) |
| { |
| int rc = 0; |
| |
| mutex_init(&ecryptfs_kthread_ctl.mux); |
| init_waitqueue_head(&ecryptfs_kthread_ctl.wait); |
| INIT_LIST_HEAD(&ecryptfs_kthread_ctl.req_list); |
| ecryptfs_kthread = kthread_run(&ecryptfs_threadfn, NULL, |
| "ecryptfs-kthread"); |
| if (IS_ERR(ecryptfs_kthread)) { |
| rc = PTR_ERR(ecryptfs_kthread); |
| printk(KERN_ERR "%s: Failed to create kernel thread; rc = [%d]" |
| "\n", __func__, rc); |
| } |
| return rc; |
| } |
| |
| void ecryptfs_destroy_kthread(void) |
| { |
| struct ecryptfs_open_req *req; |
| |
| mutex_lock(&ecryptfs_kthread_ctl.mux); |
| ecryptfs_kthread_ctl.flags |= ECRYPTFS_KTHREAD_ZOMBIE; |
| list_for_each_entry(req, &ecryptfs_kthread_ctl.req_list, |
| kthread_ctl_list) { |
| list_del(&req->kthread_ctl_list); |
| *req->lower_file = ERR_PTR(-EIO); |
| complete(&req->done); |
| } |
| mutex_unlock(&ecryptfs_kthread_ctl.mux); |
| kthread_stop(ecryptfs_kthread); |
| wake_up(&ecryptfs_kthread_ctl.wait); |
| } |
| |
| /** |
| * ecryptfs_privileged_open |
| * @lower_file: Result of dentry_open by root on lower dentry |
| * @lower_dentry: Lower dentry for file to open |
| * @lower_mnt: Lower vfsmount for file to open |
| * |
| * This function gets a r/w file opened againt the lower dentry. |
| * |
| * Returns zero on success; non-zero otherwise |
| */ |
| int ecryptfs_privileged_open(struct file **lower_file, |
| struct dentry *lower_dentry, |
| struct vfsmount *lower_mnt, |
| const struct cred *cred) |
| { |
| struct ecryptfs_open_req req; |
| int flags = O_LARGEFILE; |
| int rc = 0; |
| |
| /* Corresponding dput() and mntput() are done when the |
| * lower file is fput() when all eCryptfs files for the inode are |
| * released. */ |
| dget(lower_dentry); |
| mntget(lower_mnt); |
| flags |= IS_RDONLY(lower_dentry->d_inode) ? O_RDONLY : O_RDWR; |
| (*lower_file) = dentry_open(lower_dentry, lower_mnt, flags, cred); |
| if (!IS_ERR(*lower_file)) |
| goto out; |
| if ((flags & O_ACCMODE) == O_RDONLY) { |
| rc = PTR_ERR((*lower_file)); |
| goto out; |
| } |
| init_completion(&req.done); |
| req.lower_file = lower_file; |
| req.lower_dentry = lower_dentry; |
| req.lower_mnt = lower_mnt; |
| mutex_lock(&ecryptfs_kthread_ctl.mux); |
| if (ecryptfs_kthread_ctl.flags & ECRYPTFS_KTHREAD_ZOMBIE) { |
| rc = -EIO; |
| mutex_unlock(&ecryptfs_kthread_ctl.mux); |
| printk(KERN_ERR "%s: We are in the middle of shutting down; " |
| "aborting privileged request to open lower file\n", |
| __func__); |
| goto out; |
| } |
| list_add_tail(&req.kthread_ctl_list, &ecryptfs_kthread_ctl.req_list); |
| mutex_unlock(&ecryptfs_kthread_ctl.mux); |
| wake_up(&ecryptfs_kthread_ctl.wait); |
| wait_for_completion(&req.done); |
| if (IS_ERR(*lower_file)) |
| rc = PTR_ERR(*lower_file); |
| out: |
| return rc; |
| } |