Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0edc9bf9 authored by Daniel Rosenberg's avatar Daniel Rosenberg
Browse files

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: default avatarDaniel Rosenberg <drosen@google.com>
Bug: 34262585
Change-Id: I4e030ce84f094a678376349b1a96923e5076a0f4
parent 71c9ec24
Loading
Loading
Loading
Loading
+193 −46
Original line number Diff line number Diff line
@@ -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,26 +61,30 @@ 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:
	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(newdentry->d_name.name, NULL, 10);
		info->userid = simple_strtoul(name, NULL, 10);
		set_top(info, &info->vfs_inode);
		break;
	case PERM_ROOT:
		/* Assume masked off by default. */
			if (!strcasecmp(newdentry->d_name.name, "Android")) {
		if (!strcasecmp(name, "Android")) {
			/* App-specific directories inside; let anyone traverse */
			info->perm = PERM_ANDROID;
			info->under_android = true;
@@ -84,36 +92,154 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, st
		}
		break;
	case PERM_ANDROID:
			if (!strcasecmp(newdentry->d_name.name, "data")) {
		if (!strcasecmp(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")) {
		} 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(newdentry->d_name.name, "media")) {
		} 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_DATA:
	case PERM_ANDROID_OBB:
	case PERM_ANDROID_DATA:
	case PERM_ANDROID_MEDIA:
			appid = get_appid(newdentry->d_name.name);
			if (appid != 0 && !is_excluded(newdentry->d_name.name, parent_info->userid)) {
		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));
+1 −1
Original line number Diff line number Diff line
@@ -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);
+24 −18
Original line number Diff line number Diff line
@@ -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;
	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);
@@ -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;
+2 −1
Original line number Diff line number Diff line
@@ -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),
+23 −8
Original line number Diff line number Diff line
@@ -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;
	}
}
Loading