ANDROID: sdcardfs: Add GID Derivation to sdcardfs
authorDaniel Rosenberg <drosen@google.com>
Wed, 25 Jan 2017 21:48:45 +0000 (13:48 -0800)
committerAmit Pundir <amit.pundir@linaro.org>
Fri, 3 Feb 2017 09:35:18 +0000 (15:05 +0530)
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]

Bug: 34262585
Change-Id: I4e030ce84f094a678376349b1a96923e5076a0f4
Signed-off-by: Daniel Rosenberg <drosen@google.com>
fs/sdcardfs/derived_perm.c
fs/sdcardfs/file.c
fs/sdcardfs/inode.c
fs/sdcardfs/lookup.c
fs/sdcardfs/multiuser.h
fs/sdcardfs/packagelist.c
fs/sdcardfs/sdcardfs.h

index 8e3baee4a2d905170979032ec870ca693dfd34f6..d2bff5ecdad02c208eba93be567b88aaf2c1bb4c 100644 (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,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,152 @@ 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;
+               mutex_lock(&inode->i_mutex);
+               error = security_path_chown(&path, newattrs.ia_uid, newattrs.ia_gid);
+               if (!error)
+                       error = notify_change2(path.mnt, path.dentry, &newattrs, &delegated_inode);
+               mutex_unlock(&inode->i_mutex);
+               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) {
@@ -167,9 +291,28 @@ void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit) {
        dput(dentry);
 }
 
+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));
index 7750a0472389bef33da10d9db11f91c7430b81dd..006c6ff57ad777da71b1b09cf61904fac9ee8f3f 100644 (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);
index a2cad0f76f14dbfed8692f8d9c52a9845bf09a38..cb0588691a0ff574116e686b7d1b4f909eaa6aa2 100644 (file)
 #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. */
@@ -479,7 +486,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);
@@ -516,11 +523,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);
@@ -750,7 +756,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;
index e94a65c8bbbd60b8f6b439e36b42fc392aeb1d7e..3c9454e5e1c6e9539393608a9ad0a6aea64a2ecd 100644 (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),
index 923ba101dfa9f1bbb4861e804c92ad1d453d85ec..52bc2008090450e236ab1c63748b255a4a2bb60d 100644 (file)
  * 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;
+    }
 }
-
index 6eb73ddc2cebe878e3e409e4e184940862b3e01b..cdab1967317b4bcd608b8c62a653e27fa61ea73c 100644 (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;
@@ -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,20 +789,28 @@ 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 = {
                        .ci_namebuf = "sdcardfs",
                        .ci_type = &packages_type,
                },
+               .default_groups = sd_default_groups,
        },
 };
 
 static int configfs_sdcardfs_init(void)
 {
-       int ret;
+       int ret, i;
        struct configfs_subsystem *subsys = &sdcardfs_packages;
-
+       for (i = 0; sd_default_groups[i]; i++) {
+               config_group_init(sd_default_groups[i]);
+       }
        config_group_init(&subsys->su_group);
        mutex_init(&subsys->su_mutex);
        ret = configfs_register_subsystem(subsys);
index 3434849cee63c9767d7b018f1bbb6ae0684c4331..03da961e3b09d269ad711aa2bb46dbc255ce05cb 100644 (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
 
  * 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);      \
+#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred, info)          \
+       saved_cred = override_fsids(sdcardfs_sbi, info);        \
        if (!saved_cred) { return -ENOMEM; }
 
-#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred)    \
-       saved_cred = override_fsids(sdcardfs_sbi);      \
+#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred, info)      \
+       saved_cred = override_fsids(sdcardfs_sbi, info);        \
        if (!saved_cred) { return ERR_PTR(-ENOMEM); }
 
 #define REVERT_CRED(saved_cred)        revert_fsids(saved_cred)
@@ -127,13 +130,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 +183,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 +457,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 +477,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);