sdcardfs: Added top to sdcardfs_inode_info
authorDaniel Rosenberg <drosen@google.com>
Wed, 18 May 2016 23:57:10 +0000 (16:57 -0700)
committerStricted <info@stricted.net>
Thu, 11 Oct 2018 16:03:17 +0000 (18:03 +0200)
Adding packages to the package list and moving files
takes a large amount of locks, and is currently a
heavy operation. This adds a 'top' field to the
inode_info, which points to the inode for the top
most directory whose owner you would like to match.

On permission checks and get_attr, we look up the
owner based on the information at top. When we change
a package mapping, we need only modify the information
in the corresponding top inode_info's. When renaming,
we must ensure top is set correctly in all children.
This happens when an app specific folder gets moved
outside of the folder for that app.

Change-Id: Ib749c60b568e9a45a46f8ceed985c1338246ec6c
Signed-off-by: Daniel Rosenberg <drosen@google.com>
fs/sdcardfs/derived_perm.c
fs/sdcardfs/inode.c
fs/sdcardfs/main.c
fs/sdcardfs/packagelist.c
fs/sdcardfs/sdcardfs.h
fs/sdcardfs/super.c

index 68837e724af42cd4e2035935bd1fc8bae1768fcc..6be6747f617559df663f4691fd041043299bd499 100644 (file)
@@ -30,11 +30,12 @@ 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;
+       set_top(ci, pi->top);
 }
 
 /* helper function for derived state */
-void setup_derived_state(struct inode *inode, perm_t perm,
-                        userid_t userid, uid_t uid, bool under_android)
+void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid,
+                        uid_t uid, bool under_android, struct inode *top)
 {
        struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
 
@@ -42,6 +43,7 @@ void setup_derived_state(struct inode *inode, perm_t perm,
        info->userid = userid;
        info->d_uid = uid;
        info->under_android = under_android;
+       set_top(info, top);
 }
 
 /* While renaming, there is a point where we want the path from dentry, but the name from newdentry */
@@ -70,6 +72,7 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, st
                        /* Legacy internal layout places users at top level */
                        info->perm = PERM_ROOT;
                        info->userid = simple_strtoul(newdentry->d_name.name, NULL, 10);
+                       set_top(info, &info->vfs_inode);
                        break;
                case PERM_ROOT:
                        /* Assume masked off by default. */
@@ -77,19 +80,23 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, st
                                /* 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:
@@ -99,6 +106,7 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, st
                        if (appid != 0) {
                                info->d_uid = multiuser_get_uid(parent_info->userid, appid);
                        }
+                       set_top(info, &info->vfs_inode);
                        break;
        }
 }
@@ -108,14 +116,65 @@ void get_derived_permission(struct dentry *parent, struct dentry *dentry)
        get_derived_permission_new(parent, dentry, dentry);
 }
 
-void get_derive_permissions_recursive(struct dentry *parent) {
+static int descendant_may_need_fixup(perm_t perm) {
+       if (perm == PERM_PRE_ROOT || perm == PERM_ROOT || perm == PERM_ANDROID)
+               return 1;
+       return 0;
+}
+
+static int needs_fixup(perm_t perm) {
+       if (perm == PERM_ANDROID_DATA || perm == PERM_ANDROID_OBB
+                       || perm == PERM_ANDROID_MEDIA)
+               return 1;
+       return 0;
+}
+
+void fixup_perms_recursive(struct dentry *dentry, const char* name, size_t len) {
+       struct dentry *child;
+       struct sdcardfs_inode_info *info;
+       if (!dget(dentry))
+               return;
+       if (!dentry->d_inode) {
+               dput(dentry);
+               return;
+       }
+       info = SDCARDFS_I(dentry->d_inode);
+
+       if (needs_fixup(info->perm)) {
+               mutex_lock(&dentry->d_inode->i_mutex);
+               child = lookup_one_len(name, dentry, len);
+               mutex_unlock(&dentry->d_inode->i_mutex);
+               if (!IS_ERR(child)) {
+                       if (child->d_inode) {
+                               get_derived_permission(dentry, child);
+                               fix_derived_permission(child->d_inode);
+                       }
+                       dput(child);
+               }
+       } else  if (descendant_may_need_fixup(info->perm)) {
+               mutex_lock(&dentry->d_inode->i_mutex);
+               list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child) {
+                               fixup_perms_recursive(child, name, len);
+               }
+               mutex_unlock(&dentry->d_inode->i_mutex);
+       }
+       dput(dentry);
+}
+
+void fixup_top_recursive(struct dentry *parent) {
        struct dentry *dentry;
+       struct sdcardfs_inode_info *info;
+       if (!parent->d_inode)
+               return;
+       info = SDCARDFS_I(parent->d_inode);
        spin_lock(&parent->d_lock);
        list_for_each_entry(dentry, &parent->d_subdirs, d_u.d_child) {
                if (dentry->d_inode) {
-                       get_derived_permission(parent, dentry);
-                       fix_derived_permission(dentry->d_inode);
-                       get_derive_permissions_recursive(dentry);
+                       if (SDCARDFS_I(parent->d_inode)->top != SDCARDFS_I(dentry->d_inode)->top) {
+                               get_derived_permission(parent, dentry);
+                               fix_derived_permission(dentry->d_inode);
+                               fixup_top_recursive(dentry);
+                       }
                }
        }
        spin_unlock(&parent->d_lock);
index a61e8c8217960c1028c1fe53ba57accc56b961bc..9842a92856a36f0e48a6aa4c3e790fb421824ffe 100644 (file)
@@ -514,7 +514,7 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry,
         * we pass along new_dentry for the name.*/
        get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry);
        fix_derived_permission(old_dentry->d_inode);
-       get_derive_permissions_recursive(old_dentry);
+       fixup_top_recursive(old_dentry);
 out:
        unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
        dput(lower_old_dir_dentry);
@@ -599,6 +599,16 @@ static void sdcardfs_put_link(struct dentry *dentry, struct nameidata *nd,
 static int sdcardfs_permission(struct inode *inode, int mask)
 {
        int err;
+       struct inode *top = grab_top(SDCARDFS_I(inode));
+
+       if (!top)
+               return -EINVAL;
+       /* Ensure owner is up to date */
+       if (!uid_eq(inode->i_uid, top->i_uid)) {
+               SDCARDFS_I(inode)->d_uid = SDCARDFS_I(top)->d_uid;
+               fix_derived_permission(inode);
+       }
+       release_top(SDCARDFS_I(inode));
 
        /*
         * Permission check on sdcardfs inode.
@@ -634,41 +644,6 @@ static int sdcardfs_permission(struct inode *inode, int mask)
 
 }
 
-static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
-                struct kstat *stat)
-{
-       struct dentry *lower_dentry;
-       struct inode *inode;
-       struct inode *lower_inode;
-       struct path lower_path;
-       struct dentry *parent;
-
-       parent = dget_parent(dentry);
-       if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) {
-               printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
-                                                "  dentry: %s, task:%s\n",
-                                                __func__, dentry->d_name.name, current->comm);
-               dput(parent);
-               return -EACCES;
-       }
-       dput(parent);
-
-       inode = dentry->d_inode;
-
-       sdcardfs_get_lower_path(dentry, &lower_path);
-       lower_dentry = lower_path.dentry;
-       lower_inode = sdcardfs_lower_inode(inode);
-
-
-       sdcardfs_copy_and_fix_attrs(inode, lower_inode);
-       fsstack_copy_inode_size(inode, lower_inode);
-
-
-       generic_fillattr(inode, stat);
-       sdcardfs_put_lower_path(dentry, &lower_path);
-       return 0;
-}
-
 static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia)
 {
        int err = 0;
@@ -771,6 +746,64 @@ out_err:
        return err;
 }
 
+static int sdcardfs_fillattr(struct inode *inode, struct kstat *stat)
+{
+       struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
+       struct inode *top = grab_top(info);
+       if (!top)
+               return -EINVAL;
+
+       stat->dev = inode->i_sb->s_dev;
+       stat->ino = inode->i_ino;
+       stat->mode = (inode->i_mode  & S_IFMT) | get_mode(SDCARDFS_I(top));
+       stat->nlink = inode->i_nlink;
+       stat->uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid);
+       stat->gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(top)));
+       stat->rdev = inode->i_rdev;
+       stat->size = i_size_read(inode);
+       stat->atime = inode->i_atime;
+       stat->mtime = inode->i_mtime;
+       stat->ctime = inode->i_ctime;
+       stat->blksize = (1 << inode->i_blkbits);
+       stat->blocks = inode->i_blocks;
+       release_top(info);
+       return 0;
+}
+
+static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
+                struct kstat *stat)
+{
+       struct dentry *lower_dentry;
+       struct inode *inode;
+       struct inode *lower_inode;
+       struct path lower_path;
+       struct dentry *parent;
+       int err;
+
+       parent = dget_parent(dentry);
+       if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) {
+               printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
+                                                "  dentry: %s, task:%s\n",
+                                                __func__, dentry->d_name.name, current->comm);
+               dput(parent);
+               return -EACCES;
+       }
+       dput(parent);
+
+       inode = dentry->d_inode;
+
+       sdcardfs_get_lower_path(dentry, &lower_path);
+       lower_dentry = lower_path.dentry;
+       lower_inode = sdcardfs_lower_inode(inode);
+
+       sdcardfs_copy_and_fix_attrs(inode, lower_inode);
+       fsstack_copy_inode_size(inode, lower_inode);
+
+       err = sdcardfs_fillattr(inode, stat);
+       sdcardfs_put_lower_path(dentry, &lower_path);
+       return err;
+}
+
 const struct inode_operations sdcardfs_symlink_iops = {
        .permission     = sdcardfs_permission,
        .setattr        = sdcardfs_setattr,
@@ -786,9 +819,7 @@ const struct inode_operations sdcardfs_symlink_iops = {
 const struct inode_operations sdcardfs_dir_iops = {
        .create         = sdcardfs_create,
        .lookup         = sdcardfs_lookup,
-#if 0
        .permission     = sdcardfs_permission,
-#endif
        .unlink         = sdcardfs_unlink,
        .mkdir          = sdcardfs_mkdir,
        .rmdir          = sdcardfs_rmdir,
index 7d6a17324267b4f69065c4d1e626c3eec9b26a0f..db681fb2411e9d8a74c047199a8bca24d873c639 100644 (file)
@@ -268,13 +268,13 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name,
        sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL);
        mutex_lock(&sdcardfs_super_list_lock);
        if(sb_info->options.multiuser) {
-               setup_derived_state(sb->s_root->d_inode, PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false);
+               setup_derived_state(sb->s_root->d_inode, PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, sb->s_root->d_inode);
                snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name);
                /*err =  prepare_dir(sb_info->obbpath_s,
                                        sb_info->options.fs_low_uid,
                                        sb_info->options.fs_low_gid, 00664);*/
        } else {
-               setup_derived_state(sb->s_root->d_inode, PERM_ROOT, sb_info->options.fs_low_uid, AID_ROOT, false);
+               setup_derived_state(sb->s_root->d_inode, PERM_ROOT, sb_info->options.fs_low_uid, AID_ROOT, false, sb->s_root->d_inode);
                snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name);
        }
        fix_derived_permission(sb->s_root->d_inode);
index 608a4b50ccd82a5d78e64a362062332243110da9..dac5d0f7d2dec0517ba167a8a485ecc40c4a9bfc 100644 (file)
@@ -143,18 +143,18 @@ static int insert_packagelist_entry_locked(const char *key, appid_t value)
        return 0;
 }
 
-static void fixup_perms(struct super_block *sb) {
+static void fixup_perms(struct super_block *sb, const char *key) {
        if (sb && sb->s_magic == SDCARDFS_SUPER_MAGIC) {
-               get_derive_permissions_recursive(sb->s_root);
+               fixup_perms_recursive(sb->s_root, key, strlen(key));
        }
 }
 
-static void fixup_all_perms(void)
+static void fixup_all_perms(const char *key)
 {
        struct sdcardfs_sb_info *sbinfo;
        list_for_each_entry(sbinfo, &sdcardfs_super_list, list)
                if (sbinfo)
-                       fixup_perms(sbinfo->sb);
+                       fixup_perms(sbinfo->sb, key);
 }
 
 static int insert_packagelist_entry(const char *key, appid_t value)
@@ -164,7 +164,7 @@ static int insert_packagelist_entry(const char *key, appid_t value)
        mutex_lock(&sdcardfs_super_list_lock);
        err = insert_packagelist_entry_locked(key, value);
        if (!err)
-               fixup_all_perms();
+               fixup_all_perms(key);
        mutex_unlock(&sdcardfs_super_list_lock);
 
        return err;
@@ -196,7 +196,7 @@ static void remove_packagelist_entry(const char *key)
 {
        mutex_lock(&sdcardfs_super_list_lock);
        remove_packagelist_entry_locked(key);
-       fixup_all_perms();
+       fixup_all_perms(key);
        mutex_unlock(&sdcardfs_super_list_lock);
        return;
 }
index 48de31b5a53c985ad2476c7b0575da9ae4fd3ed8..00b8f7435e62daf9b5b3d6ad004d51a61ce5afc2 100644 (file)
@@ -169,6 +169,8 @@ struct sdcardfs_inode_info {
        userid_t userid;
        uid_t d_uid;
        bool under_android;
+       /* top folder for ownership */
+       struct inode *top;
 
        struct inode vfs_inode;
 };
@@ -321,6 +323,35 @@ static inline void sdcardfs_put_reset_##pname(const struct dentry *dent) \
 SDCARDFS_DENT_FUNC(lower_path)
 SDCARDFS_DENT_FUNC(orig_path)
 
+/* grab a refererence if we aren't linking to ourself */
+static inline void set_top(struct sdcardfs_inode_info *info, struct inode *top)
+{
+       struct inode *old_top = NULL;
+       BUG_ON(IS_ERR_OR_NULL(top));
+       if (info->top && info->top != &info->vfs_inode) {
+               old_top = info->top;
+       }
+       if (top != &info->vfs_inode)
+               igrab(top);
+       info->top = top;
+       iput(old_top);
+}
+
+static inline struct inode *grab_top(struct sdcardfs_inode_info *info)
+{
+       struct inode *top = info->top;
+       if (top) {
+               return igrab(top);
+       } else {
+               return NULL;
+       }
+}
+
+static inline void release_top(struct sdcardfs_inode_info *info)
+{
+       iput(info->top);
+}
+
 static inline int get_gid(struct sdcardfs_inode_info *info) {
        struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb);
        if (sb_info->options.gid == AID_SDCARD_RW) {
@@ -337,7 +368,7 @@ static inline int get_gid(struct sdcardfs_inode_info *info) {
 static inline int get_mode(struct sdcardfs_inode_info *info) {
        int owner_mode;
        int filtered_mode;
-       struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb);
+       struct sdcardfs_sb_info * sb_info = SDCARDFS_SB(info->vfs_inode.i_sb);
        int visible_mode = 0775 & ~sb_info->options.mask;
 
        if (info->perm == PERM_PRE_ROOT) {
@@ -403,11 +434,12 @@ extern int packagelist_init(void);
 extern void packagelist_exit(void);
 
 /* for derived_perm.c */
-extern void setup_derived_state(struct inode *inode, perm_t perm,
-                       userid_t userid, uid_t uid, bool under_android);
+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_derive_permissions_recursive(struct dentry *parent);
+extern void fixup_top_recursive(struct dentry *parent);
+extern void fixup_perms_recursive(struct dentry *dentry, const char *name, size_t len);
 
 extern void update_derived_permission_lock(struct dentry *dentry);
 extern int need_graft_path(struct dentry *dentry);
index 1d6490128c9905f36eba30e0e896a4634ee5dc32..0a465395aab7f7f83fb4d4a4230d624df1448f85 100644 (file)
@@ -126,6 +126,7 @@ static void sdcardfs_evict_inode(struct inode *inode)
         */
        lower_inode = sdcardfs_lower_inode(inode);
        sdcardfs_set_lower_inode(inode, NULL);
+       set_top(SDCARDFS_I(inode), inode);
        iput(lower_inode);
 }