blob: d8c3a373aafa40547090427c0ae1e96b73f49dbe [file] [log] [blame]
Michael Halcrow746f1e52008-07-23 21:30:02 -07001/**
2 * eCryptfs: Linux filesystem encryption layer
3 *
4 * Copyright (C) 2008 International Business Machines Corp.
5 * Author(s): Michael A. Halcrow <mahalcro@us.ibm.com>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 * 02111-1307, USA.
21 */
22
23#include <linux/kthread.h>
24#include <linux/freezer.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090025#include <linux/slab.h>
Michael Halcrow746f1e52008-07-23 21:30:02 -070026#include <linux/wait.h>
27#include <linux/mount.h>
28#include "ecryptfs_kernel.h"
29
30struct kmem_cache *ecryptfs_open_req_cache;
31
32static struct ecryptfs_kthread_ctl {
33#define ECRYPTFS_KTHREAD_ZOMBIE 0x00000001
34 u32 flags;
35 struct mutex mux;
36 struct list_head req_list;
37 wait_queue_head_t wait;
38} ecryptfs_kthread_ctl;
39
40static struct task_struct *ecryptfs_kthread;
41
42/**
43 * ecryptfs_threadfn
44 * @ignored: ignored
45 *
46 * The eCryptfs kernel thread that has the responsibility of getting
47 * the lower persistent file with RW permissions.
48 *
49 * Returns zero on success; non-zero otherwise
50 */
51static int ecryptfs_threadfn(void *ignored)
52{
53 set_freezable();
54 while (1) {
55 struct ecryptfs_open_req *req;
56
57 wait_event_freezable(
58 ecryptfs_kthread_ctl.wait,
59 (!list_empty(&ecryptfs_kthread_ctl.req_list)
60 || kthread_should_stop()));
61 mutex_lock(&ecryptfs_kthread_ctl.mux);
62 if (ecryptfs_kthread_ctl.flags & ECRYPTFS_KTHREAD_ZOMBIE) {
63 mutex_unlock(&ecryptfs_kthread_ctl.mux);
64 goto out;
65 }
66 while (!list_empty(&ecryptfs_kthread_ctl.req_list)) {
67 req = list_first_entry(&ecryptfs_kthread_ctl.req_list,
68 struct ecryptfs_open_req,
69 kthread_ctl_list);
70 mutex_lock(&req->mux);
71 list_del(&req->kthread_ctl_list);
72 if (!(req->flags & ECRYPTFS_REQ_ZOMBIE)) {
73 dget(req->lower_dentry);
74 mntget(req->lower_mnt);
75 (*req->lower_file) = dentry_open(
76 req->lower_dentry, req->lower_mnt,
David Howells745ca242008-11-14 10:39:22 +110077 (O_RDWR | O_LARGEFILE), current_cred());
Michael Halcrow746f1e52008-07-23 21:30:02 -070078 req->flags |= ECRYPTFS_REQ_PROCESSED;
79 }
80 wake_up(&req->wait);
81 mutex_unlock(&req->mux);
82 }
83 mutex_unlock(&ecryptfs_kthread_ctl.mux);
84 }
85out:
86 return 0;
87}
88
89int ecryptfs_init_kthread(void)
90{
91 int rc = 0;
92
93 mutex_init(&ecryptfs_kthread_ctl.mux);
94 init_waitqueue_head(&ecryptfs_kthread_ctl.wait);
95 INIT_LIST_HEAD(&ecryptfs_kthread_ctl.req_list);
96 ecryptfs_kthread = kthread_run(&ecryptfs_threadfn, NULL,
97 "ecryptfs-kthread");
98 if (IS_ERR(ecryptfs_kthread)) {
99 rc = PTR_ERR(ecryptfs_kthread);
100 printk(KERN_ERR "%s: Failed to create kernel thread; rc = [%d]"
101 "\n", __func__, rc);
102 }
103 return rc;
104}
105
106void ecryptfs_destroy_kthread(void)
107{
108 struct ecryptfs_open_req *req;
109
110 mutex_lock(&ecryptfs_kthread_ctl.mux);
111 ecryptfs_kthread_ctl.flags |= ECRYPTFS_KTHREAD_ZOMBIE;
112 list_for_each_entry(req, &ecryptfs_kthread_ctl.req_list,
113 kthread_ctl_list) {
114 mutex_lock(&req->mux);
115 req->flags |= ECRYPTFS_REQ_ZOMBIE;
116 wake_up(&req->wait);
117 mutex_unlock(&req->mux);
118 }
119 mutex_unlock(&ecryptfs_kthread_ctl.mux);
120 kthread_stop(ecryptfs_kthread);
121 wake_up(&ecryptfs_kthread_ctl.wait);
122}
123
124/**
125 * ecryptfs_privileged_open
126 * @lower_file: Result of dentry_open by root on lower dentry
127 * @lower_dentry: Lower dentry for file to open
128 * @lower_mnt: Lower vfsmount for file to open
129 *
130 * This function gets a r/w file opened againt the lower dentry.
131 *
132 * Returns zero on success; non-zero otherwise
133 */
134int ecryptfs_privileged_open(struct file **lower_file,
135 struct dentry *lower_dentry,
David Howells745ca242008-11-14 10:39:22 +1100136 struct vfsmount *lower_mnt,
137 const struct cred *cred)
Michael Halcrow746f1e52008-07-23 21:30:02 -0700138{
139 struct ecryptfs_open_req *req;
Tyler Hicksac22ba22009-08-12 01:06:54 -0500140 int flags = O_LARGEFILE;
Michael Halcrow746f1e52008-07-23 21:30:02 -0700141 int rc = 0;
142
143 /* Corresponding dput() and mntput() are done when the
144 * persistent file is fput() when the eCryptfs inode is
145 * destroyed. */
146 dget(lower_dentry);
147 mntget(lower_mnt);
Tyler Hicksac22ba22009-08-12 01:06:54 -0500148 flags |= IS_RDONLY(lower_dentry->d_inode) ? O_RDONLY : O_RDWR;
149 (*lower_file) = dentry_open(lower_dentry, lower_mnt, flags, cred);
Michael Halcrow746f1e52008-07-23 21:30:02 -0700150 if (!IS_ERR(*lower_file))
151 goto out;
Tyler Hicksac22ba22009-08-12 01:06:54 -0500152 if (flags & O_RDONLY) {
153 rc = PTR_ERR((*lower_file));
154 goto out;
155 }
Michael Halcrow746f1e52008-07-23 21:30:02 -0700156 req = kmem_cache_alloc(ecryptfs_open_req_cache, GFP_KERNEL);
157 if (!req) {
158 rc = -ENOMEM;
159 goto out;
160 }
161 mutex_init(&req->mux);
162 req->lower_file = lower_file;
163 req->lower_dentry = lower_dentry;
164 req->lower_mnt = lower_mnt;
165 init_waitqueue_head(&req->wait);
166 req->flags = 0;
167 mutex_lock(&ecryptfs_kthread_ctl.mux);
168 if (ecryptfs_kthread_ctl.flags & ECRYPTFS_KTHREAD_ZOMBIE) {
169 rc = -EIO;
170 mutex_unlock(&ecryptfs_kthread_ctl.mux);
171 printk(KERN_ERR "%s: We are in the middle of shutting down; "
172 "aborting privileged request to open lower file\n",
173 __func__);
174 goto out_free;
175 }
176 list_add_tail(&req->kthread_ctl_list, &ecryptfs_kthread_ctl.req_list);
177 mutex_unlock(&ecryptfs_kthread_ctl.mux);
178 wake_up(&ecryptfs_kthread_ctl.wait);
179 wait_event(req->wait, (req->flags != 0));
180 mutex_lock(&req->mux);
181 BUG_ON(req->flags == 0);
182 if (req->flags & ECRYPTFS_REQ_DROPPED
183 || req->flags & ECRYPTFS_REQ_ZOMBIE) {
184 rc = -EIO;
185 printk(KERN_WARNING "%s: Privileged open request dropped\n",
186 __func__);
187 goto out_unlock;
188 }
Tyler Hicksac22ba22009-08-12 01:06:54 -0500189 if (IS_ERR(*req->lower_file))
Michael Halcrow746f1e52008-07-23 21:30:02 -0700190 rc = PTR_ERR(*req->lower_file);
Michael Halcrow746f1e52008-07-23 21:30:02 -0700191out_unlock:
192 mutex_unlock(&req->mux);
193out_free:
194 kmem_cache_free(ecryptfs_open_req_cache, req);
195out:
196 return rc;
197}