Eric Biggers 3e5dc4ed2d 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-24 22:13:05 +00:00

519 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/fscrypt.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/parser.h>
enum {
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 match_table_t sdcardfs_tokens = {
{Opt_fsuid, "fsuid=%u"},
{Opt_fsgid, "fsgid=%u"},
{Opt_gid, "gid=%u"},
{Opt_debug, "debug"},
{Opt_mask, "mask=%u"},
{Opt_userid, "userid=%d"},
{Opt_multiuser, "multiuser"},
{Opt_gid_derivation, "derive_gid"},
{Opt_default_normal, "default_normal"},
{Opt_unshared_obb, "unshared_obb"},
{Opt_reserved_mb, "reserved_mb=%u"},
{Opt_nocache, "nocache"},
{Opt_err, NULL}
};
static int parse_options(struct super_block *sb, char *options, int silent,
int *debug, struct sdcardfs_vfsmount_options *vfsopts,
struct sdcardfs_mount_options *opts)
{
char *p;
substring_t args[MAX_OPT_ARGS];
int option;
/* by default, we use AID_MEDIA_RW as uid, gid */
opts->fs_low_uid = AID_MEDIA_RW;
opts->fs_low_gid = AID_MEDIA_RW;
vfsopts->mask = 0;
opts->multiuser = false;
opts->fs_user_id = 0;
vfsopts->gid = 0;
/* by default, 0MB is reserved */
opts->reserved_mb = 0;
/* by default, gid derivation is off */
opts->gid_derivation = false;
opts->default_normal = false;
opts->nocache = false;
*debug = 0;
if (!options)
return 0;
while ((p = strsep(&options, ",")) != NULL) {
int token;
if (!*p)
continue;
token = match_token(p, sdcardfs_tokens, args);
switch (token) {
case Opt_debug:
*debug = 1;
break;
case Opt_fsuid:
if (match_int(&args[0], &option))
return 0;
opts->fs_low_uid = option;
break;
case Opt_fsgid:
if (match_int(&args[0], &option))
return 0;
opts->fs_low_gid = option;
break;
case Opt_gid:
if (match_int(&args[0], &option))
return 0;
vfsopts->gid = option;
break;
case Opt_userid:
if (match_int(&args[0], &option))
return 0;
opts->fs_user_id = option;
break;
case Opt_mask:
if (match_int(&args[0], &option))
return 0;
vfsopts->mask = option;
break;
case Opt_multiuser:
opts->multiuser = true;
break;
case Opt_reserved_mb:
if (match_int(&args[0], &option))
return 0;
opts->reserved_mb = option;
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;
/* unknown option */
default:
if (!silent)
pr_err("Unrecognized mount option \"%s\" or missing value", p);
return -EINVAL;
}
}
if (*debug) {
pr_info("sdcardfs : options - debug:%d\n", *debug);
pr_info("sdcardfs : options - uid:%d\n",
opts->fs_low_uid);
pr_info("sdcardfs : options - gid:%d\n",
opts->fs_low_gid);
}
return 0;
}
int parse_options_remount(struct super_block *sb, char *options, int silent,
struct sdcardfs_vfsmount_options *vfsopts)
{
char *p;
substring_t args[MAX_OPT_ARGS];
int option;
int debug;
if (!options)
return 0;
while ((p = strsep(&options, ",")) != NULL) {
int token;
if (!*p)
continue;
token = match_token(p, sdcardfs_tokens, args);
switch (token) {
case Opt_debug:
debug = 1;
break;
case Opt_gid:
if (match_int(&args[0], &option))
return 0;
vfsopts->gid = option;
break;
case Opt_mask:
if (match_int(&args[0], &option))
return 0;
vfsopts->mask = option;
break;
case Opt_unshared_obb:
case Opt_default_normal:
case Opt_multiuser:
case Opt_userid:
case Opt_fsuid:
case Opt_fsgid:
case Opt_reserved_mb:
case Opt_gid_derivation:
if (!silent)
pr_warn("Option \"%s\" can't be changed during remount\n", p);
break;
/* unknown option */
default:
if (!silent)
pr_err("Unrecognized mount option \"%s\" or missing value", p);
return -EINVAL;
}
}
if (debug) {
pr_info("sdcardfs : options - debug:%d\n", debug);
pr_info("sdcardfs : options - gid:%d\n", vfsopts->gid);
pr_info("sdcardfs : options - mask:%d\n", vfsopts->mask);
}
return 0;
}
#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);
/*
* There is no need to lock the sdcardfs_super_info's rwsem as there is no
* way anyone can have a reference to the superblock at this point in time.
*/
static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb,
const char *dev_name, void *raw_data, int silent)
{
int err = 0;
int debug;
struct super_block *lower_sb;
struct path lower_path;
struct sdcardfs_sb_info *sb_info;
struct sdcardfs_vfsmount_options *mnt_opt = mnt->data;
struct inode *inode;
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: options -> %s\n", (char *)raw_data);
pr_info("sdcardfs: mnt -> %p\n", mnt);
/* 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;
/* parse options */
err = parse_options(sb, raw_data, silent, &debug, mnt_opt, &sb_info->options);
if (err) {
pr_err("sdcardfs: invalid options\n");
goto out_freesbi;
}
/* 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 (!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);
out_freesbi:
kfree(SDCARDFS_SB(sb));
sb->s_fs_info = NULL;
out_free:
path_put(&lower_path);
out:
return err;
}
struct sdcardfs_mount_private {
struct vfsmount *mnt;
const char *dev_name;
void *raw_data;
};
static int __sdcardfs_fill_super(
struct super_block *sb,
void *_priv, int silent)
{
struct sdcardfs_mount_private *priv = _priv;
return sdcardfs_read_super(priv->mnt,
sb, priv->dev_name, priv->raw_data, silent);
}
static struct dentry *sdcardfs_mount(struct vfsmount *mnt,
struct file_system_type *fs_type, int flags,
const char *dev_name, void *raw_data)
{
struct sdcardfs_mount_private priv = {
.mnt = mnt,
.dev_name = dev_name,
.raw_data = raw_data
};
return mount_nodev(fs_type, flags,
&priv, __sdcardfs_fill_super);
}
static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data)
{
WARN(1, "sdcardfs does not support mount. Use mount2.\n");
return ERR_PTR(-EINVAL);
}
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 struct file_system_type sdcardfs_fs_type = {
.owner = THIS_MODULE,
.name = SDCARDFS_NAME,
.mount = sdcardfs_mount_wrn,
.mount2 = sdcardfs_mount,
.alloc_mnt_data = sdcardfs_alloc_mnt_data,
.kill_sb = sdcardfs_kill_sb,
.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);