From f4439cff7f6be6a20d7699465a5e3faa005e407e Mon Sep 17 00:00:00 2001 From: zhang-daiyue Date: Sat, 23 Jul 2022 20:26:00 +0800 Subject: [PATCH] Add redactionfs support Signed-off-by: zhang-daiyue Change-Id: Ic8f9cb2c0ba36599092251cab3896ad5c162656a --- fs/Kconfig | 1 + fs/Makefile | 1 + fs/redactionfs/Kconfig | 12 ++ fs/redactionfs/Makefile | 3 + fs/redactionfs/dentry.c | 23 ++++ fs/redactionfs/dir.c | 18 +++ fs/redactionfs/file.c | 252 +++++++++++++++++++++++++++++++++++++ fs/redactionfs/inode.c | 96 ++++++++++++++ fs/redactionfs/internal.h | 40 ++++++ fs/redactionfs/main.c | 43 +++++++ fs/redactionfs/redaction.h | 31 +++++ fs/redactionfs/super.c | 116 +++++++++++++++++ 12 files changed, 636 insertions(+) create mode 100644 fs/redactionfs/Kconfig create mode 100644 fs/redactionfs/Makefile create mode 100644 fs/redactionfs/dentry.c create mode 100644 fs/redactionfs/dir.c create mode 100644 fs/redactionfs/file.c create mode 100644 fs/redactionfs/inode.c create mode 100644 fs/redactionfs/internal.h create mode 100644 fs/redactionfs/main.c create mode 100644 fs/redactionfs/redaction.h create mode 100644 fs/redactionfs/super.c diff --git a/fs/Kconfig b/fs/Kconfig index b95f212be39e..20dcd2d677b3 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -346,6 +346,7 @@ endif # NETWORK_FILESYSTEMS source "fs/nls/Kconfig" source "fs/dlm/Kconfig" source "fs/unicode/Kconfig" +source "fs/redactionfs/Kconfig" config IO_WQ bool diff --git a/fs/Makefile b/fs/Makefile index d71954aaba20..f00c38ca6237 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -137,3 +137,4 @@ obj-$(CONFIG_EFIVAR_FS) += efivarfs/ obj-$(CONFIG_EROFS_FS) += erofs/ obj-$(CONFIG_VBOXSF_FS) += vboxsf/ obj-$(CONFIG_ZONEFS_FS) += zonefs/ +obj-$(CONFIG_REDACTION_FS) += redactionfs/ diff --git a/fs/redactionfs/Kconfig b/fs/redactionfs/Kconfig new file mode 100644 index 000000000000..f75e11e59436 --- /dev/null +++ b/fs/redactionfs/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +config REDACTION_FS + tristate "Redaction File System support" + depends on TMPFS + help + Redaction fs support. If unsure, say N. + +config REDACTION_FS_DEBUG + tristate "Debug message of Redaction File System" + depends on REDACTION_FS + help + Redaction fs debug support. diff --git a/fs/redactionfs/Makefile b/fs/redactionfs/Makefile new file mode 100644 index 000000000000..d3669862a128 --- /dev/null +++ b/fs/redactionfs/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_REDACTION_FS) += redaction.o +redaction-y := main.o super.o dentry.o inode.o file.o dir.o diff --git a/fs/redactionfs/dentry.c b/fs/redactionfs/dentry.c new file mode 100644 index 000000000000..ec442c34d0c3 --- /dev/null +++ b/fs/redactionfs/dentry.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * fs/redactionfs/main.c + * + * Copyright (c) 2022 Huawei Technologies Co., Ltd. + * Author: weilongping@huawei.com + * Create: 2022-06-10 + */ +#include "internal.h" + +static int redaction_d_revalidate(struct dentry *dentry, unsigned int flags) +{ + return 1; +} + +static void redaction_d_release(struct dentry *dentry) +{ +} + +struct dentry_operations redaction_dops = { + .d_revalidate = redaction_d_revalidate, + .d_release = redaction_d_release, +}; diff --git a/fs/redactionfs/dir.c b/fs/redactionfs/dir.c new file mode 100644 index 000000000000..26e86cd22312 --- /dev/null +++ b/fs/redactionfs/dir.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * fs/redactionfs/dir.c + * + * Copyright (c) 2022 Huawei Technologies Co., Ltd. + * Author: weilongping@huawei.com + * Create: 2022-06-10 + */ +#include + +#include "internal.h" + +static int redaction_iterate(struct file *file, struct dir_context *ctx) +{ + return 0; +} + +struct file_operations redaction_dir_fops = { .iterate = redaction_iterate }; diff --git a/fs/redactionfs/file.c b/fs/redactionfs/file.c new file mode 100644 index 000000000000..1d5b76636e43 --- /dev/null +++ b/fs/redactionfs/file.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * fs/redactionfs/file.c + * + * Copyright (c) 2022 Huawei Technologies Co., Ltd. + * Author: weilongping@huawei.com + * Create: 2022-06-10 + */ +#include +#include +#include +#include +#include + +#include "internal.h" + +long redaction_set_origin_fd(struct file *file, unsigned long arg) +{ + int fd; + struct file *origin_file; + struct inode *inode = file->f_inode; + struct redaction_inode_info *info = redaction_inode_to_private(inode); + int ret = 0; + if (copy_from_user(&fd, (int *)arg, sizeof(fd))) + return -EFAULT; + if (IS_ENABLED(CONFIG_REDACTION_FS_DEBUG)) + pr_debug("redaction_set_origin_fd %d", fd); + origin_file = fget(fd); + if (!origin_file) + return -EBADF; + mutex_lock(&info->lock); + if (info->origin_file) { + // origin_file had been set. + ret = -EEXIST; + fput(origin_file); + } else if (file_inode(origin_file) == inode) { + pr_err("Could not set itself as origin_file!"); + fput(origin_file); + return -EINVAL; + } else { + info->origin_file = origin_file; + fsstack_copy_attr_all(inode, file_inode(origin_file)); + fsstack_copy_inode_size(inode, file_inode(origin_file)); + } + mutex_unlock(&info->lock); + return ret; +} + +int check_range(struct redaction_range *range) +{ + __u64 index; + if (range->range[0].begin >= range->range[0].end) { + return -EINVAL; + } + for (index = 1; index < range->num; index++) { + if ((range->range[index].begin >= range->range[index].end) || + (range->range[index].begin < range->range[index - 1].end)) { + return -EINVAL; + } + } + if (IS_ENABLED(CONFIG_REDACTION_FS_DEBUG)) { + pr_debug("redaction_range recv %llu ranges:", range->num); + for (index = 0; index < range->num; index++) { + pr_debug("range:[%llu %llu])", + range->range[index].begin, + range->range[index].end); + } + pr_debug("\n"); + } + return 0; +} + +long redaction_set_redaction_range(struct file *file, unsigned long arg) +{ + struct inode *inode = file->f_inode; + struct redaction_inode_info *info = redaction_inode_to_private(inode); + int ret = 0; + struct redaction_range *range; + struct redaction_range header; + + mutex_lock(&info->lock); + if (!info->origin_file) { + ret = -EBADF; + goto out; + } + + if (copy_from_user(&header, (struct redaction_range *)arg, + sizeof(header))) { + ret = -EFAULT; + pr_err("redaction_set_redaction_range: get header failed!"); + goto out; + } + + if (header.num > REDACTION_MAX_RANGES || header.num == 0) { + ret = -EINVAL; + pr_err("redaction_set_redaction_range: illegal num!"); + goto out; + } + + range = kzalloc(sizeof(header) + sizeof(header.range[0]) * header.num, + GFP_KERNEL); + if (!range) { + ret = -ENOMEM; + goto out; + } + + if (copy_from_user(range, (struct redaction_range *)arg, + sizeof(header) + + sizeof(header.range[0]) * header.num)) { + ret = -EFAULT; + pr_err("Failed to get range from user! num=%llu", header.num); + kfree(range); + goto out; + } + + ret = check_range(range); + if (ret) { + kfree(range); + goto out; + } + + info->range = range; +out: + mutex_unlock(&info->lock); + return ret; +} + +static long __redaction_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + long rc = -ENOTTY; + switch (cmd) { + case IOC_SET_ORIGIN_FD: + rc = redaction_set_origin_fd(file, arg); + pr_info("Exit IOC_SET_ORIGIN_FD, ret: %ld", rc); + return rc; + case IOC_SET_REDACTION_RANGE: + rc = redaction_set_redaction_range(file, arg); + pr_info("Exit IOC_SET_ORIGIN_FD, ret: %ld", rc); + return rc; + default: + pr_info("Exit redaction_unlocked_ioctl, ret: %ld", rc); + return rc; + } +} + +static long redaction_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return __redaction_ioctl(file, cmd, arg); +} + +static long redaction_unlocked_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return __redaction_ioctl(file, cmd, arg); +} + +static ssize_t redaction_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) +{ + struct inode *inode = file_inode(file); + struct redaction_inode_info *info = redaction_inode_to_private(inode); + struct file *origin_file; + struct redaction_range *range = info->range; + ssize_t ret = 0; + loff_t pos = *ppos; + loff_t file_size; + int current_range_index = 0; + mutex_lock(&info->lock); + origin_file = info->origin_file; + mutex_unlock(&info->lock); + if (!origin_file) { + ret = -ENOENT; + goto out; + } + + // Reduce count when it will read over file size. + file_size = i_size_read(file_inode(origin_file)); + if (IS_ENABLED(CONFIG_REDACTION_FS_DEBUG)) + if (count > (file_size - pos)) + pr_debug( + "redaction_read:count will be truncated to %llu, as file_size=%llu,pos=%llu", + file_size - pos, file_size, pos); + count = count <= (file_size - pos) ? count : (file_size - pos); + + // Skip ranges before pos. + while ((range->range[current_range_index].end <= pos) && + (current_range_index < range->num)) + current_range_index++; + + while (count > 0) { + __u64 current_begin, current_end; + if (current_range_index >= range->num) { + // read directly when redaction range gone; + if (IS_ENABLED(CONFIG_REDACTION_FS_DEBUG)) + pr_debug( + "redaction_read:read from %llu with len %lu at the end.", + pos, count); + ret = vfs_read(origin_file, buf, count, &pos); + break; + } + current_begin = range->range[current_range_index].begin; + current_end = range->range[current_range_index].end; + if (current_begin <= pos) { + // Clear user memory + unsigned long clear_len = current_end - pos; + clear_len = clear_len < count ? clear_len : count; + if (IS_ENABLED(CONFIG_REDACTION_FS_DEBUG)) + pr_debug( + "redaction_read:clear user memory from %llu with len %lu", + pos, clear_len); + if (clear_user(buf, clear_len)) { + ret = EFAULT; + break; + } + buf += clear_len; + pos += clear_len; + count -= clear_len; + current_range_index++; + } else { + // Read from pos to (next)current_begin + unsigned long read_len = current_begin - pos; + read_len = read_len < count ? read_len : count; + if (IS_ENABLED(CONFIG_REDACTION_FS_DEBUG)) + pr_debug( + "redaction_read:read from %llu with len %lu", + pos, read_len); + ret = vfs_read(origin_file, buf, read_len, &pos); + if (ret < 0 || ret < read_len) { + // Could not read enough bytes; + break; + } + buf += ret; + count -= ret; + } + } + + if (ret >= 0) { + ret = pos - *ppos; + *ppos = pos; + } +out: + return ret; +} + +struct file_operations redaction_file_fops = { + .unlocked_ioctl = redaction_unlocked_ioctl, + .compat_ioctl = redaction_compat_ioctl, + .read = redaction_read, + .llseek = generic_file_llseek, +}; diff --git a/fs/redactionfs/inode.c b/fs/redactionfs/inode.c new file mode 100644 index 000000000000..91f21a494bc7 --- /dev/null +++ b/fs/redactionfs/inode.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * fs/redactionfs/inode.c + * + * Copyright (c) 2022 Huawei Technologies Co., Ltd. + * Author: weilongping@huawei.com + * Create: 2022-06-10 + */ +#include +#include +#include + +#include "internal.h" + +struct dentry *redaction_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags) +{ + return ERR_PTR(-ENOENT); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) +static int redaction_tmpfile(struct user_namespace *, struct inode *dir, + struct dentry *dentry, umode_t mode) +#else +static int redaction_tmpfile(struct inode *dir, struct dentry *dentry, + umode_t mode) +#endif +{ + struct inode *inode = redaction_iget(dir->i_sb, false); + d_tmpfile(dentry, inode); + if (IS_ENABLED(CONFIG_REDACTION_FS_DEBUG)) + pr_debug("redaction: tmpfile %p", inode); + return 0; +} + +struct inode_operations redaction_dir_iops = { + .tmpfile = redaction_tmpfile, + .lookup = redaction_lookup, +}; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) +static int redaction_getattr(struct user_namespace *mnt_userns, + const struct path *path, struct kstat *stat, + u32 request_mask, unsigned int flags) +#else +static int redaction_getattr(const struct path *path, struct kstat *stat, + u32 request_mask, unsigned int flags) +#endif +{ + struct dentry *dentry = path->dentry; + struct inode *inode = d_inode(dentry); + struct redaction_inode_info *info = redaction_inode_to_private(inode); + struct file *origin_file; + struct kstat origin_stat; + int ret; + mutex_lock(&info->lock); + origin_file = info->origin_file; + mutex_unlock(&info->lock); + if (!origin_file) { + return -ENOENT; + } + ret = vfs_getattr(&(origin_file->f_path), &origin_stat, request_mask, + flags); + if (!ret) { + fsstack_copy_attr_all(inode, file_inode(origin_file)); + fsstack_copy_inode_size(inode, file_inode(origin_file)); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(mnt_userns, d_inode(dentry), stat); +#else + generic_fillattr(d_inode(dentry), stat); +#endif + stat->blocks = origin_stat.blocks; + } + + return ret; +} + +struct inode_operations redaction_file_iops = { + // .listxattr = redaction_listxattr, + .getattr = redaction_getattr, +}; + +struct inode *redaction_iget(struct super_block *sb, bool is_dir) +{ + struct inode *inode = new_inode(sb); + if (is_dir) { + inode->i_op = &redaction_dir_iops; + inode->i_fop = &redaction_dir_fops; + inode->i_mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO; + } else { + inode->i_op = &redaction_file_iops; + inode->i_fop = &redaction_file_fops; + inode->i_mode = S_IFREG; + } + return inode; +} diff --git a/fs/redactionfs/internal.h b/fs/redactionfs/internal.h new file mode 100644 index 000000000000..7feb7cf2dde0 --- /dev/null +++ b/fs/redactionfs/internal.h @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * fs/redactionfs/internal.h + * + * Copyright (c) 2022 Huawei Technologies Co., Ltd. + * Author: weilongping@huawei.com + * Create: 2022-06-10 + */ +#ifndef __FS_REDACTIONFS_INTERNAL_H__ +#define __FS_REDACTIONFS_INTERNAL_H__ + +#include +#include +#include + +#include "redaction.h" + +#define REDACTION_SUPER_MAGIC 0x20220607 + +struct redaction_inode_info { + struct inode vfs_inode; + struct file *origin_file; + struct redaction_range *range; + struct mutex lock; +}; + +static inline struct redaction_inode_info * +redaction_inode_to_private(struct inode *inode) +{ + return container_of(inode, struct redaction_inode_info, vfs_inode); +} + +struct inode *redaction_iget(struct super_block *sb, bool is_dir); +extern struct dentry_operations redaction_dops; +extern struct file_operations redaction_dir_fops; +extern struct file_operations redaction_file_fops; +extern struct file_system_type redaction_fs_type; +extern struct kmem_cache *redaction_inode_cachep; + +#endif // __FS_REDACTIONFS_INTERNAL_H__ diff --git a/fs/redactionfs/main.c b/fs/redactionfs/main.c new file mode 100644 index 000000000000..aebeeba2048d --- /dev/null +++ b/fs/redactionfs/main.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * fs/redactionfs/main.c + * + * Copyright (c) 2022 Huawei Technologies Co., Ltd. + * Author: weilongping@huawei.com + * Create: 2022-06-10 + */ +#include +#include +#include + +#include "internal.h" + +struct kmem_cache *redaction_inode_cachep = NULL; + +static int __init redaction_init(void) +{ + int ret; + redaction_inode_cachep = + kmem_cache_create("redaction_inode_cache", + sizeof(struct redaction_inode_info), 0, 0, + NULL); + if (!redaction_inode_cachep) + return -ENOMEM; + ret = register_filesystem(&redaction_fs_type); + if (ret) + kmem_cache_destroy(redaction_inode_cachep); + return ret; +} + +static void __exit redaction_exit(void) +{ + unregister_filesystem(&redaction_fs_type); + kmem_cache_destroy(redaction_inode_cachep); +} + +module_init(redaction_init) module_exit(redaction_exit) + + MODULE_DESCRIPTION("Redaction File System for Open Harmony"); +MODULE_AUTHOR("LongPing Wei weilongping@huawei.com"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS_FS("redaction"); diff --git a/fs/redactionfs/redaction.h b/fs/redactionfs/redaction.h new file mode 100644 index 000000000000..e074882d472a --- /dev/null +++ b/fs/redactionfs/redaction.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * fs/redactionfs/redaction.h + * + * Copyright (c) 2022 Huawei Technologies Co., Ltd. + * Author: weilongping@huawei.com + * Create: 2022-06-10 + */ +#ifndef __FS_REDACTIONFS_H__ +#define __FS_REDACTIONFS_H__ + +#include +#include + +#define REDACTION_MAX_RANGES 127 + +struct __attribute__((__packed__)) redaction_range { + __u64 num; + __u64 reserved; + struct { + __u64 begin; + __u64 end; + } range[0]; +}; + +#define REDACTION_IOCTL_MAGIC 0x71 +#define IOC_SET_ORIGIN_FD _IOW(REDACTION_IOCTL_MAGIC, 1, __s32) +#define IOC_SET_REDACTION_RANGE \ + _IOW(REDACTION_IOCTL_MAGIC, 2, struct redaction_range) + +#endif // __FS_REDACTIONFS_H__ diff --git a/fs/redactionfs/super.c b/fs/redactionfs/super.c new file mode 100644 index 000000000000..bbe95474fa28 --- /dev/null +++ b/fs/redactionfs/super.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * fs/redactionfs/super.c + * + * Copyright (c) 2022 Huawei Technologies Co., Ltd. + * Author: weilongping@huawei.com + * Create: 2022-06-10 + */ +#include +#include +#include +#include + +#include "internal.h" + +static struct inode *redaction_alloc_inode(struct super_block *sb) +{ + struct redaction_inode_info *info = + kmem_cache_zalloc(redaction_inode_cachep, GFP_KERNEL); + if (IS_ENABLED(CONFIG_REDACTION_FS_DEBUG)) + pr_debug("redaction:redaction_alloc_inode %p", info); + inode_init_once(&info->vfs_inode); + mutex_init(&info->lock); + return &info->vfs_inode; +} + +// Free redaction_inode_info +static void redaction_free_inode(struct inode *inode) +{ + if (IS_ENABLED(CONFIG_REDACTION_FS_DEBUG)) + pr_debug("redaction:free_inode %p", inode); + kmem_cache_free(redaction_inode_cachep, + redaction_inode_to_private(inode)); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0) +static void i_callback(struct rcu_head *head) +{ + struct inode *inode = container_of(head, struct inode, i_rcu); + redaction_free_inode(inode); +} +#endif + +// Destory redaction_range +static void redaction_destroy_inode(struct inode *inode) +{ + struct redaction_inode_info *info = redaction_inode_to_private(inode); + if (info->range) { + kfree(info->range); + info->range = NULL; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0) + call_rcu(&inode->i_rcu, i_callback); +#endif +} + +// Clear vfs_inode +static void redaction_evict_inode(struct inode *inode) +{ + struct redaction_inode_info *info = redaction_inode_to_private(inode); + clear_inode(inode); + if (info->origin_file) { + fput(info->origin_file); + info->origin_file = NULL; + } +} + +struct super_operations redaction_sops = { + .alloc_inode = redaction_alloc_inode, + .destroy_inode = redaction_destroy_inode, +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0) + .free_inode = redaction_free_inode, +#endif + .evict_inode = redaction_evict_inode, +}; + +struct dentry *redaction_mount(struct file_system_type *fs_type, int flags, + const char *dev_name, void *raw_data) +{ + struct super_block *s; + struct inode *inode; + int ret = 0; + s = sget(fs_type, NULL, set_anon_super, flags, 0); + if (IS_ERR(s)) { + ret = PTR_ERR(s); + goto out; + } + s->s_op = &redaction_sops; + s->s_d_op = &redaction_dops; + s->s_magic = REDACTION_SUPER_MAGIC; + inode = redaction_iget(s, true /* dir */); + if (!inode) { + ret = -ENOMEM; + goto out1; + } + + s->s_root = d_make_root(inode); + + return dget(s->s_root); +out1: + deactivate_locked_super(s); +out: + return ERR_PTR(ret); +} + +void redaction_kill_sb(struct super_block *sb) +{ + kill_anon_super(sb); +} + +struct file_system_type redaction_fs_type = { + .owner = THIS_MODULE, + .name = "redaction", + .mount = redaction_mount, + .kill_sb = redaction_kill_sb, +}; -- Gitee