Files
android_kernel_xiaomi_sm8450/fs/sdcardfs/main.c
Eric Biggers 2b6ff0224c ANDROID: sdcardfs: evict dentries on fscrypt key removal
Use the fscrypt key removal notifier chain to make sdcardfs evict its
dentries when an fscrypt key is about to be removed.  This is needed for
the FS_IOC_REMOVE_ENCRYPTION_KEY ioctl to properly "lock" the encrypted
files underneath sdcardfs when an Android user is stopped.

Test: pm create-user 10
      am start-user 10
      find /data/media/10/    # filenames are in plaintext form
      am stop-user 10
      find /data/media/10/    # filenames are in ciphertext form

      (But currently the kernel and vold still warn about other files
      still being open, due to b/140762419)

Bug: 120446149
Bug: 142275883
Change-Id: I83b451a2bc40c72fcd01d24aa5c34ad8de427534
Signed-off-by: Eric Biggers <ebiggers@google.com>
2019-10-23 21:19:54 +00:00

463 lines
12 KiB
C

/*
* fs/sdcardfs/main.c
*
* Copyright (c) 2013 Samsung Electronics Co. Ltd
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
* Sunghwan Yun, Sungjong Seo
*
* This program has been developed as a stackable file system based on
* the WrapFS which written by
*
* Copyright (c) 1998-2011 Erez Zadok
* Copyright (c) 2009 Shrikar Archak
* Copyright (c) 2003-2011 Stony Brook University
* Copyright (c) 2003-2011 The Research Foundation of SUNY
*
* This file is dual licensed. It may be redistributed and/or modified
* under the terms of the Apache 2.0 License OR version 2 of the GNU
* General Public License.
*/
#include "sdcardfs.h"
#include <linux/fs_context.h>
#include <linux/fs_parser.h>
#include <linux/fscrypt.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/parser.h>
enum sdcardfs_param {
Opt_fsuid,
Opt_fsgid,
Opt_gid,
Opt_debug,
Opt_mask,
Opt_multiuser,
Opt_userid,
Opt_reserved_mb,
Opt_gid_derivation,
Opt_default_normal,
Opt_nocache,
Opt_unshared_obb,
Opt_err,
};
static const struct fs_parameter_spec sdcardfs_param_specs[] = {
fsparam_u32("fsuid", Opt_fsuid),
fsparam_u32("fsgid", Opt_fsgid),
fsparam_u32("gid", Opt_gid),
fsparam_bool("debug", Opt_debug),
fsparam_u32("mask", Opt_mask),
fsparam_u32("userid", Opt_userid),
fsparam_bool("multiuser", Opt_multiuser),
fsparam_bool("derive_gid", Opt_gid_derivation),
fsparam_bool("default_normal", Opt_default_normal),
fsparam_bool("unshared_obb", Opt_unshared_obb),
fsparam_u32("reserved_mb", Opt_reserved_mb),
fsparam_bool("nocache", Opt_nocache),
{}
};
static const struct fs_parameter_description sdcardfs_parameters = {
.name = "sdcardfs",
.specs = sdcardfs_param_specs,
};
static int sdcardfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
{
struct sdcardfs_context_options *fc_opts = fc->fs_private;
struct sdcardfs_mount_options *opts = &fc_opts->opts;
struct sdcardfs_vfsmount_options *vfsopts = &fc_opts->vfsopts;
struct fs_parse_result result;
int opt;
opt = fs_parse(fc, &sdcardfs_parameters, param, &result);
if (opt < 0)
return opt;
switch (opt) {
case Opt_debug:
opts->debug = true;
break;
case Opt_fsuid:
opts->fs_low_uid = result.uint_32;
break;
case Opt_fsgid:
opts->fs_low_gid = result.uint_32;
break;
case Opt_gid:
vfsopts->gid = result.uint_32;
break;
case Opt_userid:
opts->fs_user_id = result.uint_32;
break;
case Opt_mask:
vfsopts->mask = result.uint_32;
break;
case Opt_multiuser:
opts->multiuser = true;
break;
case Opt_reserved_mb:
opts->reserved_mb = result.uint_32;
break;
case Opt_gid_derivation:
opts->gid_derivation = true;
break;
case Opt_default_normal:
opts->default_normal = true;
break;
case Opt_nocache:
opts->nocache = true;
break;
case Opt_unshared_obb:
opts->unshared_obb = true;
break;
default:
return -EINVAL;
}
return 0;
}
static void copy_sb_opts(struct sdcardfs_mount_options *opts,
struct fs_context *fc)
{
struct sdcardfs_context_options *fcopts = fc->fs_private;
opts->debug = fcopts->opts.debug;
opts->default_normal = fcopts->opts.default_normal;
opts->fs_low_gid = fcopts->opts.fs_low_gid;
opts->fs_low_uid = fcopts->opts.fs_low_uid;
opts->fs_user_id = fcopts->opts.fs_user_id;
opts->gid_derivation = fcopts->opts.gid_derivation;
opts->multiuser = fcopts->opts.multiuser;
opts->nocache = fcopts->opts.nocache;
opts->reserved_mb = fcopts->opts.reserved_mb;
opts->unshared_obb = fcopts->opts.unshared_obb;
}
#if 0
/*
* our custom d_alloc_root work-alike
*
* we can't use d_alloc_root if we want to use our own interpose function
* unchanged, so we simply call our own "fake" d_alloc_root
*/
static struct dentry *sdcardfs_d_alloc_root(struct super_block *sb)
{
struct dentry *ret = NULL;
if (sb) {
static const struct qstr name = {
.name = "/",
.len = 1
};
ret = d_alloc(NULL, &name);
if (ret) {
d_set_d_op(ret, &sdcardfs_ci_dops);
ret->d_sb = sb;
ret->d_parent = ret;
}
}
return ret;
}
#endif
DEFINE_MUTEX(sdcardfs_super_list_lock);
EXPORT_SYMBOL_GPL(sdcardfs_super_list_lock);
LIST_HEAD(sdcardfs_super_list);
EXPORT_SYMBOL_GPL(sdcardfs_super_list);
struct sdcardfs_mount_private {
struct vfsmount *mnt;
const char *dev_name;
void *raw_data;
};
static int __sdcardfs_fill_super(
struct super_block *sb,
struct fs_context *fc)
{
int err = 0;
struct super_block *lower_sb;
struct path lower_path;
struct sdcardfs_sb_info *sb_info;
struct inode *inode;
const char *dev_name = fc->source;
struct sdcardfs_context_options *fcopts = fc->fs_private;
struct sdcardfs_mount_options *opts = &fcopts->opts;
struct sdcardfs_vfsmount_options *mntopts = &fcopts->vfsopts;
pr_info("sdcardfs version 2.0\n");
if (!dev_name) {
pr_err("sdcardfs: read_super: missing dev_name argument\n");
err = -EINVAL;
goto out;
}
pr_info("sdcardfs: dev_name -> %s\n", dev_name);
pr_info("sdcardfs: gid=%d,mask=%x\n", mntopts->gid, mntopts->mask);
/* parse lower path */
err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
&lower_path);
if (err) {
pr_err("sdcardfs: error accessing lower directory '%s'\n", dev_name);
goto out;
}
/* allocate superblock private data */
sb->s_fs_info = kzalloc(sizeof(struct sdcardfs_sb_info), GFP_KERNEL);
if (!SDCARDFS_SB(sb)) {
pr_crit("sdcardfs: read_super: out of memory\n");
err = -ENOMEM;
goto out_free;
}
sb_info = sb->s_fs_info;
copy_sb_opts(&sb_info->options, fc);
if (opts->debug) {
pr_info("sdcardfs : options - debug:%d\n", opts->debug);
pr_info("sdcardfs : options - gid:%d\n", mntopts->gid);
pr_info("sdcardfs : options - mask:%d\n", mntopts->mask);
}
/* set the lower superblock field of upper superblock */
lower_sb = lower_path.dentry->d_sb;
atomic_inc(&lower_sb->s_active);
sdcardfs_set_lower_super(sb, lower_sb);
sb->s_stack_depth = lower_sb->s_stack_depth + 1;
if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) {
pr_err("sdcardfs: maximum fs stacking depth exceeded\n");
err = -EINVAL;
goto out_sput;
}
/* inherit maxbytes from lower file system */
sb->s_maxbytes = lower_sb->s_maxbytes;
/*
* Our c/m/atime granularity is 1 ns because we may stack on file
* systems whose granularity is as good.
*/
sb->s_time_gran = 1;
sb->s_magic = SDCARDFS_SUPER_MAGIC;
sb->s_op = &sdcardfs_sops;
/* get a new inode and allocate our root dentry */
inode = sdcardfs_iget(sb, d_inode(lower_path.dentry), 0);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
goto out_sput;
}
sb->s_root = d_make_root(inode);
if (!sb->s_root) {
err = -ENOMEM;
goto out_sput;
}
d_set_d_op(sb->s_root, &sdcardfs_ci_dops);
/* link the upper and lower dentries */
sb->s_root->d_fsdata = NULL;
err = new_dentry_private_data(sb->s_root);
if (err)
goto out_freeroot;
/* set the lower dentries for s_root */
sdcardfs_set_lower_path(sb->s_root, &lower_path);
/*
* No need to call interpose because we already have a positive
* dentry, which was instantiated by d_make_root. Just need to
* d_rehash it.
*/
d_rehash(sb->s_root);
/* setup permission policy */
sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL);
mutex_lock(&sdcardfs_super_list_lock);
if (sb_info->options.multiuser) {
setup_derived_state(d_inode(sb->s_root), PERM_PRE_ROOT,
sb_info->options.fs_user_id, AID_ROOT);
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name);
} else {
setup_derived_state(d_inode(sb->s_root), PERM_ROOT,
sb_info->options.fs_user_id, AID_ROOT);
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name);
}
fixup_tmp_permissions(d_inode(sb->s_root));
sb_info->sb = sb;
list_add(&sb_info->list, &sdcardfs_super_list);
mutex_unlock(&sdcardfs_super_list_lock);
sb_info->fscrypt_nb.notifier_call = sdcardfs_on_fscrypt_key_removed;
fscrypt_register_key_removal_notifier(&sb_info->fscrypt_nb);
if (!(fc->sb_flags & SB_SILENT))
pr_info("sdcardfs: mounted on top of %s type %s\n",
dev_name, lower_sb->s_type->name);
goto out; /* all is well */
/* no longer needed: free_dentry_private_data(sb->s_root); */
out_freeroot:
dput(sb->s_root);
sb->s_root = NULL;
out_sput:
/* drop refs we took earlier */
atomic_dec(&lower_sb->s_active);
kfree(SDCARDFS_SB(sb));
sb->s_fs_info = NULL;
out_free:
path_put(&lower_path);
out:
return err;
}
static int sdcardfs_get_tree(struct fs_context *fc)
{
return vfs_get_super(fc, vfs_get_independent_super,
__sdcardfs_fill_super);
}
void *sdcardfs_alloc_mnt_data(void)
{
return kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL);
}
void sdcardfs_kill_sb(struct super_block *sb)
{
struct sdcardfs_sb_info *sbi;
if (sb->s_magic == SDCARDFS_SUPER_MAGIC && sb->s_fs_info) {
sbi = SDCARDFS_SB(sb);
fscrypt_unregister_key_removal_notifier(&sbi->fscrypt_nb);
mutex_lock(&sdcardfs_super_list_lock);
list_del(&sbi->list);
mutex_unlock(&sdcardfs_super_list_lock);
}
kill_anon_super(sb);
}
static void sdcardfs_free_fs_context(struct fs_context *fc)
{
struct sdcardfs_context_options *fc_opts = fc->fs_private;
kfree(fc_opts);
}
/* Most of the remount happens in sdcardfs_update_mnt_data */
static int sdcardfs_reconfigure_context(struct fs_context *fc)
{
struct sdcardfs_context_options *fc_opts = fc->fs_private;
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(fc->root->d_sb);
sbi->options.debug = fc_opts->opts.debug;
if (sbi->options.debug) {
pr_info("sdcardfs : options - debug:%d\n", sbi->options.debug);
pr_info("sdcardfs : options - gid:%d\n", fc_opts->vfsopts.gid);
pr_info("sdcardfs : options - mask:%d\n",
fc_opts->vfsopts.mask);
}
return 0;
}
/* reconfigure is handled by sdcardfs_update_mnt_data */
static const struct fs_context_operations sdcardfs_context_options_ops = {
.parse_param = sdcardfs_parse_param,
.get_tree = sdcardfs_get_tree,
.free = sdcardfs_free_fs_context,
.reconfigure = sdcardfs_reconfigure_context,
};
static int sdcardfs_init_fs_context(struct fs_context *fc)
{
struct sdcardfs_context_options *fc_opts =
kmalloc(sizeof(struct sdcardfs_context_options), GFP_KERNEL);
/* by default, we use AID_MEDIA_RW as uid, gid */
fc_opts->opts.fs_low_uid = AID_MEDIA_RW;
fc_opts->opts.fs_low_gid = AID_MEDIA_RW;
fc_opts->opts.fs_user_id = 0;
fc_opts->vfsopts.gid = 0;
fc_opts->vfsopts.mask = 0;
/* by default, 0MB is reserved */
fc_opts->opts.reserved_mb = 0;
/* by default, gid derivation is off */
fc_opts->opts.gid_derivation = false;
fc_opts->opts.default_normal = false;
fc_opts->opts.nocache = false;
fc_opts->opts.multiuser = false;
fc_opts->opts.debug = false;
fc->fs_private = fc_opts;
fc->ops = &sdcardfs_context_options_ops;
return 0;
}
static struct file_system_type sdcardfs_fs_type = {
.owner = THIS_MODULE,
.name = SDCARDFS_NAME,
.alloc_mnt_data = sdcardfs_alloc_mnt_data,
.kill_sb = sdcardfs_kill_sb,
.init_fs_context = sdcardfs_init_fs_context,
.fs_flags = 0,
};
MODULE_ALIAS_FS(SDCARDFS_NAME);
static int __init init_sdcardfs_fs(void)
{
int err;
pr_info("Registering sdcardfs " SDCARDFS_VERSION "\n");
err = sdcardfs_init_inode_cache();
if (err)
goto out;
err = sdcardfs_init_dentry_cache();
if (err)
goto out;
err = packagelist_init();
if (err)
goto out;
err = register_filesystem(&sdcardfs_fs_type);
out:
if (err) {
sdcardfs_destroy_inode_cache();
sdcardfs_destroy_dentry_cache();
packagelist_exit();
}
return err;
}
static void __exit exit_sdcardfs_fs(void)
{
sdcardfs_destroy_inode_cache();
sdcardfs_destroy_dentry_cache();
packagelist_exit();
unregister_filesystem(&sdcardfs_fs_type);
pr_info("Completed sdcardfs module unload\n");
}
/* Original wrapfs authors */
MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University (http://www.fsl.cs.sunysb.edu/)");
/* Original sdcardfs authors */
MODULE_AUTHOR("Woojoong Lee, Daeho Jeong, Kitae Lee, Yeongjin Gil System Memory Lab., Samsung Electronics");
/* Current maintainer */
MODULE_AUTHOR("Daniel Rosenberg, Google");
MODULE_DESCRIPTION("Sdcardfs " SDCARDFS_VERSION);
MODULE_LICENSE("GPL");
module_init(init_sdcardfs_fs);
module_exit(exit_sdcardfs_fs);