ANDROID: sdcardfs: Add GID Derivation to sdcardfs

This changes sdcardfs to modify the user and group in the
underlying filesystem depending on its usage. Ownership is
set by Android user, and package, as well as if the file is
under obb or cache. Other files can be labeled by extension.
Those values are set via the configfs interace.

To add an entry,
mkdir -p [configfs root]/sdcardfs/extensions/[gid]/[ext]

Signed-off-by: Daniel Rosenberg <drosen@google.com>
Bug: 34262585
Change-Id: I4e030ce84f094a678376349b1a96923e5076a0f4
This commit is contained in:
Daniel Rosenberg 2017-01-25 13:48:45 -08:00 committed by Amit Pundir
parent 0cc825683b
commit dc7a4c4826
7 changed files with 487 additions and 96 deletions

View file

@ -30,6 +30,8 @@ static void inherit_derived_state(struct inode *parent, struct inode *child)
ci->userid = pi->userid;
ci->d_uid = pi->d_uid;
ci->under_android = pi->under_android;
ci->under_cache = pi->under_cache;
ci->under_obb = pi->under_obb;
set_top(ci, pi->top);
}
@ -43,11 +45,13 @@ void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid,
info->userid = userid;
info->d_uid = uid;
info->under_android = under_android;
info->under_cache = false;
info->under_obb = false;
set_top(info, top);
}
/* While renaming, there is a point where we want the path from dentry, but the name from newdentry */
void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry)
void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const char *name)
{
struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry));
struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent));
@ -57,63 +61,185 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, st
* the properties are maintained on its private fields
* because the inode attributes will be modified with that of
* its lower inode.
* The derived state will be updated on the last
* stage of each system call by fix_derived_permission(inode).
* These values are used by our custom permission call instead
* of using the inode permissions.
*/
inherit_derived_state(d_inode(parent), d_inode(dentry));
/* Files don't get special labels */
if (!S_ISDIR(d_inode(dentry)->i_mode))
return;
/* Derive custom permissions based on parent and current node */
switch (parent_info->perm) {
case PERM_INHERIT:
/* Already inherited above */
break;
case PERM_PRE_ROOT:
/* Legacy internal layout places users at top level */
info->perm = PERM_ROOT;
info->userid = simple_strtoul(newdentry->d_name.name, NULL, 10);
case PERM_INHERIT:
case PERM_ANDROID_PACKAGE_CACHE:
/* Already inherited above */
break;
case PERM_PRE_ROOT:
/* Legacy internal layout places users at top level */
info->perm = PERM_ROOT;
info->userid = simple_strtoul(name, NULL, 10);
set_top(info, &info->vfs_inode);
break;
case PERM_ROOT:
/* Assume masked off by default. */
if (!strcasecmp(name, "Android")) {
/* App-specific directories inside; let anyone traverse */
info->perm = PERM_ANDROID;
info->under_android = true;
set_top(info, &info->vfs_inode);
break;
case PERM_ROOT:
/* Assume masked off by default. */
if (!strcasecmp(newdentry->d_name.name, "Android")) {
/* App-specific directories inside; let anyone traverse */
info->perm = PERM_ANDROID;
info->under_android = true;
set_top(info, &info->vfs_inode);
}
break;
case PERM_ANDROID:
if (!strcasecmp(newdentry->d_name.name, "data")) {
/* App-specific directories inside; let anyone traverse */
info->perm = PERM_ANDROID_DATA;
set_top(info, &info->vfs_inode);
} else if (!strcasecmp(newdentry->d_name.name, "obb")) {
/* App-specific directories inside; let anyone traverse */
info->perm = PERM_ANDROID_OBB;
set_top(info, &info->vfs_inode);
/* Single OBB directory is always shared */
} else if (!strcasecmp(newdentry->d_name.name, "media")) {
/* App-specific directories inside; let anyone traverse */
info->perm = PERM_ANDROID_MEDIA;
set_top(info, &info->vfs_inode);
}
break;
case PERM_ANDROID_DATA:
case PERM_ANDROID_OBB:
case PERM_ANDROID_MEDIA:
appid = get_appid(newdentry->d_name.name);
if (appid != 0 && !is_excluded(newdentry->d_name.name, parent_info->userid)) {
info->d_uid = multiuser_get_uid(parent_info->userid, appid);
}
}
break;
case PERM_ANDROID:
if (!strcasecmp(name, "data")) {
/* App-specific directories inside; let anyone traverse */
info->perm = PERM_ANDROID_DATA;
set_top(info, &info->vfs_inode);
break;
} else if (!strcasecmp(name, "obb")) {
/* App-specific directories inside; let anyone traverse */
info->perm = PERM_ANDROID_OBB;
info->under_obb = true;
set_top(info, &info->vfs_inode);
/* Single OBB directory is always shared */
} else if (!strcasecmp(name, "media")) {
/* App-specific directories inside; let anyone traverse */
info->perm = PERM_ANDROID_MEDIA;
set_top(info, &info->vfs_inode);
}
break;
case PERM_ANDROID_OBB:
case PERM_ANDROID_DATA:
case PERM_ANDROID_MEDIA:
info->perm = PERM_ANDROID_PACKAGE;
appid = get_appid(name);
if (appid != 0 && !is_excluded(name, parent_info->userid)) {
info->d_uid = multiuser_get_uid(parent_info->userid, appid);
}
set_top(info, &info->vfs_inode);
break;
case PERM_ANDROID_PACKAGE:
if (!strcasecmp(name, "cache")) {
info->perm = PERM_ANDROID_PACKAGE_CACHE;
info->under_cache = true;
}
break;
}
}
void get_derived_permission(struct dentry *parent, struct dentry *dentry)
{
get_derived_permission_new(parent, dentry, dentry);
get_derived_permission_new(parent, dentry, dentry->d_name.name);
}
static appid_t get_type(const char *name)
{
const char *ext = strrchr(name, '.');
appid_t id;
if (ext && ext[0]) {
ext = &ext[1];
id = get_ext_gid(ext);
return id?:AID_MEDIA_RW;
}
return AID_MEDIA_RW;
}
void fixup_lower_ownership(struct dentry *dentry, const char *name)
{
struct path path;
struct inode *inode;
struct inode *delegated_inode = NULL;
int error;
struct sdcardfs_inode_info *info;
struct sdcardfs_inode_info *info_top;
perm_t perm;
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
uid_t uid = sbi->options.fs_low_uid;
gid_t gid = sbi->options.fs_low_gid;
struct iattr newattrs;
info = SDCARDFS_I(d_inode(dentry));
perm = info->perm;
if (info->under_obb) {
perm = PERM_ANDROID_OBB;
} else if (info->under_cache) {
perm = PERM_ANDROID_PACKAGE_CACHE;
} else if (perm == PERM_INHERIT) {
info_top = SDCARDFS_I(grab_top(info));
perm = info_top->perm;
release_top(info);
}
switch (perm) {
case PERM_ROOT:
case PERM_ANDROID:
case PERM_ANDROID_DATA:
case PERM_ANDROID_MEDIA:
case PERM_ANDROID_PACKAGE:
case PERM_ANDROID_PACKAGE_CACHE:
uid = multiuser_get_uid(info->userid, uid);
break;
case PERM_ANDROID_OBB:
uid = AID_MEDIA_OBB;
break;
case PERM_PRE_ROOT:
default:
break;
}
switch (perm) {
case PERM_ROOT:
case PERM_ANDROID:
case PERM_ANDROID_DATA:
case PERM_ANDROID_MEDIA:
if (S_ISDIR(d_inode(dentry)->i_mode))
gid = multiuser_get_uid(info->userid, AID_MEDIA_RW);
else
gid = multiuser_get_uid(info->userid, get_type(name));
break;
case PERM_ANDROID_OBB:
gid = AID_MEDIA_OBB;
break;
case PERM_ANDROID_PACKAGE:
if (info->d_uid != 0)
gid = multiuser_get_ext_gid(info->userid, info->d_uid);
else
gid = multiuser_get_uid(info->userid, uid);
break;
case PERM_ANDROID_PACKAGE_CACHE:
if (info->d_uid != 0)
gid = multiuser_get_cache_gid(info->userid, info->d_uid);
else
gid = multiuser_get_uid(info->userid, uid);
break;
case PERM_PRE_ROOT:
default:
break;
}
sdcardfs_get_lower_path(dentry, &path);
inode = d_inode(path.dentry);
if (d_inode(path.dentry)->i_gid.val != gid || d_inode(path.dentry)->i_uid.val != uid) {
retry_deleg:
newattrs.ia_valid = ATTR_GID | ATTR_UID | ATTR_FORCE;
newattrs.ia_uid = make_kuid(current_user_ns(), uid);
newattrs.ia_gid = make_kgid(current_user_ns(), gid);
if (!S_ISDIR(inode->i_mode))
newattrs.ia_valid |=
ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
inode_lock(inode);
error = security_path_chown(&path, newattrs.ia_uid, newattrs.ia_gid);
if (!error)
error = notify_change2(path.mnt, path.dentry, &newattrs, &delegated_inode);
inode_unlock(inode);
if (delegated_inode) {
error = break_deleg_wait(&delegated_inode);
if (!error)
goto retry_deleg;
}
if (error)
pr_err("sdcardfs: Failed to touch up lower fs gid/uid.\n");
}
}
static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct limit_search *limit)
@ -169,9 +295,30 @@ void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit)
dput(dentry);
}
void fixup_top_recursive(struct dentry *parent) {
void drop_recursive(struct dentry *parent)
{
struct dentry *dentry;
struct sdcardfs_inode_info *info;
if (!d_inode(parent))
return;
info = SDCARDFS_I(d_inode(parent));
spin_lock(&parent->d_lock);
list_for_each_entry(dentry, &parent->d_subdirs, d_child) {
if (d_inode(dentry)) {
if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) {
drop_recursive(dentry);
d_drop(dentry);
}
}
}
spin_unlock(&parent->d_lock);
}
void fixup_top_recursive(struct dentry *parent)
{
struct dentry *dentry;
struct sdcardfs_inode_info *info;
if (!d_inode(parent))
return;
info = SDCARDFS_I(d_inode(parent));

View file

@ -225,7 +225,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file)
}
/* save current_cred and override it */
OVERRIDE_CRED(sbi, saved_cred);
OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(inode));
file->private_data =
kzalloc(sizeof(struct sdcardfs_file_info), GFP_KERNEL);

View file

@ -22,16 +22,21 @@
#include <linux/fs_struct.h>
/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
const struct cred * override_fsids(struct sdcardfs_sb_info* sbi)
const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_info *info)
{
struct cred * cred;
const struct cred * old_cred;
struct cred *cred;
const struct cred *old_cred;
uid_t uid;
cred = prepare_creds();
if (!cred)
return NULL;
cred->fsuid = make_kuid(&init_user_ns, sbi->options.fs_low_uid);
if (info->under_obb)
uid = AID_MEDIA_OBB;
else
uid = multiuser_get_uid(info->userid, sbi->options.fs_low_uid);
cred->fsuid = make_kuid(&init_user_ns, uid);
cred->fsgid = make_kgid(&init_user_ns, sbi->options.fs_low_gid);
old_cred = override_creds(cred);
@ -40,9 +45,9 @@ const struct cred * override_fsids(struct sdcardfs_sb_info* sbi)
}
/* Do not directly use this function, use REVERT_CRED() instead. */
void revert_fsids(const struct cred * old_cred)
void revert_fsids(const struct cred *old_cred)
{
const struct cred * cur_cred;
const struct cred *cur_cred;
cur_cred = current->cred;
revert_creds(old_cred);
@ -70,7 +75,7 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry,
}
/* save current_cred and override it */
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
@ -98,6 +103,7 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry,
goto out;
fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry));
fixup_lower_ownership(dentry, dentry->d_name.name);
out:
current->fs = saved_fs;
@ -171,7 +177,7 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry)
}
/* save current_cred and override it */
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
@ -279,7 +285,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
}
/* save current_cred and override it */
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
/* check disk space */
if (!check_min_free_space(dentry, 0, 1)) {
@ -343,9 +349,8 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry));
/* update number of links on parent directory */
set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink);
fixup_lower_ownership(dentry, dentry->d_name.name);
unlock_dir(lower_parent_dentry);
if ((!sbi->options.multiuser) && (!strcasecmp(dentry->d_name.name, "obb"))
&& (pi->perm == PERM_ANDROID) && (pi->userid == 0))
make_nomedia_in_obb = 1;
@ -353,6 +358,8 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
/* When creating /Android/data and /Android/obb, mark them as .nomedia */
if (make_nomedia_in_obb ||
((pi->perm == PERM_ANDROID) && (!strcasecmp(dentry->d_name.name, "data")))) {
REVERT_CRED(saved_cred);
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(d_inode(dentry)));
set_fs_pwd(current->fs, &lower_path);
touch_err = touch(".nomedia", 0664);
if (touch_err) {
@ -390,7 +397,7 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry)
}
/* save current_cred and override it */
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
/* sdcardfs_get_real_lower(): in case of remove an user's obb dentry
* the dentry on the original path should be deleted. */
@ -483,7 +490,7 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry,
}
/* save current_cred and override it */
OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred);
OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred, SDCARDFS_I(new_dir));
sdcardfs_get_real_lower(old_dentry, &lower_old_path);
sdcardfs_get_lower_path(new_dentry, &lower_new_path);
@ -520,11 +527,10 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry,
sdcardfs_copy_and_fix_attrs(old_dir, d_inode(lower_old_dir_dentry));
fsstack_copy_inode_size(old_dir, d_inode(lower_old_dir_dentry));
}
/* At this point, not all dentry information has been moved, so
* we pass along new_dentry for the name.*/
get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry);
get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry->d_name.name);
fixup_tmp_permissions(d_inode(old_dentry));
fixup_top_recursive(old_dentry);
fixup_lower_ownership(old_dentry, new_dentry->d_name.name);
drop_recursive(old_dentry); /* Can't fixup ownership recursively :( */
out:
unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
dput(lower_old_dir_dentry);
@ -759,7 +765,7 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct
goto out_err;
/* save current_cred and override it */
OVERRIDE_CRED(SDCARDFS_SB(dentry->d_sb), saved_cred);
OVERRIDE_CRED(SDCARDFS_SB(dentry->d_sb), saved_cred, SDCARDFS_I(inode));
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;

View file

@ -368,7 +368,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
}
/* save current_cred and override it */
OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred);
OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
sdcardfs_get_lower_path(parent, &lower_parent_path);
@ -392,6 +392,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
/* get derived permission */
get_derived_permission(parent, dentry);
fixup_tmp_permissions(d_inode(dentry));
fixup_lower_ownership(dentry, dentry->d_name.name);
}
/* update parent directory's atime */
fsstack_copy_attr_atime(d_inode(parent),

View file

@ -18,20 +18,35 @@
* General Public License.
*/
#define MULTIUSER_APP_PER_USER_RANGE 100000
#define AID_USER_OFFSET 100000 /* offset for uid ranges for each user */
#define AID_APP_START 10000 /* first app user */
#define AID_APP_END 19999 /* last app user */
#define AID_CACHE_GID_START 20000 /* start of gids for apps to mark cached data */
#define AID_EXT_GID_START 30000 /* start of gids for apps to mark external data */
#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
typedef uid_t userid_t;
typedef uid_t appid_t;
static inline userid_t multiuser_get_user_id(uid_t uid) {
return uid / MULTIUSER_APP_PER_USER_RANGE;
static inline uid_t multiuser_get_uid(userid_t user_id, appid_t app_id)
{
return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET);
}
static inline appid_t multiuser_get_app_id(uid_t uid) {
return uid % MULTIUSER_APP_PER_USER_RANGE;
static inline gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id)
{
if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_CACHE_GID_START);
} else {
return -1;
}
}
static inline uid_t multiuser_get_uid(userid_t userId, appid_t appId) {
return userId * MULTIUSER_APP_PER_USER_RANGE + (appId % MULTIUSER_APP_PER_USER_RANGE);
static inline gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id)
{
if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_EXT_GID_START);
} else {
return -1;
}
}

View file

@ -21,6 +21,7 @@
#include "sdcardfs.h"
#include <linux/hashtable.h>
#include <linux/delay.h>
#include <linux/radix-tree.h>
#include <linux/init.h>
@ -38,6 +39,8 @@ struct hashtable_entry {
static DEFINE_HASHTABLE(package_to_appid, 8);
static DEFINE_HASHTABLE(package_to_userid, 8);
static DEFINE_HASHTABLE(ext_to_groupid, 8);
static struct kmem_cache *hashtable_entry_cachep;
@ -53,15 +56,33 @@ static unsigned int str_hash(const char *key) {
return h;
}
appid_t get_appid(const char *app_name)
appid_t get_appid(const char *key)
{
struct hashtable_entry *hash_cur;
unsigned int hash = str_hash(app_name);
unsigned int hash = str_hash(key);
appid_t ret_id;
rcu_read_lock();
hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) {
if (!strcasecmp(app_name, hash_cur->key)) {
if (!strcasecmp(key, hash_cur->key)) {
ret_id = atomic_read(&hash_cur->value);
rcu_read_unlock();
return ret_id;
}
}
rcu_read_unlock();
return 0;
}
appid_t get_ext_gid(const char *key)
{
struct hashtable_entry *hash_cur;
unsigned int hash = str_hash(key);
appid_t ret_id;
rcu_read_lock();
hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) {
if (!strcasecmp(key, hash_cur->key)) {
ret_id = atomic_read(&hash_cur->value);
rcu_read_unlock();
return ret_id;
@ -90,8 +111,8 @@ appid_t is_excluded(const char *app_name, userid_t user)
/* Kernel has already enforced everything we returned through
* derive_permissions_locked(), so this is used to lock down access
* even further, such as enforcing that apps hold sdcard_rw. */
int check_caller_access_to_name(struct inode *parent_node, const char* name) {
int check_caller_access_to_name(struct inode *parent_node, const char *name)
{
/* Always block security-sensitive files at root */
if (parent_node && SDCARDFS_I(parent_node)->perm == PERM_ROOT) {
if (!strcasecmp(name, "autorun.inf")
@ -124,7 +145,7 @@ int open_flags_to_access_mode(int open_flags) {
}
}
static struct hashtable_entry *alloc_packagelist_entry(const char *key,
static struct hashtable_entry *alloc_hashtable_entry(const char *key,
appid_t value)
{
struct hashtable_entry *ret = kmem_cache_alloc(hashtable_entry_cachep,
@ -154,13 +175,31 @@ static int insert_packagelist_appid_entry_locked(const char *key, appid_t value)
return 0;
}
}
new_entry = alloc_packagelist_entry(key, value);
new_entry = alloc_hashtable_entry(key, value);
if (!new_entry)
return -ENOMEM;
hash_add_rcu(package_to_appid, &new_entry->hlist, hash);
return 0;
}
static int insert_ext_gid_entry_locked(const char *key, appid_t value)
{
struct hashtable_entry *hash_cur;
struct hashtable_entry *new_entry;
unsigned int hash = str_hash(key);
/* An extension can only belong to one gid */
hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) {
if (!strcasecmp(key, hash_cur->key))
return -EINVAL;
}
new_entry = alloc_hashtable_entry(key, value);
if (!new_entry)
return -ENOMEM;
hash_add_rcu(ext_to_groupid, &new_entry->hlist, hash);
return 0;
}
static int insert_userid_exclude_entry_locked(const char *key, userid_t value)
{
struct hashtable_entry *hash_cur;
@ -172,7 +211,7 @@ static int insert_userid_exclude_entry_locked(const char *key, userid_t value)
if (atomic_read(&hash_cur->value) == value && !strcasecmp(key, hash_cur->key))
return 0;
}
new_entry = alloc_packagelist_entry(key, value);
new_entry = alloc_hashtable_entry(key, value);
if (!new_entry)
return -ENOMEM;
hash_add_rcu(package_to_userid, &new_entry->hlist, hash);
@ -234,6 +273,17 @@ static int insert_packagelist_entry(const char *key, appid_t value)
return err;
}
static int insert_ext_gid_entry(const char *key, appid_t value)
{
int err;
mutex_lock(&sdcardfs_super_list_lock);
err = insert_ext_gid_entry_locked(key, value);
mutex_unlock(&sdcardfs_super_list_lock);
return err;
}
static int insert_userid_exclude_entry(const char *key, userid_t value)
{
int err;
@ -247,7 +297,7 @@ static int insert_userid_exclude_entry(const char *key, userid_t value)
return err;
}
static void free_packagelist_entry(struct hashtable_entry *entry)
static void free_hashtable_entry(struct hashtable_entry *entry)
{
kfree(entry->key);
hash_del_rcu(&entry->dlist);
@ -276,7 +326,7 @@ static void remove_packagelist_entry_locked(const char *key)
}
synchronize_rcu();
hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist)
free_packagelist_entry(hash_cur);
free_hashtable_entry(hash_cur);
}
static void remove_packagelist_entry(const char *key)
@ -288,6 +338,29 @@ static void remove_packagelist_entry(const char *key)
return;
}
static void remove_ext_gid_entry_locked(const char *key, gid_t group)
{
struct hashtable_entry *hash_cur;
unsigned int hash = str_hash(key);
hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) {
if (!strcasecmp(key, hash_cur->key) && atomic_read(&hash_cur->value) == group) {
hash_del_rcu(&hash_cur->hlist);
synchronize_rcu();
free_hashtable_entry(hash_cur);
break;
}
}
}
static void remove_ext_gid_entry(const char *key, gid_t group)
{
mutex_lock(&sdcardfs_super_list_lock);
remove_ext_gid_entry_locked(key, group);
mutex_unlock(&sdcardfs_super_list_lock);
return;
}
static void remove_userid_all_entry_locked(userid_t userid)
{
struct hashtable_entry *hash_cur;
@ -303,7 +376,7 @@ static void remove_userid_all_entry_locked(userid_t userid)
}
synchronize_rcu();
hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) {
free_packagelist_entry(hash_cur);
free_hashtable_entry(hash_cur);
}
}
@ -325,7 +398,7 @@ static void remove_userid_exclude_entry_locked(const char *key, userid_t userid)
if (!strcasecmp(key, hash_cur->key) && atomic_read(&hash_cur->value) == userid) {
hash_del_rcu(&hash_cur->hlist);
synchronize_rcu();
free_packagelist_entry(hash_cur);
free_hashtable_entry(hash_cur);
break;
}
}
@ -357,7 +430,7 @@ static void packagelist_destroy(void)
}
synchronize_rcu();
hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist)
free_packagelist_entry(hash_cur);
free_hashtable_entry(hash_cur);
mutex_unlock(&sdcardfs_super_list_lock);
printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld\n");
}
@ -502,6 +575,127 @@ static struct config_item_type package_appid_type = {
.ct_owner = THIS_MODULE,
};
struct extensions_value {
struct config_group group;
unsigned int num;
};
struct extension_details {
struct config_item item;
const char *name;
unsigned int num;
};
static inline struct extensions_value *to_extensions_value(struct config_item *item)
{
return item ? container_of(to_config_group(item), struct extensions_value, group) : NULL;
}
static inline struct extension_details *to_extension_details(struct config_item *item)
{
return item ? container_of(item, struct extension_details, item) : NULL;
}
static void extension_details_release(struct config_item *item)
{
struct extension_details *extension_details = to_extension_details(item);
printk(KERN_INFO "sdcardfs: No longer mapping %s files to gid %d\n",
extension_details->name, extension_details->num);
remove_ext_gid_entry(extension_details->name, extension_details->num);
kfree(extension_details->name);
kfree(extension_details);
}
static struct configfs_item_operations extension_details_item_ops = {
.release = extension_details_release,
};
static struct config_item_type extension_details_type = {
.ct_item_ops = &extension_details_item_ops,
.ct_owner = THIS_MODULE,
};
static struct config_item *extension_details_make_item(struct config_group *group, const char *name)
{
struct extensions_value *extensions_value = to_extensions_value(&group->cg_item);
struct extension_details *extension_details = kzalloc(sizeof(struct extension_details), GFP_KERNEL);
int ret;
if (!extension_details)
return ERR_PTR(-ENOMEM);
extension_details->name = kstrdup(name, GFP_KERNEL);
if (!extension_details->name) {
kfree(extension_details);
return ERR_PTR(-ENOMEM);
}
extension_details->num = extensions_value->num;
ret = insert_ext_gid_entry(name, extensions_value->num);
if (ret) {
kfree(extension_details->name);
kfree(extension_details);
return ERR_PTR(ret);
}
config_item_init_type_name(&extension_details->item, name, &extension_details_type);
return &extension_details->item;
}
static struct configfs_group_operations extensions_value_group_ops = {
.make_item = extension_details_make_item,
};
static struct config_item_type extensions_name_type = {
.ct_group_ops = &extensions_value_group_ops,
.ct_owner = THIS_MODULE,
};
static struct config_group *extensions_make_group(struct config_group *group, const char *name)
{
struct extensions_value *extensions_value;
unsigned int tmp;
int ret;
extensions_value = kzalloc(sizeof(struct extensions_value), GFP_KERNEL);
if (!extensions_value)
return ERR_PTR(-ENOMEM);
ret = kstrtouint(name, 10, &tmp);
if (ret) {
kfree(extensions_value);
return ERR_PTR(ret);
}
extensions_value->num = tmp;
config_group_init_type_name(&extensions_value->group, name,
&extensions_name_type);
return &extensions_value->group;
}
static void extensions_drop_group(struct config_group *group, struct config_item *item)
{
struct extensions_value *value = to_extensions_value(item);
printk(KERN_INFO "sdcardfs: No longer mapping any files to gid %d\n", value->num);
kfree(value);
}
static struct configfs_group_operations extensions_group_ops = {
.make_group = extensions_make_group,
.drop_item = extensions_drop_group,
};
static struct config_item_type extensions_type = {
.ct_group_ops = &extensions_group_ops,
.ct_owner = THIS_MODULE,
};
struct config_group extension_group = {
.cg_item = {
.ci_namebuf = "extensions",
.ci_type = &extensions_type,
},
};
static struct config_item *packages_make_item(struct config_group *group, const char *name)
{
struct package_details *package_details;
@ -595,6 +789,11 @@ static struct config_item_type packages_type = {
.ct_owner = THIS_MODULE,
};
struct config_group *sd_default_groups[] = {
&extension_group,
NULL,
};
static struct configfs_subsystem sdcardfs_packages = {
.su_group = {
.cg_item = {
@ -606,10 +805,14 @@ static struct configfs_subsystem sdcardfs_packages = {
static int configfs_sdcardfs_init(void)
{
int ret;
int ret, i;
struct configfs_subsystem *subsys = &sdcardfs_packages;
config_group_init(&subsys->su_group);
for (i = 0; sd_default_groups[i]; i++) {
config_group_init(sd_default_groups[i]);
configfs_add_default_group(sd_default_groups[i], &subsys->su_group);
}
mutex_init(&subsys->su_mutex);
ret = configfs_register_subsystem(subsys);
if (ret) {

View file

@ -65,6 +65,9 @@
#define AID_SDCARD_PICS 1033 /* external storage photos access */
#define AID_SDCARD_AV 1034 /* external storage audio/video access */
#define AID_SDCARD_ALL 1035 /* access all users external storage */
#define AID_MEDIA_OBB 1059 /* obb files */
#define AID_SDCARD_IMAGE 1057
#define AID_PACKAGE_INFO 1027
@ -91,13 +94,19 @@
* These two macro should be used in pair, and OVERRIDE_CRED() should be
* placed at the beginning of a function, right after variable declaration.
*/
#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred) \
saved_cred = override_fsids(sdcardfs_sbi); \
if (!saved_cred) { return -ENOMEM; }
#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred, info) \
do { \
saved_cred = override_fsids(sdcardfs_sbi, info); \
if (!saved_cred) \
return -ENOMEM; \
} while (0)
#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred) \
saved_cred = override_fsids(sdcardfs_sbi); \
if (!saved_cred) { return ERR_PTR(-ENOMEM); }
#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred, info) \
do { \
saved_cred = override_fsids(sdcardfs_sbi, info); \
if (!saved_cred) \
return ERR_PTR(-ENOMEM); \
} while (0)
#define REVERT_CRED(saved_cred) revert_fsids(saved_cred)
@ -127,13 +136,18 @@ typedef enum {
PERM_ANDROID_OBB,
/* This node is "/Android/media" */
PERM_ANDROID_MEDIA,
/* This node is "/Android/[data|media|obb]/[package]" */
PERM_ANDROID_PACKAGE,
/* This node is "/Android/[data|media|obb]/[package]/cache" */
PERM_ANDROID_PACKAGE_CACHE,
} perm_t;
struct sdcardfs_sb_info;
struct sdcardfs_mount_options;
struct sdcardfs_inode_info;
/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
const struct cred * override_fsids(struct sdcardfs_sb_info* sbi);
const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_info *info);
/* Do not directly use this function, use REVERT_CRED() instead. */
void revert_fsids(const struct cred * old_cred);
@ -175,6 +189,8 @@ struct sdcardfs_inode_info {
userid_t userid;
uid_t d_uid;
bool under_android;
bool under_cache;
bool under_obb;
/* top folder for ownership */
struct inode *top;
@ -447,6 +463,7 @@ extern struct list_head sdcardfs_super_list;
/* for packagelist.c */
extern appid_t get_appid(const char *app_name);
extern appid_t get_ext_gid(const char *app_name);
extern appid_t is_excluded(const char *app_name, userid_t userid);
extern int check_caller_access_to_name(struct inode *parent_node, const char* name);
extern int open_flags_to_access_mode(int open_flags);
@ -466,11 +483,13 @@ struct limit_search {
extern void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid,
uid_t uid, bool under_android, struct inode *top);
extern void get_derived_permission(struct dentry *parent, struct dentry *dentry);
extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry);
extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const char *name);
extern void drop_recursive(struct dentry *parent);
extern void fixup_top_recursive(struct dentry *parent);
extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit);
extern void update_derived_permission_lock(struct dentry *dentry);
void fixup_lower_ownership(struct dentry *dentry, const char *name);
extern int need_graft_path(struct dentry *dentry);
extern int is_base_obbpath(struct dentry *dentry);
extern int is_obbpath_invalid(struct dentry *dentry);