mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
159 lines
4.3 KiB
159 lines
4.3 KiB
// SPDX-License-Identifier: GPL-2.0-or-later |
|
/* |
|
* eCryptfs: Linux filesystem encryption layer |
|
* |
|
* Copyright (C) 2008 International Business Machines Corp. |
|
* Author(s): Michael A. Halcrow <[email protected]> |
|
*/ |
|
|
|
#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 path path; |
|
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); |
|
*req->lower_file = dentry_open(&req->path, |
|
(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, *tmp; |
|
|
|
mutex_lock(&ecryptfs_kthread_ctl.mux); |
|
ecryptfs_kthread_ctl.flags |= ECRYPTFS_KTHREAD_ZOMBIE; |
|
list_for_each_entry_safe(req, tmp, &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 |
|
* @cred: credential to use for this call |
|
* |
|
* This function gets a r/w file opened against 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; |
|
|
|
init_completion(&req.done); |
|
req.lower_file = lower_file; |
|
req.path.dentry = lower_dentry; |
|
req.path.mnt = lower_mnt; |
|
|
|
/* Corresponding dput() and mntput() are done when the |
|
* lower file is fput() when all eCryptfs files for the inode are |
|
* released. */ |
|
flags |= IS_RDONLY(d_inode(lower_dentry)) ? O_RDONLY : O_RDWR; |
|
(*lower_file) = dentry_open(&req.path, flags, cred); |
|
if (!IS_ERR(*lower_file)) |
|
goto out; |
|
if ((flags & O_ACCMODE) == O_RDONLY) { |
|
rc = PTR_ERR((*lower_file)); |
|
goto out; |
|
} |
|
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; |
|
}
|
|
|