sdcardfs: Bring up to date with Android M permissions:

In M, the workings of sdcardfs were changed significantly.
This brings sdcardfs into line with the changes.

Change-Id: I10e91a84a884c838feef7aa26c0a2b21f02e052e
diff --git a/fs/sdcardfs/Kconfig b/fs/sdcardfs/Kconfig
index d995f3e..ab25f88 100644
--- a/fs/sdcardfs/Kconfig
+++ b/fs/sdcardfs/Kconfig
@@ -1,5 +1,6 @@
 config SDCARD_FS
 	tristate "sdcard file system"
+	depends on CONFIGFS_FS
 	default n
 	help
 	  Sdcardfs is based on Wrapfs file system.
diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c
index 00c33a4..128b3e5 100644
--- a/fs/sdcardfs/derived_perm.c
+++ b/fs/sdcardfs/derived_perm.c
@@ -29,24 +29,23 @@
 	ci->perm = PERM_INHERIT;
 	ci->userid = pi->userid;
 	ci->d_uid = pi->d_uid;
-	ci->d_gid = pi->d_gid;
-	ci->d_mode = pi->d_mode;
+	ci->under_android = pi->under_android;
 }
 
 /* helper function for derived state */
 void setup_derived_state(struct inode *inode, perm_t perm,
-                        userid_t userid, uid_t uid, gid_t gid, mode_t mode)
+                        userid_t userid, uid_t uid, bool under_android)
 {
 	struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
 
 	info->perm = perm;
 	info->userid = userid;
 	info->d_uid = uid;
-	info->d_gid = gid;
-	info->d_mode = mode;
+	info->under_android = under_android;
 }
 
-void get_derived_permission(struct dentry *parent, struct dentry *dentry)
+/* 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)
 {
 	struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
 	struct sdcardfs_inode_info *info = SDCARDFS_I(dentry->d_inode);
@@ -63,86 +62,68 @@
 
 	inherit_derived_state(parent->d_inode, dentry->d_inode);
 
-	//printk(KERN_INFO "sdcardfs: derived: %s, %s, %d\n", parent->d_name.name,
-	//				dentry->d_name.name, parent_info->perm);
-
-	if (sbi->options.derive == DERIVE_NONE) {
-		return;
-	}
-
 	/* Derive custom permissions based on parent and current node */
 	switch (parent_info->perm) {
 		case PERM_INHERIT:
 			/* Already inherited above */
 			break;
-		case PERM_LEGACY_PRE_ROOT:
+		case PERM_PRE_ROOT:
 			/* Legacy internal layout places users at top level */
 			info->perm = PERM_ROOT;
-			info->userid = simple_strtoul(dentry->d_name.name, NULL, 10);
+			info->userid = simple_strtoul(newdentry->d_name.name, NULL, 10);
 			break;
 		case PERM_ROOT:
 			/* Assume masked off by default. */
-			info->d_mode = 00770;
-			if (!strcasecmp(dentry->d_name.name, "Android")) {
+			if (!strcasecmp(newdentry->d_name.name, "Android")) {
 				/* App-specific directories inside; let anyone traverse */
 				info->perm = PERM_ANDROID;
-				info->d_mode = 00771;
-			} else if (sbi->options.split_perms) {
-				if (!strcasecmp(dentry->d_name.name, "DCIM")
-					|| !strcasecmp(dentry->d_name.name, "Pictures")) {
-					info->d_gid = AID_SDCARD_PICS;
-				} else if (!strcasecmp(dentry->d_name.name, "Alarms")
-						|| !strcasecmp(dentry->d_name.name, "Movies")
-						|| !strcasecmp(dentry->d_name.name, "Music")
-						|| !strcasecmp(dentry->d_name.name, "Notifications")
-						|| !strcasecmp(dentry->d_name.name, "Podcasts")
-						|| !strcasecmp(dentry->d_name.name, "Ringtones")) {
-					info->d_gid = AID_SDCARD_AV;
-				}
+				info->under_android = true;
 			}
 			break;
 		case PERM_ANDROID:
-			if (!strcasecmp(dentry->d_name.name, "data")) {
+			if (!strcasecmp(newdentry->d_name.name, "data")) {
 				/* App-specific directories inside; let anyone traverse */
 				info->perm = PERM_ANDROID_DATA;
-				info->d_mode = 00771;
-			} else if (!strcasecmp(dentry->d_name.name, "obb")) {
+			} else if (!strcasecmp(newdentry->d_name.name, "obb")) {
 				/* App-specific directories inside; let anyone traverse */
 				info->perm = PERM_ANDROID_OBB;
-				info->d_mode = 00771;
-				// FIXME : this feature will be implemented later.
 				/* Single OBB directory is always shared */
-			} else if (!strcasecmp(dentry->d_name.name, "user")) {
-				/* User directories must only be accessible to system, protected
-				 * by sdcard_all. Zygote will bind mount the appropriate user-
-				 * specific path. */
-				info->perm = PERM_ANDROID_USER;
-				info->d_gid = AID_SDCARD_ALL;
-				info->d_mode = 00770;
+			} else if (!strcasecmp(newdentry->d_name.name, "media")) {
+				/* App-specific directories inside; let anyone traverse */
+				info->perm = PERM_ANDROID_MEDIA;
 			}
 			break;
-		/* same policy will be applied on PERM_ANDROID_DATA
-		 * and PERM_ANDROID_OBB */
 		case PERM_ANDROID_DATA:
 		case PERM_ANDROID_OBB:
-			appid = get_appid(sbi->pkgl_id, dentry->d_name.name);
+		case PERM_ANDROID_MEDIA:
+			appid = get_appid(sbi->pkgl_id, newdentry->d_name.name);
 			if (appid != 0) {
 				info->d_uid = multiuser_get_uid(parent_info->userid, appid);
 			}
-			info->d_mode = 00770;
-			break;
-		case PERM_ANDROID_USER:
-			/* Root of a secondary user */
-			info->perm = PERM_ROOT;
-			info->userid = simple_strtoul(dentry->d_name.name, NULL, 10);
-			info->d_gid = AID_SDCARD_R;
-			info->d_mode = 00771;
 			break;
 	}
 }
 
+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) {
+	struct dentry *dentry;
+	list_for_each_entry(dentry, &parent->d_subdirs, d_child) {
+		if (dentry && dentry->d_inode) {
+			mutex_lock(&dentry->d_inode->i_mutex);
+			get_derived_permission(parent, dentry);
+			fix_derived_permission(dentry->d_inode);
+			get_derive_permissions_recursive(dentry);
+			mutex_unlock(&dentry->d_inode->i_mutex);
+		}
+	}
+}
+
 /* main function for updating derived permission */
-inline void update_derived_permission(struct dentry *dentry)
+inline void update_derived_permission_lock(struct dentry *dentry)
 {
 	struct dentry *parent;
 
@@ -154,6 +135,7 @@
 	 * 1. need to check whether the dentry is updated or not
 	 * 2. remove the root dentry update
 	 */
+	mutex_lock(&dentry->d_inode->i_mutex);
 	if(IS_ROOT(dentry)) {
 		//setup_default_pre_root_state(dentry->d_inode);
 	} else {
@@ -164,6 +146,7 @@
 		}
 	}
 	fix_derived_permission(dentry->d_inode);
+	mutex_unlock(&dentry->d_inode->i_mutex);
 }
 
 int need_graft_path(struct dentry *dentry)
@@ -177,7 +160,7 @@
 			!strcasecmp(dentry->d_name.name, "obb")) {
 
 		/* /Android/obb is the base obbpath of DERIVED_UNIFIED */
-		if(!(sbi->options.derive == DERIVE_UNIFIED
+		if(!(sbi->options.multiuser == false
 				&& parent_info->userid == 0)) {
 			ret = 1;
 		}
@@ -207,8 +190,7 @@
 			path_buf = kmalloc(PATH_MAX, GFP_ATOMIC);
 			if(!path_buf) {
 				ret = 1;
-				printk(KERN_ERR "sdcardfs: "
-					"fail to allocate path_buf in %s.\n", __func__);
+				printk(KERN_ERR "sdcardfs: fail to allocate path_buf in %s.\n", __func__);
 			} else {
 				obbpath_s = d_path(&di->lower_path, path_buf, PATH_MAX);
 				if (d_unhashed(di->lower_path.dentry) ||
@@ -234,21 +216,16 @@
 	struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
 
 	spin_lock(&SDCARDFS_D(dentry)->lock);
-	/* DERIVED_LEGACY */
-	if(parent_info->perm == PERM_LEGACY_PRE_ROOT &&
+	if (sbi->options.multiuser) {
+		if(parent_info->perm == PERM_PRE_ROOT &&
+				!strcasecmp(dentry->d_name.name, "obb")) {
+			ret = 1;
+		}
+	} else  if (parent_info->perm == PERM_ANDROID &&
 			!strcasecmp(dentry->d_name.name, "obb")) {
 		ret = 1;
 	}
-	/* DERIVED_UNIFIED :/Android/obb is the base obbpath */
-	else if (parent_info->perm == PERM_ANDROID &&
-			!strcasecmp(dentry->d_name.name, "obb")) {
-		if((sbi->options.derive == DERIVE_UNIFIED
-				&& parent_info->userid == 0)) {
-			ret = 1;
-		}
-	}
 	spin_unlock(&SDCARDFS_D(dentry)->lock);
-	dput(parent);
 	return ret;
 }
 
@@ -272,8 +249,7 @@
 
 	if(!err) {
 		/* the obbpath base has been found */
-		printk(KERN_INFO "sdcardfs: "
-				"the sbi->obbpath is found\n");
+		printk(KERN_INFO "sdcardfs: the sbi->obbpath is found\n");
 		pathcpy(lower_path, &obbpath);
 	} else {
 		/* if the sbi->obbpath is not available, we can optionally
@@ -281,8 +257,7 @@
 		 * but, the current implementation just returns an error
 		 * because the sdcard daemon also regards this case as
 		 * a lookup fail. */
-		printk(KERN_INFO "sdcardfs: "
-				"the sbi->obbpath is not available\n");
+		printk(KERN_INFO "sdcardfs: the sbi->obbpath is not available\n");
 	}
 	return err;
 }
diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c
index f9c5eaa..c249fa9 100644
--- a/fs/sdcardfs/file.c
+++ b/fs/sdcardfs/file.c
@@ -209,7 +209,6 @@
 	struct dentry *parent = dget_parent(dentry);
 	struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
 	const struct cred *saved_cred = NULL;
-	int has_rw;
 
 	/* don't open unhashed/deleted files */
 	if (d_unhashed(dentry)) {
@@ -217,11 +216,7 @@
 		goto out_err;
 	}
 
-	has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive);
-
-	if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name,
-				sbi->options.derive,
-				open_flags_to_access_mode(file->f_flags), has_rw)) {
+	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);
@@ -257,8 +252,7 @@
 	if (err)
 		kfree(SDCARDFS_F(file));
 	else {
-		fsstack_copy_attr_all(inode, sdcardfs_lower_inode(inode));
-		fix_derived_permission(inode);
+		sdcardfs_copy_and_fix_attrs(inode, sdcardfs_lower_inode(inode));
 	}
 
 out_revert_cred:
diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c
index 75c622b..2528da0 100644
--- a/fs/sdcardfs/inode.c
+++ b/fs/sdcardfs/inode.c
@@ -55,11 +55,9 @@
 	struct dentry *lower_dentry;
 	struct dentry *lower_parent_dentry = NULL;
 	struct path lower_path;
-	struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
 	const struct cred *saved_cred = NULL;
 
-	int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive);
-	if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) {
+	if(!check_caller_access_to_name(dir, 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);
@@ -80,7 +78,7 @@
 	if (err)
 		goto out;
 
-	err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path);
+	err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, SDCARDFS_I(dir)->userid);
 	if (err)
 		goto out;
 	fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
@@ -143,11 +141,9 @@
 	struct inode *lower_dir_inode = sdcardfs_lower_inode(dir);
 	struct dentry *lower_dir_dentry;
 	struct path lower_path;
-	struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
 	const struct cred *saved_cred = NULL;
 
-	int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive);
-	if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) {
+	if(!check_caller_access_to_name(dir, 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);
@@ -255,8 +251,7 @@
 	int fullpath_namelen;
 	int touch_err = 0;
 
-	int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive);
-	if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) {
+	if(!check_caller_access_to_name(dir, 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);
@@ -293,19 +288,19 @@
 		if(err) {
 			/* if the sbi->obbpath is not available, the lower_path won't be
 			 * changed by setup_obb_dentry() but the lower path is saved to
-             * its orig_path. this dentry will be revalidated later.
+			 * its orig_path. this dentry will be revalidated later.
 			 * but now, the lower_path should be NULL */
 			sdcardfs_put_reset_lower_path(dentry);
 
 			/* the newly created lower path which saved to its orig_path or
 			 * the lower_path is the base obbpath.
-             * therefore, an additional path_get is required */
+			 * therefore, an additional path_get is required */
 			path_get(&lower_path);
 		} else
 			make_nomedia_in_obb = 1;
 	}
 
-	err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path);
+	err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pi->userid);
 	if (err)
 		goto out;
 
@@ -314,7 +309,7 @@
 	/* update number of links on parent directory */
 	set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink);
 
-	if ((sbi->options.derive == DERIVE_UNIFIED) && (!strcasecmp(dentry->d_name.name, "obb"))
+	if ((!sbi->options.multiuser) && (!strcasecmp(dentry->d_name.name, "obb"))
 		&& (pi->perm == PERM_ANDROID) && (pi->userid == 0))
 		make_nomedia_in_obb = 1;
 
@@ -371,12 +366,9 @@
 	struct dentry *lower_dir_dentry;
 	int err;
 	struct path lower_path;
-	struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
 	const struct cred *saved_cred = NULL;
-	//char *path_s = NULL;
 
-	int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive);
-	if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) {
+	if(!check_caller_access_to_name(dir, 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);
@@ -461,14 +453,10 @@
 	struct dentry *trap = NULL;
 	struct dentry *new_parent = NULL;
 	struct path lower_old_path, lower_new_path;
-	struct sdcardfs_sb_info *sbi = SDCARDFS_SB(old_dentry->d_sb);
 	const struct cred *saved_cred = NULL;
 
-	int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive);
-	if(!check_caller_access_to_name(old_dir, old_dentry->d_name.name,
-			sbi->options.derive, 1, has_rw) ||
-		!check_caller_access_to_name(new_dir, new_dentry->d_name.name,
-			sbi->options.derive, 1, has_rw)) {
+	if(!check_caller_access_to_name(old_dir, old_dentry->d_name.name) ||
+		!check_caller_access_to_name(new_dir, new_dentry->d_name.name)) {
 		printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
 						 "  new_dentry: %s, task:%s\n",
 						 __func__, new_dentry->d_name.name, current->comm);
@@ -505,26 +493,31 @@
 		goto out;
 
 	/* Copy attrs from lower dir, but i_uid/i_gid */
-	fsstack_copy_attr_all(new_dir, d_inode(lower_new_dir_dentry));
+	sdcardfs_copy_and_fix_attrs(new_dir, d_inode(lower_new_dir_dentry));
 	fsstack_copy_inode_size(new_dir, d_inode(lower_new_dir_dentry));
-	fix_derived_permission(new_dir);
+
 	if (new_dir != old_dir) {
-		fsstack_copy_attr_all(old_dir, d_inode(lower_old_dir_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));
-		fix_derived_permission(old_dir);
+
 		/* update the derived permission of the old_dentry
 		 * with its new parent
 		 */
 		new_parent = dget_parent(new_dentry);
 		if(new_parent) {
 			if(d_inode(old_dentry)) {
-				get_derived_permission(new_parent, old_dentry);
-				fix_derived_permission(d_inode(old_dentry));
+				update_derived_permission_lock(old_dentry);
 			}
 			dput(new_parent);
 		}
 	}
-
+	/* At this point, not all dentry information has been moved, so
+	 * we pass along new_dentry for the name.*/
+	mutex_lock(&d_inode(old_dentry)->i_mutex);
+	get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry);
+	fix_derived_permission(d_inode(old_dentry));
+	get_derive_permissions_recursive(old_dentry);
+	mutex_unlock(&d_inode(old_dentry)->i_mutex);
 out:
 	unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
 	dput(lower_old_dir_dentry);
@@ -639,9 +632,7 @@
 	struct inode *lower_inode;
 	struct path lower_path;
 	struct iattr lower_ia;
-	struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
 	struct dentry *parent;
-	int has_rw;
 
 	inode = d_inode(dentry);
 
@@ -655,10 +646,8 @@
 	/* no vfs_XXX operations required, cred overriding will be skipped. wj*/
 	if (!err) {
 		/* check the Android group ID */
-		has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive);
 		parent = dget_parent(dentry);
-		if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name,
-						sbi->options.derive, 1, has_rw)) {
+		if(!check_caller_access_to_name(d_inode(parent), 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);
@@ -723,10 +712,8 @@
 	if (err)
 		goto out;
 
-	/* get attributes from the lower inode */
-	fsstack_copy_attr_all(inode, lower_inode);
-	/* update derived permission of the upper inode */
-	fix_derived_permission(inode);
+	/* get attributes from the lower inode and update derived permissions */
+	sdcardfs_copy_and_fix_attrs(inode, lower_inode);
 
 	/*
 	 * Not running fsstack_copy_inode_size(inode, lower_inode), because
@@ -748,11 +735,9 @@
 	struct inode *lower_inode;
 	struct path lower_path;
 	struct dentry *parent;
-	struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
 
 	parent = dget_parent(dentry);
-	if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name,
-						sbi->options.derive, 0, 0)) {
+	if(!check_caller_access_to_name(d_inode(parent), 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);
@@ -767,13 +752,10 @@
 	lower_dentry = lower_path.dentry;
 	lower_inode = sdcardfs_lower_inode(inode);
 
-	fsstack_copy_attr_all(inode, lower_inode);
+
+	sdcardfs_copy_and_fix_attrs(inode, lower_inode);
 	fsstack_copy_inode_size(inode, lower_inode);
-	/* if the dentry has been moved from other location
-	 * so, on this stage, its derived permission must be
-	 * rechecked from its private field.
-	 */
-	fix_derived_permission(inode);
+
 
 	generic_fillattr(inode, stat);
 	sdcardfs_put_lower_path(dentry, &lower_path);
diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c
index a4b94df..f80abcb 100644
--- a/fs/sdcardfs/lookup.c
+++ b/fs/sdcardfs/lookup.c
@@ -64,10 +64,17 @@
 	return 0;
 }
 
-static int sdcardfs_inode_test(struct inode *inode, void *candidate_lower_inode)
+struct inode_data {
+	struct inode *lower_inode;
+	userid_t id;
+};
+
+static int sdcardfs_inode_test(struct inode *inode, void *candidate_data/*void *candidate_lower_inode*/)
 {
 	struct inode *current_lower_inode = sdcardfs_lower_inode(inode);
-	if (current_lower_inode == (struct inode *)candidate_lower_inode)
+	userid_t current_userid = SDCARDFS_I(inode)->userid;
+	if (current_lower_inode == ((struct inode_data *)candidate_data)->lower_inode &&
+			current_userid == ((struct inode_data *)candidate_data)->id)
 		return 1; /* found a match */
 	else
 		return 0; /* no match */
@@ -79,12 +86,15 @@
 	return 0;
 }
 
-struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode)
+struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode, userid_t id)
 {
 	struct sdcardfs_inode_info *info;
+	struct inode_data data;
 	struct inode *inode; /* the new inode to return */
 	int err;
 
+	data.id = id;
+	data.lower_inode = lower_inode;
 	inode = iget5_locked(sb, /* our superblock */
 			     /*
 			      * hashval: we use inode number, but we can
@@ -94,7 +104,7 @@
 			     lower_inode->i_ino, /* hashval */
 			     sdcardfs_inode_test,	/* inode comparison function */
 			     sdcardfs_inode_set, /* inode init function */
-			     lower_inode); /* data passed to test+set fxns */
+			     &data); /* data passed to test+set fxns */
 	if (!inode) {
 		err = -EACCES;
 		iput(lower_inode);
@@ -146,11 +156,9 @@
 				   lower_inode->i_rdev);
 
 	/* all well, copy inode attributes */
-	fsstack_copy_attr_all(inode, lower_inode);
+	sdcardfs_copy_and_fix_attrs(inode, lower_inode);
 	fsstack_copy_inode_size(inode, lower_inode);
 
-	fix_derived_permission(inode);
-
 	unlock_new_inode(inode);
 	return inode;
 }
@@ -164,7 +172,7 @@
  * @lower_path: the lower path (caller does path_get/put)
  */
 int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb,
-		     struct path *lower_path)
+		     struct path *lower_path, userid_t id)
 {
 	int err = 0;
 	struct inode *inode;
@@ -186,14 +194,14 @@
 	 */
 
 	/* inherit lower inode number for sdcardfs's inode */
-	inode = sdcardfs_iget(sb, lower_inode);
+	inode = sdcardfs_iget(sb, lower_inode, id);
 	if (IS_ERR(inode)) {
 		err = PTR_ERR(inode);
 		goto out;
 	}
 
 	d_add(dentry, inode);
-	update_derived_permission(dentry);
+	update_derived_permission_lock(dentry);
 out:
 	return err;
 }
@@ -205,7 +213,7 @@
  * Fills in lower_parent_path with <dentry,mnt> on success.
  */
 static struct dentry *__sdcardfs_lookup(struct dentry *dentry,
-		unsigned int flags, struct path *lower_parent_path)
+		unsigned int flags, struct path *lower_parent_path, userid_t id)
 {
 	int err = 0;
 	struct vfsmount *lower_dir_mnt;
@@ -266,7 +274,7 @@
 		}
 
 		sdcardfs_set_lower_path(dentry, &lower_path);
-		err = sdcardfs_interpose(dentry, dentry->d_sb, &lower_path);
+		err = sdcardfs_interpose(dentry, dentry->d_sb, &lower_path, id);
 		if (err) /* path_put underlying path on error */
 			sdcardfs_put_reset_lower_path(dentry);
 		goto out;
@@ -328,13 +336,11 @@
 	struct dentry *ret = NULL, *parent;
 	struct path lower_parent_path;
 	int err = 0;
-	struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
 	const struct cred *saved_cred = NULL;
 
 	parent = dget_parent(dentry);
 
-	if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name,
-						sbi->options.derive, 0, 0)) {
+	if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) {
 		ret = ERR_PTR(-EACCES);
 		printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
                          "	dentry: %s, task:%s\n",
@@ -354,7 +360,7 @@
 		goto out;
 	}
 
-	ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path);
+	ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path, SDCARDFS_I(dir)->userid);
 	if (IS_ERR(ret))
 	{
 		goto out;
@@ -365,8 +371,10 @@
 		fsstack_copy_attr_times(dentry->d_inode,
 					sdcardfs_lower_inode(dentry->d_inode));
 		/* get drived permission */
+		mutex_lock(&dentry->d_inode->i_mutex);
 		get_derived_permission(parent, dentry);
 		fix_derived_permission(dentry->d_inode);
+		mutex_unlock(&dentry->d_inode->i_mutex);
 	}
 	/* update parent directory's atime */
 	fsstack_copy_attr_atime(parent->d_inode,
diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c
index 9d04ae8..80aa355 100644
--- a/fs/sdcardfs/main.c
+++ b/fs/sdcardfs/main.c
@@ -24,25 +24,27 @@
 #include <linux/parser.h>
 
 enum {
-	Opt_uid,
+	Opt_fsuid,
+	Opt_fsgid,
 	Opt_gid,
-	Opt_wgid,
 	Opt_debug,
-	Opt_split,
-	Opt_derive,
 	Opt_lower_fs,
+	Opt_mask,
+	Opt_multiuser, // May need?
+	Opt_userid,
 	Opt_reserved_mb,
 	Opt_err,
 };
 
 static const match_table_t sdcardfs_tokens = {
-	{Opt_uid, "uid=%u"},
+	{Opt_fsuid, "fsuid=%u"},
+	{Opt_fsgid, "fsgid=%u"},
 	{Opt_gid, "gid=%u"},
-	{Opt_wgid, "wgid=%u"},
 	{Opt_debug, "debug"},
-	{Opt_split, "split"},
-	{Opt_derive, "derive=%s"},
 	{Opt_lower_fs, "lower_fs=%s"},
+	{Opt_mask, "mask=%u"},
+	{Opt_userid, "userid=%d"},
+	{Opt_multiuser, "multiuser"},
 	{Opt_reserved_mb, "reserved_mb=%u"},
 	{Opt_err, NULL}
 };
@@ -58,12 +60,10 @@
 	/* by default, we use AID_MEDIA_RW as uid, gid */
 	opts->fs_low_uid = AID_MEDIA_RW;
 	opts->fs_low_gid = AID_MEDIA_RW;
-	/* by default, we use AID_SDCARD_RW as write_gid */
-	opts->write_gid = AID_SDCARD_RW;
-	/* default permission policy
-	 * (DERIVE_NONE | DERIVE_LEGACY | DERIVE_UNIFIED) */
-	opts->derive = DERIVE_NONE;
-	opts->split_perms = 0;
+	opts->mask = 0;
+	opts->multiuser = false;
+	opts->fs_user_id = 0;
+	opts->gid = 0;
 	/* by default, we use LOWER_FS_EXT4 as lower fs type */
 	opts->lower_fs = LOWER_FS_EXT4;
 	/* by default, 0MB is reserved */
@@ -85,37 +85,33 @@
 		case Opt_debug:
 			*debug = 1;
 			break;
-		case Opt_uid:
+		case Opt_fsuid:
 			if (match_int(&args[0], &option))
 				return 0;
 			opts->fs_low_uid = option;
 			break;
-		case Opt_gid:
+		case Opt_fsgid:
 			if (match_int(&args[0], &option))
 				return 0;
 			opts->fs_low_gid = option;
 			break;
-		case Opt_wgid:
+		case Opt_gid:
 			if (match_int(&args[0], &option))
 				return 0;
-			opts->write_gid = option;
+			opts->gid = option;
 			break;
-		case Opt_split:
-			opts->split_perms=1;
+		case Opt_userid:
+			if (match_int(&args[0], &option))
+				return 0;
+			opts->fs_user_id = option;
 			break;
-		case Opt_derive:
-			string_option = match_strdup(&args[0]);
-			if (!strcmp("none", string_option)) {
-				opts->derive = DERIVE_NONE;
-			} else if (!strcmp("legacy", string_option)) {
-				opts->derive = DERIVE_LEGACY;
-			} else if (!strcmp("unified", string_option)) {
-				opts->derive = DERIVE_UNIFIED;
-			} else {
-				kfree(string_option);
-				goto invalid_option;
-			}
-			kfree(string_option);
+		case Opt_mask:
+			if (match_int(&args[0], &option))
+				return 0;
+			opts->mask = option;
+			break;
+		case Opt_multiuser:
+			opts->multiuser = true;
 			break;
 		case Opt_lower_fs:
 			string_option = match_strdup(&args[0]);
@@ -184,6 +180,11 @@
 }
 #endif
 
+DEFINE_MUTEX(sdcardfs_super_list_lock);
+LIST_HEAD(sdcardfs_super_list);
+EXPORT_SYMBOL_GPL(sdcardfs_super_list_lock);
+EXPORT_SYMBOL_GPL(sdcardfs_super_list);
+
 /*
  * There is no need to lock the sdcardfs_super_info's rwsem as there is no
  * way anyone can have a reference to the superblock at this point in time.
@@ -196,7 +197,6 @@
 	struct super_block *lower_sb;
 	struct path lower_path;
 	struct sdcardfs_sb_info *sb_info;
-	void *pkgl_id;
 	struct inode *inode;
 
 	printk(KERN_INFO "sdcardfs version 2.0\n");
@@ -215,8 +215,7 @@
 	err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
 			&lower_path);
 	if (err) {
-		printk(KERN_ERR	"sdcardfs: error accessing "
-		       "lower directory '%s'\n", dev_name);
+		printk(KERN_ERR	"sdcardfs: error accessing lower directory '%s'\n", dev_name);
 		goto out;
 	}
 
@@ -229,7 +228,6 @@
 	}
 
 	sb_info = sb->s_fs_info;
-
 	/* parse options */
 	err = parse_options(sb, raw_data, silent, &debug, &sb_info->options);
 	if (err) {
@@ -237,14 +235,6 @@
 		goto out_freesbi;
 	}
 
-	if (sb_info->options.derive != DERIVE_NONE) {
-		pkgl_id = packagelist_create(sb_info->options.write_gid);
-		if(IS_ERR(pkgl_id))
-			goto out_freesbi;
-		else
-			sb_info->pkgl_id = pkgl_id;
-	}
-
 	/* set the lower superblock field of upper superblock */
 	lower_sb = lower_path.dentry->d_sb;
 	atomic_inc(&lower_sb->s_active);
@@ -263,7 +253,7 @@
 	sb->s_op = &sdcardfs_sops;
 
 	/* get a new inode and allocate our root dentry */
-	inode = sdcardfs_iget(sb, lower_path.dentry->d_inode);
+	inode = sdcardfs_iget(sb, lower_path.dentry->d_inode, 0);
 	if (IS_ERR(inode)) {
 		err = PTR_ERR(inode);
 		goto out_sput;
@@ -292,41 +282,22 @@
 	d_rehash(sb->s_root);
 
 	/* setup permission policy */
-	switch(sb_info->options.derive) {
-		case DERIVE_NONE:
-			setup_derived_state(sb->s_root->d_inode,
-					PERM_ROOT, 0, AID_ROOT, AID_SDCARD_RW, 00775);
-			sb_info->obbpath_s = NULL;
-			break;
-		case DERIVE_LEGACY:
-			/* Legacy behavior used to support internal multiuser layout which
-			 * places user_id at the top directory level, with the actual roots
-			 * just below that. Shared OBB path is also at top level. */
-			setup_derived_state(sb->s_root->d_inode,
-					PERM_LEGACY_PRE_ROOT, 0, AID_ROOT, AID_SDCARD_R, 00771);
-			/* initialize the obbpath string and lookup the path
-			 * sb_info->obb_path will be deactivated by path_put
-			 * on sdcardfs_put_super */
-			sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL);
-			snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name);
-			err =  prepare_dir(sb_info->obbpath_s,
+	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);
+		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, 00755);
-			if(err)
-				printk(KERN_ERR "sdcardfs: %s: %d, error on creating %s\n",
-						__func__,__LINE__, sb_info->obbpath_s);
-			break;
-		case DERIVE_UNIFIED:
-			/* Unified multiuser layout which places secondary user_id under
-			 * /Android/user and shared OBB path under /Android/obb. */
-			setup_derived_state(sb->s_root->d_inode,
-					PERM_ROOT, 0, AID_ROOT, AID_SDCARD_R, 00771);
-
-			sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL);
-			snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name);
-			break;
+					sb_info->options.fs_low_gid, 00755);*/
+	} else {
+		setup_derived_state(sb->s_root->d_inode, PERM_ROOT, sb_info->options.fs_low_uid, AID_ROOT, false);
+		snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name);
 	}
 	fix_derived_permission(sb->s_root->d_inode);
+	sb_info->sb = sb;
+	list_add(&sb_info->list, &sdcardfs_super_list);
+	mutex_unlock(&sdcardfs_super_list_lock);
 
 	if (!silent)
 		printk(KERN_INFO "sdcardfs: mounted on top of %s type %s\n",
@@ -341,7 +312,6 @@
 out_sput:
 	/* drop refs we took earlier */
 	atomic_dec(&lower_sb->s_active);
-	packagelist_destroy(sb_info->pkgl_id);
 out_freesbi:
 	kfree(SDCARDFS_SB(sb));
 	sb->s_fs_info = NULL;
@@ -386,11 +356,22 @@
 					raw_data, sdcardfs_read_super);
 }
 
+void sdcardfs_kill_sb(struct super_block *sb) {
+	struct sdcardfs_sb_info *sbi;
+	if (sb->s_magic == SDCARDFS_SUPER_MAGIC) {
+		sbi = SDCARDFS_SB(sb);
+		mutex_lock(&sdcardfs_super_list_lock);
+		list_del(&sbi->list);
+		mutex_unlock(&sdcardfs_super_list_lock);
+	}
+	generic_shutdown_super(sb);
+}
+
 static struct file_system_type sdcardfs_fs_type = {
 	.owner		= THIS_MODULE,
 	.name		= SDCARDFS_NAME,
 	.mount		= sdcardfs_mount,
-	.kill_sb	= generic_shutdown_super,
+	.kill_sb	= sdcardfs_kill_sb,
 	.fs_flags	= 0,
 };
 
diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c
index f11591d..ba3478d 100644
--- a/fs/sdcardfs/packagelist.c
+++ b/fs/sdcardfs/packagelist.c
@@ -19,13 +19,16 @@
  */
 
 #include "sdcardfs.h"
-#include "strtok.h"
 #include <linux/hashtable.h>
-#include <linux/syscalls.h>
-#include <linux/kthread.h>
-#include <linux/inotify.h>
 #include <linux/delay.h>
 
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <linux/configfs.h>
+
 #define STRING_BUF_SIZE		(512)
 
 struct hashtable_entry {
@@ -34,25 +37,20 @@
 	unsigned int value;
 };
 
-struct packagelist_data {
-	DECLARE_HASHTABLE(package_to_appid,8);
-	DECLARE_HASHTABLE(appid_with_rw,7);
-	struct mutex hashtable_lock;
-	struct task_struct *thread_id;
-	gid_t write_gid;
-	char *strtok_last;
-	char read_buf[STRING_BUF_SIZE];
-	char event_buf[STRING_BUF_SIZE];
-	char app_name_buf[STRING_BUF_SIZE];
-	char gids_buf[STRING_BUF_SIZE];
+struct sb_list {
+	struct super_block *sb;
+	struct list_head list;
 };
 
-static struct kmem_cache *hashtable_entry_cachep;
+struct packagelist_data {
+	DECLARE_HASHTABLE(package_to_appid,8);
+	struct mutex hashtable_lock;
 
-/* Path to system-provided mapping of package name to appIds */
-static const char* const kpackageslist_file = "/data/system/packages.list";
-/* Supplementary groups to execute with */
-static const gid_t kgroups[1] = { AID_PACKAGE_INFO };
+};
+
+static struct packagelist_data *pkgl_data_all;
+
+static struct kmem_cache *hashtable_entry_cachep;
 
 static unsigned int str_hash(const char *key) {
 	int i;
@@ -66,62 +64,29 @@
 	return h;
 }
 
-static int contain_appid_key(struct packagelist_data *pkgl_dat, unsigned int appid) {
-	struct hashtable_entry *hash_cur;
-
-	hash_for_each_possible(pkgl_dat->appid_with_rw, hash_cur, hlist, appid)
-		if ((void *)(uintptr_t)appid == hash_cur->key)
-			return 1;
-
-	return 0;
-}
-
-/* Return if the calling UID holds sdcard_rw. */
-int get_caller_has_rw_locked(void *pkgl_id, derive_t derive) {
-	struct packagelist_data *pkgl_dat = (struct packagelist_data *)pkgl_id;
-	appid_t appid;
-	int ret;
-
-	/* No additional permissions enforcement */
-	if (derive == DERIVE_NONE) {
-		return 1;
-	}
-
-	appid = multiuser_get_app_id(from_kuid(&init_user_ns, current_fsuid()));
-	mutex_lock(&pkgl_dat->hashtable_lock);
-	ret = contain_appid_key(pkgl_dat, appid);
-	mutex_unlock(&pkgl_dat->hashtable_lock);
-	return ret;
-}
-
 appid_t get_appid(void *pkgl_id, const char *app_name)
 {
-	struct packagelist_data *pkgl_dat = (struct packagelist_data *)pkgl_id;
+	struct packagelist_data *pkgl_dat = pkgl_data_all;
 	struct hashtable_entry *hash_cur;
 	unsigned int hash = str_hash(app_name);
 	appid_t ret_id;
 
-	//printk(KERN_INFO "sdcardfs: %s: %s, %u\n", __func__, (char *)app_name, hash);
 	mutex_lock(&pkgl_dat->hashtable_lock);
 	hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) {
-		//printk(KERN_INFO "sdcardfs: %s: %s\n", __func__, (char *)hash_cur->key);
 		if (!strcasecmp(app_name, hash_cur->key)) {
 			ret_id = (appid_t)hash_cur->value;
 			mutex_unlock(&pkgl_dat->hashtable_lock);
-			//printk(KERN_INFO "=> app_id: %d\n", (int)ret_id);
 			return ret_id;
 		}
 	}
 	mutex_unlock(&pkgl_dat->hashtable_lock);
-	//printk(KERN_INFO "=> app_id: %d\n", 0);
 	return 0;
 }
 
 /* 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,
-					derive_t derive, int w_ok, int has_rw) {
+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) {
@@ -132,28 +97,12 @@
 		}
 	}
 
-	/* No additional permissions enforcement */
-	if (derive == DERIVE_NONE) {
-		return 1;
-	}
-
 	/* Root always has access; access for any other UIDs should always
 	 * be controlled through packages.list. */
 	if (from_kuid(&init_user_ns, current_fsuid()) == 0) {
 		return 1;
 	}
 
-	/* If asking to write, verify that caller either owns the
-	 * parent or holds sdcard_rw. */
-	if (w_ok) {
-		if (parent_node &&
-			(from_kuid(&init_user_ns, current_fsuid()) ==
-			 SDCARDFS_I(parent_node)->d_uid)) {
-			return 1;
-		}
-		return has_rw;
-	}
-
 	/* No extra permissions to enforce */
 	return 1;
 }
@@ -171,14 +120,13 @@
 	}
 }
 
-static int insert_str_to_int(struct packagelist_data *pkgl_dat, char *key,
+static int insert_str_to_int_lock(struct packagelist_data *pkgl_dat, char *key,
 		unsigned int value)
 {
 	struct hashtable_entry *hash_cur;
 	struct hashtable_entry *new_entry;
 	unsigned int hash = str_hash(key);
 
-	//printk(KERN_INFO "sdcardfs: %s: %s: %d, %u\n", __func__, (char *)key, value, hash);
 	hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) {
 		if (!strcasecmp(key, hash_cur->key)) {
 			hash_cur->value = value;
@@ -194,247 +142,277 @@
 	return 0;
 }
 
-static void remove_str_to_int(struct hashtable_entry *h_entry) {
-	//printk(KERN_INFO "sdcardfs: %s: %s: %d\n", __func__, (char *)h_entry->key, h_entry->value);
-	kfree(h_entry->key);
-	kmem_cache_free(hashtable_entry_cachep, h_entry);
+static void fixup_perms(struct super_block *sb) {
+	if (sb && sb->s_magic == SDCARDFS_SUPER_MAGIC) {
+		mutex_lock(&sb->s_root->d_inode->i_mutex);
+		get_derive_permissions_recursive(sb->s_root);
+		mutex_unlock(&sb->s_root->d_inode->i_mutex);
+	}
 }
 
-static int insert_int_to_null(struct packagelist_data *pkgl_dat, unsigned int key,
-		unsigned int value)
-{
-	struct hashtable_entry *hash_cur;
-	struct hashtable_entry *new_entry;
+static int insert_str_to_int(struct packagelist_data *pkgl_dat, char *key,
+		unsigned int value) {
+	int ret;
+	struct sdcardfs_sb_info *sbinfo;
+	mutex_lock(&sdcardfs_super_list_lock);
+	mutex_lock(&pkgl_dat->hashtable_lock);
+	ret = insert_str_to_int_lock(pkgl_dat, key, value);
+	mutex_unlock(&pkgl_dat->hashtable_lock);
 
-	//printk(KERN_INFO "sdcardfs: %s: %d: %d\n", __func__, (int)key, value);
-	hash_for_each_possible(pkgl_dat->appid_with_rw,	hash_cur, hlist, key) {
-		if ((void *)(uintptr_t)key == hash_cur->key) {
-			hash_cur->value = value;
-			return 0;
+	list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
+		if (sbinfo) {
+			fixup_perms(sbinfo->sb);
 		}
 	}
-	new_entry = kmem_cache_alloc(hashtable_entry_cachep, GFP_KERNEL);
-	if (!new_entry)
-		return -ENOMEM;
-	new_entry->key = (void *)(uintptr_t)key;
-	new_entry->value = value;
-	hash_add(pkgl_dat->appid_with_rw, &new_entry->hlist, key);
-	return 0;
+	mutex_unlock(&sdcardfs_super_list_lock);
+	return ret;
 }
 
-static void remove_int_to_null(struct hashtable_entry *h_entry) {
-	//printk(KERN_INFO "sdcardfs: %s: %d: %d\n", __func__, (int)h_entry->key, h_entry->value);
+static void remove_str_to_int_lock(struct hashtable_entry *h_entry) {
+	kfree(h_entry->key);
+	hash_del(&h_entry->hlist);
 	kmem_cache_free(hashtable_entry_cachep, h_entry);
 }
 
+static void remove_str_to_int(struct packagelist_data *pkgl_dat, const char *key)
+{
+	struct sdcardfs_sb_info *sbinfo;
+	struct hashtable_entry *hash_cur;
+	unsigned int hash = str_hash(key);
+	mutex_lock(&sdcardfs_super_list_lock);
+	mutex_lock(&pkgl_dat->hashtable_lock);
+	hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) {
+		if (!strcasecmp(key, hash_cur->key)) {
+			remove_str_to_int_lock(hash_cur);
+			break;
+		}
+	}
+	mutex_unlock(&pkgl_dat->hashtable_lock);
+	list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
+		if (sbinfo) {
+			fixup_perms(sbinfo->sb);
+		}
+	}
+	mutex_unlock(&sdcardfs_super_list_lock);
+	return;
+}
+
 static void remove_all_hashentrys(struct packagelist_data *pkgl_dat)
 {
 	struct hashtable_entry *hash_cur;
 	struct hlist_node *h_t;
 	int i;
-
-	hash_for_each_safe(pkgl_dat->package_to_appid, i, h_t, hash_cur, hlist)
-		remove_str_to_int(hash_cur);
-	hash_for_each_safe(pkgl_dat->appid_with_rw, i, h_t, hash_cur, hlist)
-		remove_int_to_null(hash_cur);
-
-	hash_init(pkgl_dat->package_to_appid);
-	hash_init(pkgl_dat->appid_with_rw);
-}
-
-static int read_package_list(struct packagelist_data *pkgl_dat) {
-	int ret;
-	int fd;
-	int read_amount;
-
-	printk(KERN_INFO "sdcardfs: read_package_list\n");
-
 	mutex_lock(&pkgl_dat->hashtable_lock);
-
-	remove_all_hashentrys(pkgl_dat);
-
-	fd = sys_open(kpackageslist_file, O_RDONLY, 0);
-	if (fd < 0) {
-		printk(KERN_ERR "sdcardfs: failed to open package list\n");
-		mutex_unlock(&pkgl_dat->hashtable_lock);
-		return fd;
-	}
-
-	while ((read_amount = sys_read(fd, pkgl_dat->read_buf,
-					sizeof(pkgl_dat->read_buf))) > 0) {
-		unsigned int appid;
-		char *token;
-		int one_line_len = 0;
-		int additional_read;
-		unsigned long ret_gid;
-
-		while (one_line_len < read_amount) {
-			if (pkgl_dat->read_buf[one_line_len] == '\n') {
-				one_line_len++;
-				break;
-			}
-			one_line_len++;
-		}
-		additional_read = read_amount - one_line_len;
-		if (additional_read > 0)
-			sys_lseek(fd, -additional_read, SEEK_CUR);
-
-		if (sscanf(pkgl_dat->read_buf, "%s %u %*d %*s %*s %s",
-				pkgl_dat->app_name_buf, &appid,
-				pkgl_dat->gids_buf) == 3) {
-			ret = insert_str_to_int(pkgl_dat, pkgl_dat->app_name_buf, appid);
-			if (ret) {
-				sys_close(fd);
-				mutex_unlock(&pkgl_dat->hashtable_lock);
-				return ret;
-			}
-
-			token = strtok_r(pkgl_dat->gids_buf, ",", &pkgl_dat->strtok_last);
-			while (token != NULL) {
-				if (!kstrtoul(token, 10, &ret_gid) &&
-						(ret_gid == pkgl_dat->write_gid)) {
-					ret = insert_int_to_null(pkgl_dat, appid, 1);
-					if (ret) {
-						sys_close(fd);
-						mutex_unlock(&pkgl_dat->hashtable_lock);
-						return ret;
-					}
-					break;
-				}
-				token = strtok_r(NULL, ",", &pkgl_dat->strtok_last);
-			}
-		}
-	}
-
-	sys_close(fd);
+	hash_for_each_safe(pkgl_dat->package_to_appid, i, h_t, hash_cur, hlist)
+		remove_str_to_int_lock(hash_cur);
 	mutex_unlock(&pkgl_dat->hashtable_lock);
-	return 0;
+	hash_init(pkgl_dat->package_to_appid);
 }
 
-static int packagelist_reader(void *thread_data)
-{
-	struct packagelist_data *pkgl_dat = (struct packagelist_data *)thread_data;
-	struct inotify_event *event;
-	bool active = false;
-	int event_pos;
-	int event_size;
-	int res = 0;
-	int nfd;
-
-	allow_signal(SIGINT);
-
-	nfd = sys_inotify_init();
-	if (nfd < 0) {
-		printk(KERN_ERR "sdcardfs: inotify_init failed: %d\n", nfd);
-		return nfd;
-	}
-
-	while (!kthread_should_stop()) {
-		if (signal_pending(current)) {
-			ssleep(1);
-			continue;
-		}
-
-		if (!active) {
-			res = sys_inotify_add_watch(nfd, kpackageslist_file, IN_DELETE_SELF);
-			if (res < 0) {
-				if (res == -ENOENT || res == -EACCES) {
-				/* Framework may not have created yet, sleep and retry */
-					printk(KERN_ERR "sdcardfs: missing packages.list; retrying\n");
-					ssleep(2);
-					printk(KERN_ERR "sdcardfs: missing packages.list_end; retrying\n");
-					continue;
-				} else {
-					printk(KERN_ERR "sdcardfs: inotify_add_watch failed: %d\n", res);
-					goto interruptable_sleep;
-				}
-			}
-			/* Watch above will tell us about any future changes, so
-			 * read the current state. */
-			res = read_package_list(pkgl_dat);
-			if (res) {
-				printk(KERN_ERR "sdcardfs: read_package_list failed: %d\n", res);
-				goto interruptable_sleep;
-			}
-			active = true;
-		}
-
-		event_pos = 0;
-		res = sys_read(nfd, pkgl_dat->event_buf, sizeof(pkgl_dat->event_buf));
-		if (res < (int) sizeof(*event)) {
-			if (res == -EINTR)
-				continue;
-			printk(KERN_ERR "sdcardfs: failed to read inotify event: %d\n", res);
-			goto interruptable_sleep;
-		}
-
-		while (res >= (int) sizeof(*event)) {
-			event = (struct inotify_event *) (pkgl_dat->event_buf + event_pos);
-
-			printk(KERN_INFO "sdcardfs: inotify event: %08x\n", event->mask);
-			if ((event->mask & IN_IGNORED) == IN_IGNORED) {
-				/* Previously watched file was deleted, probably due to move
-				 * that swapped in new data; re-arm the watch and read. */
-				active = false;
-			}
-
-			event_size = sizeof(*event) + event->len;
-			res -= event_size;
-			event_pos += event_size;
-		}
-		continue;
-
-interruptable_sleep:
-		set_current_state(TASK_INTERRUPTIBLE);
-		schedule();
-	}
-	flush_signals(current);
-	sys_close(nfd);
-	return res;
-}
-
-void * packagelist_create(gid_t write_gid)
+static struct packagelist_data * packagelist_create(void)
 {
 	struct packagelist_data *pkgl_dat;
-        struct task_struct *packagelist_thread;
 
 	pkgl_dat = kmalloc(sizeof(*pkgl_dat), GFP_KERNEL | __GFP_ZERO);
 	if (!pkgl_dat) {
-                printk(KERN_ERR "sdcardfs: creating kthread failed\n");
+                printk(KERN_ERR "sdcardfs: Failed to create hash\n");
 		return ERR_PTR(-ENOMEM);
 	}
 
 	mutex_init(&pkgl_dat->hashtable_lock);
 	hash_init(pkgl_dat->package_to_appid);
-	hash_init(pkgl_dat->appid_with_rw);
-	pkgl_dat->write_gid = write_gid;
 
-        packagelist_thread = kthread_run(packagelist_reader, (void *)pkgl_dat, "pkgld");
-        if (IS_ERR(packagelist_thread)) {
-                printk(KERN_ERR "sdcardfs: creating kthread failed\n");
-		kfree(pkgl_dat);
-		return packagelist_thread;
-        }
-	pkgl_dat->thread_id = packagelist_thread;
-
-	printk(KERN_INFO "sdcardfs: created packagelist pkgld/%d\n",
-				(int)pkgl_dat->thread_id->pid);
-
-	return (void *)pkgl_dat;
+	return pkgl_dat;
 }
 
-void packagelist_destroy(void *pkgl_id)
+static void packagelist_destroy(struct packagelist_data *pkgl_dat)
 {
-	struct packagelist_data *pkgl_dat = (struct packagelist_data *)pkgl_id;
-	pid_t pkgl_pid = pkgl_dat->thread_id->pid;
-
-	force_sig_info(SIGINT, SEND_SIG_PRIV, pkgl_dat->thread_id);
-	kthread_stop(pkgl_dat->thread_id);
 	remove_all_hashentrys(pkgl_dat);
-	printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld/%d\n", (int)pkgl_pid);
+	printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld\n");
 	kfree(pkgl_dat);
 }
 
+struct package_appid {
+	struct config_item item;
+	int add_pid;
+};
+
+static inline struct package_appid *to_package_appid(struct config_item *item)
+{
+	return item ? container_of(item, struct package_appid, item) : NULL;
+}
+
+static ssize_t package_appid_attr_show(struct config_item *item,
+				      char *page)
+{
+	ssize_t count;
+	count = sprintf(page, "%d\n", get_appid(pkgl_data_all, item->ci_name));
+	return count;
+}
+
+static ssize_t package_appid_attr_store(struct config_item *item,
+				       const char *page, size_t count)
+{
+	struct package_appid *package_appid = to_package_appid(item);
+	unsigned long tmp;
+	char *p = (char *) page;
+	int ret;
+
+	tmp = simple_strtoul(p, &p, 10);
+	if (!p || (*p && (*p != '\n')))
+		return -EINVAL;
+
+	if (tmp > INT_MAX)
+		return -ERANGE;
+	ret = insert_str_to_int(pkgl_data_all, item->ci_name, (unsigned int)tmp);
+	package_appid->add_pid = tmp;
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static struct configfs_attribute package_appid_attr_add_pid = {
+	.ca_owner = THIS_MODULE,
+	.ca_name = "appid",
+	.ca_mode = S_IRUGO | S_IWUGO,
+	.show = package_appid_attr_show,
+	.store = package_appid_attr_store,
+};
+
+static struct configfs_attribute *package_appid_attrs[] = {
+	&package_appid_attr_add_pid,
+	NULL,
+};
+
+static void package_appid_release(struct config_item *item)
+{
+	printk(KERN_INFO "sdcardfs: removing %s\n", item->ci_dentry->d_name.name);
+	/* item->ci_name is freed already, so we rely on the dentry */
+	remove_str_to_int(pkgl_data_all, item->ci_dentry->d_name.name);
+	kfree(to_package_appid(item));
+}
+
+static struct configfs_item_operations package_appid_item_ops = {
+	.release		= package_appid_release,
+};
+
+static struct config_item_type package_appid_type = {
+	.ct_item_ops	= &package_appid_item_ops,
+	.ct_attrs	= package_appid_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+
+struct sdcardfs_packages {
+	struct config_group group;
+};
+
+static inline struct sdcardfs_packages *to_sdcardfs_packages(struct config_item *item)
+{
+	return item ? container_of(to_config_group(item), struct sdcardfs_packages, group) : NULL;
+}
+
+static struct config_item *sdcardfs_packages_make_item(struct config_group *group, const char *name)
+{
+	struct package_appid *package_appid;
+
+	package_appid = kzalloc(sizeof(struct package_appid), GFP_KERNEL);
+	if (!package_appid)
+		return ERR_PTR(-ENOMEM);
+
+	config_item_init_type_name(&package_appid->item, name,
+				   &package_appid_type);
+
+	package_appid->add_pid = 0;
+
+	return &package_appid->item;
+}
+
+static ssize_t packages_attr_show(struct config_item *item,
+					 char *page)
+{
+	struct hashtable_entry *hash_cur;
+	struct hlist_node *h_t;
+	int i;
+	int count = 0;
+	mutex_lock(&pkgl_data_all->hashtable_lock);
+	hash_for_each_safe(pkgl_data_all->package_to_appid, i, h_t, hash_cur, hlist)
+		count += snprintf(page + count, PAGE_SIZE - count, "%s %d\n", (char *)hash_cur->key, hash_cur->value);
+	mutex_unlock(&pkgl_data_all->hashtable_lock);
+
+
+	return count;
+}
+
+static struct configfs_attribute sdcardfs_packages_attr_description = {
+	.ca_owner = THIS_MODULE,
+	.ca_name = "packages_gid.list",
+	.ca_mode = S_IRUGO,
+	.show = packages_attr_show,
+};
+
+static struct configfs_attribute *sdcardfs_packages_attrs[] = {
+	&sdcardfs_packages_attr_description,
+	NULL,
+};
+
+static void sdcardfs_packages_release(struct config_item *item)
+{
+
+	printk(KERN_INFO "sdcardfs: destroyed something?\n");
+	kfree(to_sdcardfs_packages(item));
+}
+
+static struct configfs_item_operations sdcardfs_packages_item_ops = {
+	.release	= sdcardfs_packages_release,
+};
+
+/*
+ * Note that, since no extra work is required on ->drop_item(),
+ * no ->drop_item() is provided.
+ */
+static struct configfs_group_operations sdcardfs_packages_group_ops = {
+	.make_item	= sdcardfs_packages_make_item,
+};
+
+static struct config_item_type sdcardfs_packages_type = {
+	.ct_item_ops	= &sdcardfs_packages_item_ops,
+	.ct_group_ops	= &sdcardfs_packages_group_ops,
+	.ct_attrs	= sdcardfs_packages_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct configfs_subsystem sdcardfs_packages_subsys = {
+	.su_group = {
+		.cg_item = {
+			.ci_namebuf = "sdcardfs",
+			.ci_type = &sdcardfs_packages_type,
+		},
+	},
+};
+
+static int __init configfs_sdcardfs_init(void)
+{
+	int ret;
+	struct configfs_subsystem *subsys = &sdcardfs_packages_subsys;
+
+	config_group_init(&subsys->su_group);
+	mutex_init(&subsys->su_mutex);
+	ret = configfs_register_subsystem(subsys);
+	if (ret) {
+		printk(KERN_ERR "Error %d while registering subsystem %s\n",
+		       ret,
+		       subsys->su_group.cg_item.ci_namebuf);
+	}
+	return ret;
+}
+
+static void __exit configfs_sdcardfs_exit(void)
+{
+	configfs_unregister_subsystem(&sdcardfs_packages_subsys);
+}
+
 int packagelist_init(void)
 {
 	hashtable_entry_cachep =
@@ -445,13 +423,15 @@
 		return -ENOMEM;
 	}
 
+	pkgl_data_all = packagelist_create();
+	configfs_sdcardfs_init();
         return 0;
 }
 
 void packagelist_exit(void)
 {
+	configfs_sdcardfs_exit();
+	packagelist_destroy(pkgl_data_all);
 	if (hashtable_entry_cachep)
 		kmem_cache_destroy(hashtable_entry_cachep);
 }
-
-
diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h
index 51f6c79..1b85f4e 100644
--- a/fs/sdcardfs/sdcardfs.h
+++ b/fs/sdcardfs/sdcardfs.h
@@ -42,6 +42,7 @@
 #include <linux/types.h>
 #include <linux/security.h>
 #include <linux/string.h>
+#include <linux/list.h>
 #include "multiuser.h"
 
 /* the file system name */
@@ -70,10 +71,11 @@
 #define fix_derived_permission(x)	\
 	do {						\
 		(x)->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(x)->d_uid);	\
-		(x)->i_gid = make_kgid(&init_user_ns, SDCARDFS_I(x)->d_gid);	\
-		(x)->i_mode = ((x)->i_mode & S_IFMT) | SDCARDFS_I(x)->d_mode;\
+		(x)->i_gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(x)));	\
+		(x)->i_mode = ((x)->i_mode & S_IFMT) | get_mode(SDCARDFS_I(x));\
 	} while (0)
 
+
 /* OVERRIDE_CRED() and REVERT_CRED()
  * 	OVERRID_CRED()
  * 		backup original task->cred
@@ -99,35 +101,28 @@
 		(int)current->cred->fsuid, 		\
 		(int)current->cred->fsgid);
 
-/* Android 4.4 support */
+/* Android 5.0 support */
 
 /* Permission mode for a specific node. Controls how file permissions
  * are derived for children nodes. */
 typedef enum {
-	/* Nothing special; this node should just inherit from its parent. */
-	PERM_INHERIT,
-	/* This node is one level above a normal root; used for legacy layouts
-	 * which use the first level to represent user_id. */
-	PERM_LEGACY_PRE_ROOT,
-	/* This node is "/" */
-	PERM_ROOT,
-	/* This node is "/Android" */
-	PERM_ANDROID,
-	/* This node is "/Android/data" */
-	PERM_ANDROID_DATA,
-	/* This node is "/Android/obb" */
-	PERM_ANDROID_OBB,
-	/* This node is "/Android/user" */
-	PERM_ANDROID_USER,
+    /* Nothing special; this node should just inherit from its parent. */
+    PERM_INHERIT,
+    /* This node is one level above a normal root; used for legacy layouts
+     * which use the first level to represent user_id. */
+    PERM_PRE_ROOT,
+    /* This node is "/" */
+    PERM_ROOT,
+    /* This node is "/Android" */
+    PERM_ANDROID,
+    /* This node is "/Android/data" */
+    PERM_ANDROID_DATA,
+    /* This node is "/Android/obb" */
+    PERM_ANDROID_OBB,
+    /* This node is "/Android/media" */
+    PERM_ANDROID_MEDIA,
 } perm_t;
 
-/* Permissions structure to derive */
-typedef enum {
-	DERIVE_NONE,
-	DERIVE_LEGACY,
-	DERIVE_UNIFIED,
-} derive_t;
-
 typedef enum {
 	LOWER_FS_EXT4,
 	LOWER_FS_FAT,
@@ -161,9 +156,9 @@
 extern struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
 				unsigned int flags);
 extern struct inode *sdcardfs_iget(struct super_block *sb,
-				 struct inode *lower_inode);
+				 struct inode *lower_inode, userid_t id);
 extern int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb,
-			    struct path *lower_path);
+			    struct path *lower_path, userid_t id);
 
 /* file private data */
 struct sdcardfs_file_info {
@@ -174,18 +169,16 @@
 /* sdcardfs inode data in memory */
 struct sdcardfs_inode_info {
 	struct inode *lower_inode;
-	/* state derived based on current position in hierachy
-	 * caution: d_mode does not include file types
-	 */
+	/* state derived based on current position in hierachy */
 	perm_t perm;
 	userid_t userid;
 	uid_t d_uid;
-	gid_t d_gid;
-	mode_t d_mode;
+	bool under_android;
 
 	struct inode vfs_inode;
 };
 
+
 /* sdcardfs dentry data in memory */
 struct sdcardfs_dentry_info {
 	spinlock_t lock;	/* protects lower_path */
@@ -196,15 +189,17 @@
 struct sdcardfs_mount_options {
 	uid_t fs_low_uid;
 	gid_t fs_low_gid;
-	gid_t write_gid;
-	int split_perms;
-	derive_t derive;
+	userid_t fs_user_id;
+	gid_t gid;
 	lower_fs_t lower_fs;
+	mode_t mask;
+	bool multiuser;
 	unsigned int reserved_mb;
 };
 
 /* sdcardfs super-block data in memory */
 struct sdcardfs_sb_info {
+	struct super_block *sb;
 	struct super_block *lower_sb;
 	/* derived perm policy : some of options have been added
 	 * to sdcardfs_mount_options (Android 4.4 support) */
@@ -213,6 +208,7 @@
 	char *obbpath_s;
 	struct path obbpath;
 	void *pkgl_id;
+	struct list_head list;
 };
 
 /*
@@ -331,6 +327,44 @@
 SDCARDFS_DENT_FUNC(lower_path)
 SDCARDFS_DENT_FUNC(orig_path)
 
+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) {
+		/* As an optimization, certain trusted system components only run
+		 * as owner but operate across all users. Since we're now handing
+		 * out the sdcard_rw GID only to trusted apps, we're okay relaxing
+		 * the user boundary enforcement for the default view. The UIDs
+		 * assigned to app directories are still multiuser aware. */
+		return AID_SDCARD_RW;
+	} else {
+		return multiuser_get_uid(info->userid, sb_info->options.gid);
+	}
+}
+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);
+	int visible_mode = 0775 & ~sb_info->options.mask;
+
+	if (info->perm == PERM_PRE_ROOT) {
+		/* Top of multi-user view should always be visible to ensure
+		* secondary users can traverse inside. */
+		visible_mode = 0711;
+	} else if (info->under_android) {
+		/* Block "other" access to Android directories, since only apps
+		* belonging to a specific user should be in there; we still
+		* leave +x open for the default view. */
+		if (sb_info->options.gid == AID_SDCARD_RW) {
+			visible_mode = visible_mode & ~0006;
+		} else {
+			visible_mode = visible_mode & ~0007;
+		}
+	}
+	owner_mode = info->lower_inode->i_mode & 0700;
+	filtered_mode = visible_mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6));
+	return filtered_mode;
+}
+
 static inline int has_graft_path(const struct dentry *dent)
 {
 	int ret = 0;
@@ -364,22 +398,24 @@
 		sdcardfs_put_lower_path(dent, real_lower);
 }
 
+extern struct mutex sdcardfs_super_list_lock;
+extern struct list_head sdcardfs_super_list;
+
 /* for packagelist.c */
-extern int get_caller_has_rw_locked(void *pkgl_id, derive_t derive);
 extern appid_t get_appid(void *pkgl_id, const char *app_name);
-extern int check_caller_access_to_name(struct inode *parent_node, const char* name,
-                                        derive_t derive, int w_ok, int has_rw);
+extern int check_caller_access_to_name(struct inode *parent_node, const char* name);
 extern int open_flags_to_access_mode(int open_flags);
-extern void * packagelist_create(gid_t write_gid);
-extern void packagelist_destroy(void *pkgl_id);
 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, gid_t gid, mode_t mode);
+			userid_t userid, uid_t uid, bool under_android);
 extern void get_derived_permission(struct dentry *parent, struct dentry *dentry);
-extern void update_derived_permission(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 update_derived_permission_lock(struct dentry *dentry);
 extern int need_graft_path(struct dentry *dentry);
 extern int is_base_obbpath(struct dentry *dentry);
 extern int is_obbpath_invalid(struct dentry *dentry);
@@ -483,4 +519,18 @@
 		return 1;
 }
 
+/* Copies attrs and maintains sdcardfs managed attrs */
+static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct inode *src)
+{
+	dest->i_mode = (src->i_mode  & S_IFMT) | get_mode(SDCARDFS_I(dest));
+	dest->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(dest)->d_uid);
+	dest->i_gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(dest)));
+	dest->i_rdev = src->i_rdev;
+	dest->i_atime = src->i_atime;
+	dest->i_mtime = src->i_mtime;
+	dest->i_ctime = src->i_ctime;
+	dest->i_blkbits = src->i_blkbits;
+	dest->i_flags = src->i_flags;
+	set_nlink(dest, src->i_nlink);
+}
 #endif	/* not _SDCARDFS_H_ */
diff --git a/fs/sdcardfs/strtok.h b/fs/sdcardfs/strtok.h
deleted file mode 100644
index 50ab25a..0000000
--- a/fs/sdcardfs/strtok.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * fs/sdcardfs/strtok.h
- *
- * Copyright (c) 2013 Samsung Electronics Co. Ltd
- *   Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
- *               Sunghwan Yun, Sungjong Seo
- *
- * This program has been developed as a stackable file system based on
- * the WrapFS which written by
- *
- * Copyright (c) 1998-2011 Erez Zadok
- * Copyright (c) 2009     Shrikar Archak
- * Copyright (c) 2003-2011 Stony Brook University
- * Copyright (c) 2003-2011 The Research Foundation of SUNY
- *
- * This file is dual licensed.  It may be redistributed and/or modified
- * under the terms of the Apache 2.0 License OR version 2 of the GNU
- * General Public License.
- */
-
-static char *
-strtok_r(char *s, const char *delim, char **last)
-{
-        char *spanp;
-        int c, sc;
-        char *tok;
-
-
-        /* if (s == NULL && (s = *last) == NULL)
-                return NULL;     */
-        if (s == NULL) {
-                s = *last;
-                if (s == NULL)
-                        return NULL;
-        }
-
-        /*
-         * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
-         */
-cont:
-        c = *s++;
-        for (spanp = (char *)delim; (sc = *spanp++) != 0;) {
-                if (c == sc)
-                        goto cont;
-        }
-
-        if (c == 0) {           /* no non-delimiter characters */
-                *last = NULL;
-                return NULL;
-        }
-        tok = s - 1;
-
-        /*
-         * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
-         * Note that delim must have one NUL; we stop if we see that, too.
-         */
-        for (;;) {
-                c = *s++;
-                spanp = (char *)delim;
-                do {
-                        sc = *spanp++;
-                        if (sc == c) {
-                                if (c == 0)
-                                        s = NULL;
-                                else
-                                        s[-1] = 0;
-                                *last = s;
-                                return tok;
-                        }
-                } while (sc != 0);
-        }
-
-        /* NOTREACHED */
-}
-
diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c
index f153ce1..1d64901 100644
--- a/fs/sdcardfs/super.c
+++ b/fs/sdcardfs/super.c
@@ -46,9 +46,6 @@
 	sdcardfs_set_lower_super(sb, NULL);
 	atomic_dec(&s->s_active);
 
-	if(spd->pkgl_id)
-		packagelist_destroy(spd->pkgl_id);
-
 	kfree(spd);
 	sb->s_fs_info = NULL;
 }
@@ -203,12 +200,8 @@
 	if (opts->fs_low_gid != 0)
 		seq_printf(m, ",gid=%u", opts->fs_low_gid);
 
-	if (opts->derive == DERIVE_NONE)
-		seq_printf(m, ",derive=none");
-	else if (opts->derive == DERIVE_LEGACY)
-		seq_printf(m, ",derive=legacy");
-	else if (opts->derive == DERIVE_UNIFIED)
-		seq_printf(m, ",derive=unified");
+	if (opts->multiuser)
+		seq_printf(m, ",multiuser");
 
 	if (opts->reserved_mb != 0)
 		seq_printf(m, ",reserved=%uMB", opts->reserved_mb);