Files
android_kernel_xiaomi_sm8450/fs/sdcardfs/super.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

315 lines
8.3 KiB
C

/*
* fs/sdcardfs/super.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 <linux/fs_context.h>
#include "sdcardfs.h"
/*
* The inode cache is used with alloc_inode for both our inode info and the
* vfs inode.
*/
static struct kmem_cache *sdcardfs_inode_cachep;
/*
* To support the top references, we must track some data separately.
* An sdcardfs_inode_info always has a reference to its data, and once set up,
* also has a reference to its top. The top may be itself, in which case it
* holds two references to its data. When top is changed, it takes a ref to the
* new data and then drops the ref to the old data.
*/
static struct kmem_cache *sdcardfs_inode_data_cachep;
void data_release(struct kref *ref)
{
struct sdcardfs_inode_data *data =
container_of(ref, struct sdcardfs_inode_data, refcount);
kmem_cache_free(sdcardfs_inode_data_cachep, data);
}
/* final actions when unmounting a file system */
static void sdcardfs_put_super(struct super_block *sb)
{
struct sdcardfs_sb_info *spd;
struct super_block *s;
spd = SDCARDFS_SB(sb);
if (!spd)
return;
if (spd->obbpath_s) {
kfree(spd->obbpath_s);
path_put(&spd->obbpath);
}
/* decrement lower super references */
s = sdcardfs_lower_super(sb);
sdcardfs_set_lower_super(sb, NULL);
atomic_dec(&s->s_active);
kfree(spd);
sb->s_fs_info = NULL;
}
static int sdcardfs_statfs(struct dentry *dentry, struct kstatfs *buf)
{
int err;
struct path lower_path;
u32 min_blocks;
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
sdcardfs_get_lower_path(dentry, &lower_path);
err = vfs_statfs(&lower_path, buf);
sdcardfs_put_lower_path(dentry, &lower_path);
if (sbi->options.reserved_mb) {
/* Invalid statfs informations. */
if (buf->f_bsize == 0) {
pr_err("Returned block size is zero.\n");
return -EINVAL;
}
min_blocks = ((sbi->options.reserved_mb * 1024 * 1024)/buf->f_bsize);
buf->f_blocks -= min_blocks;
if (buf->f_bavail > min_blocks)
buf->f_bavail -= min_blocks;
else
buf->f_bavail = 0;
/* Make reserved blocks invisiable to media storage */
buf->f_bfree = buf->f_bavail;
}
/* set return buf to our f/s to avoid confusing user-level utils */
buf->f_type = SDCARDFS_SUPER_MAGIC;
return err;
}
static void *sdcardfs_clone_mnt_data(void *data)
{
struct sdcardfs_vfsmount_options *opt = kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL);
struct sdcardfs_vfsmount_options *old = data;
if (!opt)
return NULL;
opt->gid = old->gid;
opt->mask = old->mask;
return opt;
}
static void sdcardfs_copy_mnt_data(void *data, void *newdata)
{
struct sdcardfs_vfsmount_options *old = data;
struct sdcardfs_vfsmount_options *new = newdata;
old->gid = new->gid;
old->mask = new->mask;
}
static void sdcardfs_update_mnt_data(void *data, struct fs_context *fc)
{
struct sdcardfs_vfsmount_options *opts = data;
struct sdcardfs_context_options *fcopts = fc->fs_private;
opts->gid = fcopts->vfsopts.gid;
opts->mask = fcopts->vfsopts.mask;
}
/*
* Called by iput() when the inode reference count reached zero
* and the inode is not hashed anywhere. Used to clear anything
* that needs to be, before the inode is completely destroyed and put
* on the inode free list.
*/
static void sdcardfs_evict_inode(struct inode *inode)
{
struct inode *lower_inode;
truncate_inode_pages(&inode->i_data, 0);
set_top(SDCARDFS_I(inode), NULL);
clear_inode(inode);
/*
* Decrement a reference to a lower_inode, which was incremented
* by our read_inode when it was created initially.
*/
lower_inode = sdcardfs_lower_inode(inode);
sdcardfs_set_lower_inode(inode, NULL);
iput(lower_inode);
}
static struct inode *sdcardfs_alloc_inode(struct super_block *sb)
{
struct sdcardfs_inode_info *i;
struct sdcardfs_inode_data *d;
i = kmem_cache_alloc(sdcardfs_inode_cachep, GFP_KERNEL);
if (!i)
return NULL;
/* memset everything up to the inode to 0 */
memset(i, 0, offsetof(struct sdcardfs_inode_info, vfs_inode));
d = kmem_cache_alloc(sdcardfs_inode_data_cachep,
GFP_KERNEL | __GFP_ZERO);
if (!d) {
kmem_cache_free(sdcardfs_inode_cachep, i);
return NULL;
}
i->data = d;
kref_init(&d->refcount);
i->top_data = d;
spin_lock_init(&i->top_lock);
kref_get(&d->refcount);
inode_set_iversion(&i->vfs_inode, 1);
return &i->vfs_inode;
}
static void i_callback(struct rcu_head *head)
{
struct inode *inode = container_of(head, struct inode, i_rcu);
release_own_data(SDCARDFS_I(inode));
kmem_cache_free(sdcardfs_inode_cachep, SDCARDFS_I(inode));
}
static void sdcardfs_destroy_inode(struct inode *inode)
{
call_rcu(&inode->i_rcu, i_callback);
}
/* sdcardfs inode cache constructor */
static void init_once(void *obj)
{
struct sdcardfs_inode_info *i = obj;
inode_init_once(&i->vfs_inode);
}
int sdcardfs_init_inode_cache(void)
{
sdcardfs_inode_cachep =
kmem_cache_create("sdcardfs_inode_cache",
sizeof(struct sdcardfs_inode_info), 0,
SLAB_RECLAIM_ACCOUNT, init_once);
if (!sdcardfs_inode_cachep)
return -ENOMEM;
sdcardfs_inode_data_cachep =
kmem_cache_create("sdcardfs_inode_data_cache",
sizeof(struct sdcardfs_inode_data), 0,
SLAB_RECLAIM_ACCOUNT, NULL);
if (!sdcardfs_inode_data_cachep) {
kmem_cache_destroy(sdcardfs_inode_cachep);
return -ENOMEM;
}
return 0;
}
/* sdcardfs inode cache destructor */
void sdcardfs_destroy_inode_cache(void)
{
kmem_cache_destroy(sdcardfs_inode_data_cachep);
kmem_cache_destroy(sdcardfs_inode_cachep);
}
/*
* Used only in nfs, to kill any pending RPC tasks, so that subsequent
* code can actually succeed and won't leave tasks that need handling.
*/
static void sdcardfs_umount_begin(struct super_block *sb)
{
struct super_block *lower_sb;
lower_sb = sdcardfs_lower_super(sb);
if (lower_sb && lower_sb->s_op && lower_sb->s_op->umount_begin)
lower_sb->s_op->umount_begin(lower_sb);
}
static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m,
struct dentry *root)
{
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(root->d_sb);
struct sdcardfs_mount_options *opts = &sbi->options;
struct sdcardfs_vfsmount_options *vfsopts = mnt->data;
if (opts->fs_low_uid != 0)
seq_printf(m, ",fsuid=%u", opts->fs_low_uid);
if (opts->fs_low_gid != 0)
seq_printf(m, ",fsgid=%u", opts->fs_low_gid);
if (vfsopts->gid != 0)
seq_printf(m, ",gid=%u", vfsopts->gid);
if (opts->multiuser)
seq_puts(m, ",multiuser");
if (vfsopts->mask)
seq_printf(m, ",mask=%u", vfsopts->mask);
if (opts->fs_user_id)
seq_printf(m, ",userid=%u", opts->fs_user_id);
if (opts->gid_derivation)
seq_puts(m, ",derive_gid");
if (opts->default_normal)
seq_puts(m, ",default_normal");
if (opts->reserved_mb != 0)
seq_printf(m, ",reserved=%uMB", opts->reserved_mb);
if (opts->nocache)
seq_printf(m, ",nocache");
if (opts->unshared_obb)
seq_printf(m, ",unshared_obb");
return 0;
};
int sdcardfs_on_fscrypt_key_removed(struct notifier_block *nb,
unsigned long action, void *data)
{
struct sdcardfs_sb_info *sbi = container_of(nb, struct sdcardfs_sb_info,
fscrypt_nb);
/*
* Evict any unused sdcardfs dentries (and hence any unused sdcardfs
* inodes, since sdcardfs doesn't cache unpinned inodes by themselves)
* so that the lower filesystem's encrypted inodes can be evicted.
* This is needed to make the FS_IOC_REMOVE_ENCRYPTION_KEY ioctl
* properly "lock" the files underneath the sdcardfs mount.
*/
shrink_dcache_sb(sbi->sb);
return NOTIFY_OK;
}
const struct super_operations sdcardfs_sops = {
.put_super = sdcardfs_put_super,
.statfs = sdcardfs_statfs,
.clone_mnt_data = sdcardfs_clone_mnt_data,
.copy_mnt_data = sdcardfs_copy_mnt_data,
.update_mnt_data = sdcardfs_update_mnt_data,
.evict_inode = sdcardfs_evict_inode,
.umount_begin = sdcardfs_umount_begin,
.show_options2 = sdcardfs_show_options,
.alloc_inode = sdcardfs_alloc_inode,
.destroy_inode = sdcardfs_destroy_inode,
.drop_inode = generic_delete_inode,
};