| /* | 
 |  * fs/sdcardfs/main.c | 
 |  * | 
 |  * 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. | 
 |  */ | 
 |  | 
 | #include "sdcardfs.h" | 
 | #include <linux/fscrypt.h> | 
 | #include <linux/module.h> | 
 | #include <linux/types.h> | 
 | #include <linux/parser.h> | 
 |  | 
 | enum { | 
 | 	Opt_fsuid, | 
 | 	Opt_fsgid, | 
 | 	Opt_gid, | 
 | 	Opt_debug, | 
 | 	Opt_mask, | 
 | 	Opt_multiuser, | 
 | 	Opt_userid, | 
 | 	Opt_reserved_mb, | 
 | 	Opt_gid_derivation, | 
 | 	Opt_default_normal, | 
 | 	Opt_nocache, | 
 | 	Opt_unshared_obb, | 
 | 	Opt_err, | 
 | }; | 
 |  | 
 | static const match_table_t sdcardfs_tokens = { | 
 | 	{Opt_fsuid, "fsuid=%u"}, | 
 | 	{Opt_fsgid, "fsgid=%u"}, | 
 | 	{Opt_gid, "gid=%u"}, | 
 | 	{Opt_debug, "debug"}, | 
 | 	{Opt_mask, "mask=%u"}, | 
 | 	{Opt_userid, "userid=%d"}, | 
 | 	{Opt_multiuser, "multiuser"}, | 
 | 	{Opt_gid_derivation, "derive_gid"}, | 
 | 	{Opt_default_normal, "default_normal"}, | 
 | 	{Opt_unshared_obb, "unshared_obb"}, | 
 | 	{Opt_reserved_mb, "reserved_mb=%u"}, | 
 | 	{Opt_nocache, "nocache"}, | 
 | 	{Opt_err, NULL} | 
 | }; | 
 |  | 
 | static int parse_options(struct super_block *sb, char *options, int silent, | 
 | 				int *debug, struct sdcardfs_vfsmount_options *vfsopts, | 
 | 				struct sdcardfs_mount_options *opts) | 
 | { | 
 | 	char *p; | 
 | 	substring_t args[MAX_OPT_ARGS]; | 
 | 	int option; | 
 |  | 
 | 	/* by default, we use AID_MEDIA_RW as uid, gid */ | 
 | 	opts->fs_low_uid = AID_MEDIA_RW; | 
 | 	opts->fs_low_gid = AID_MEDIA_RW; | 
 | 	vfsopts->mask = 0; | 
 | 	opts->multiuser = false; | 
 | 	opts->fs_user_id = 0; | 
 | 	vfsopts->gid = 0; | 
 | 	/* by default, 0MB is reserved */ | 
 | 	opts->reserved_mb = 0; | 
 | 	/* by default, gid derivation is off */ | 
 | 	opts->gid_derivation = false; | 
 | 	opts->default_normal = false; | 
 | 	opts->nocache = false; | 
 |  | 
 | 	*debug = 0; | 
 |  | 
 | 	if (!options) | 
 | 		return 0; | 
 |  | 
 | 	while ((p = strsep(&options, ",")) != NULL) { | 
 | 		int token; | 
 |  | 
 | 		if (!*p) | 
 | 			continue; | 
 |  | 
 | 		token = match_token(p, sdcardfs_tokens, args); | 
 |  | 
 | 		switch (token) { | 
 | 		case Opt_debug: | 
 | 			*debug = 1; | 
 | 			break; | 
 | 		case Opt_fsuid: | 
 | 			if (match_int(&args[0], &option)) | 
 | 				return 0; | 
 | 			opts->fs_low_uid = option; | 
 | 			break; | 
 | 		case Opt_fsgid: | 
 | 			if (match_int(&args[0], &option)) | 
 | 				return 0; | 
 | 			opts->fs_low_gid = option; | 
 | 			break; | 
 | 		case Opt_gid: | 
 | 			if (match_int(&args[0], &option)) | 
 | 				return 0; | 
 | 			vfsopts->gid = option; | 
 | 			break; | 
 | 		case Opt_userid: | 
 | 			if (match_int(&args[0], &option)) | 
 | 				return 0; | 
 | 			opts->fs_user_id = option; | 
 | 			break; | 
 | 		case Opt_mask: | 
 | 			if (match_int(&args[0], &option)) | 
 | 				return 0; | 
 | 			vfsopts->mask = option; | 
 | 			break; | 
 | 		case Opt_multiuser: | 
 | 			opts->multiuser = true; | 
 | 			break; | 
 | 		case Opt_reserved_mb: | 
 | 			if (match_int(&args[0], &option)) | 
 | 				return 0; | 
 | 			opts->reserved_mb = option; | 
 | 			break; | 
 | 		case Opt_gid_derivation: | 
 | 			opts->gid_derivation = true; | 
 | 			break; | 
 | 		case Opt_default_normal: | 
 | 			opts->default_normal = true; | 
 | 			break; | 
 | 		case Opt_nocache: | 
 | 			opts->nocache = true; | 
 | 			break; | 
 | 		case Opt_unshared_obb: | 
 | 			opts->unshared_obb = true; | 
 | 			break; | 
 | 		/* unknown option */ | 
 | 		default: | 
 | 			if (!silent) | 
 | 				pr_err("Unrecognized mount option \"%s\" or missing value", p); | 
 | 			return -EINVAL; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if (*debug) { | 
 | 		pr_info("sdcardfs : options - debug:%d\n", *debug); | 
 | 		pr_info("sdcardfs : options - uid:%d\n", | 
 | 							opts->fs_low_uid); | 
 | 		pr_info("sdcardfs : options - gid:%d\n", | 
 | 							opts->fs_low_gid); | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | int parse_options_remount(struct super_block *sb, char *options, int silent, | 
 | 				struct sdcardfs_vfsmount_options *vfsopts) | 
 | { | 
 | 	char *p; | 
 | 	substring_t args[MAX_OPT_ARGS]; | 
 | 	int option; | 
 | 	int debug; | 
 |  | 
 | 	if (!options) | 
 | 		return 0; | 
 |  | 
 | 	while ((p = strsep(&options, ",")) != NULL) { | 
 | 		int token; | 
 |  | 
 | 		if (!*p) | 
 | 			continue; | 
 |  | 
 | 		token = match_token(p, sdcardfs_tokens, args); | 
 |  | 
 | 		switch (token) { | 
 | 		case Opt_debug: | 
 | 			debug = 1; | 
 | 			break; | 
 | 		case Opt_gid: | 
 | 			if (match_int(&args[0], &option)) | 
 | 				return 0; | 
 | 			vfsopts->gid = option; | 
 |  | 
 | 			break; | 
 | 		case Opt_mask: | 
 | 			if (match_int(&args[0], &option)) | 
 | 				return 0; | 
 | 			vfsopts->mask = option; | 
 | 			break; | 
 | 		case Opt_unshared_obb: | 
 | 		case Opt_default_normal: | 
 | 		case Opt_multiuser: | 
 | 		case Opt_userid: | 
 | 		case Opt_fsuid: | 
 | 		case Opt_fsgid: | 
 | 		case Opt_reserved_mb: | 
 | 		case Opt_gid_derivation: | 
 | 			if (!silent) | 
 | 				pr_warn("Option \"%s\" can't be changed during remount\n", p); | 
 | 			break; | 
 | 		/* unknown option */ | 
 | 		default: | 
 | 			if (!silent) | 
 | 				pr_err("Unrecognized mount option \"%s\" or missing value", p); | 
 | 			return -EINVAL; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if (debug) { | 
 | 		pr_info("sdcardfs : options - debug:%d\n", debug); | 
 | 		pr_info("sdcardfs : options - gid:%d\n", vfsopts->gid); | 
 | 		pr_info("sdcardfs : options - mask:%d\n", vfsopts->mask); | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | #if 0 | 
 | /* | 
 |  * our custom d_alloc_root work-alike | 
 |  * | 
 |  * we can't use d_alloc_root if we want to use our own interpose function | 
 |  * unchanged, so we simply call our own "fake" d_alloc_root | 
 |  */ | 
 | static struct dentry *sdcardfs_d_alloc_root(struct super_block *sb) | 
 | { | 
 | 	struct dentry *ret = NULL; | 
 |  | 
 | 	if (sb) { | 
 | 		static const struct qstr name = { | 
 | 			.name = "/", | 
 | 			.len = 1 | 
 | 		}; | 
 |  | 
 | 		ret = d_alloc(NULL, &name); | 
 | 		if (ret) { | 
 | 			d_set_d_op(ret, &sdcardfs_ci_dops); | 
 | 			ret->d_sb = sb; | 
 | 			ret->d_parent = ret; | 
 | 		} | 
 | 	} | 
 | 	return ret; | 
 | } | 
 | #endif | 
 |  | 
 | DEFINE_MUTEX(sdcardfs_super_list_lock); | 
 | EXPORT_SYMBOL_GPL(sdcardfs_super_list_lock); | 
 | LIST_HEAD(sdcardfs_super_list); | 
 | 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. | 
 |  */ | 
 | static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, | 
 | 		const char *dev_name, void *raw_data, int silent) | 
 | { | 
 | 	int err = 0; | 
 | 	int debug; | 
 | 	struct super_block *lower_sb; | 
 | 	struct path lower_path; | 
 | 	struct sdcardfs_sb_info *sb_info; | 
 | 	struct sdcardfs_vfsmount_options *mnt_opt = mnt->data; | 
 | 	struct inode *inode; | 
 |  | 
 | 	pr_info("sdcardfs version 2.0\n"); | 
 |  | 
 | 	if (!dev_name) { | 
 | 		pr_err("sdcardfs: read_super: missing dev_name argument\n"); | 
 | 		err = -EINVAL; | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	pr_info("sdcardfs: dev_name -> %s\n", dev_name); | 
 | 	pr_info("sdcardfs: options -> %s\n", (char *)raw_data); | 
 | 	pr_info("sdcardfs: mnt -> %p\n", mnt); | 
 |  | 
 | 	/* parse lower path */ | 
 | 	err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, | 
 | 			&lower_path); | 
 | 	if (err) { | 
 | 		pr_err("sdcardfs: error accessing lower directory '%s'\n", dev_name); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	/* allocate superblock private data */ | 
 | 	sb->s_fs_info = kzalloc(sizeof(struct sdcardfs_sb_info), GFP_KERNEL); | 
 | 	if (!SDCARDFS_SB(sb)) { | 
 | 		pr_crit("sdcardfs: read_super: out of memory\n"); | 
 | 		err = -ENOMEM; | 
 | 		goto out_free; | 
 | 	} | 
 |  | 
 | 	sb_info = sb->s_fs_info; | 
 | 	/* parse options */ | 
 | 	err = parse_options(sb, raw_data, silent, &debug, mnt_opt, &sb_info->options); | 
 | 	if (err) { | 
 | 		pr_err("sdcardfs: invalid options\n"); | 
 | 		goto out_freesbi; | 
 | 	} | 
 |  | 
 | 	/* set the lower superblock field of upper superblock */ | 
 | 	lower_sb = lower_path.dentry->d_sb; | 
 | 	atomic_inc(&lower_sb->s_active); | 
 | 	sdcardfs_set_lower_super(sb, lower_sb); | 
 |  | 
 | 	sb->s_stack_depth = lower_sb->s_stack_depth + 1; | 
 | 	if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) { | 
 | 		pr_err("sdcardfs: maximum fs stacking depth exceeded\n"); | 
 | 		err = -EINVAL; | 
 | 		goto out_sput; | 
 | 	} | 
 |  | 
 | 	/* inherit maxbytes from lower file system */ | 
 | 	sb->s_maxbytes = lower_sb->s_maxbytes; | 
 |  | 
 | 	/* | 
 | 	 * Our c/m/atime granularity is 1 ns because we may stack on file | 
 | 	 * systems whose granularity is as good. | 
 | 	 */ | 
 | 	sb->s_time_gran = 1; | 
 |  | 
 | 	sb->s_magic = SDCARDFS_SUPER_MAGIC; | 
 | 	sb->s_op = &sdcardfs_sops; | 
 |  | 
 | 	/* get a new inode and allocate our root dentry */ | 
 | 	inode = sdcardfs_iget(sb, d_inode(lower_path.dentry), 0); | 
 | 	if (IS_ERR(inode)) { | 
 | 		err = PTR_ERR(inode); | 
 | 		goto out_sput; | 
 | 	} | 
 | 	sb->s_root = d_make_root(inode); | 
 | 	if (!sb->s_root) { | 
 | 		err = -ENOMEM; | 
 | 		goto out_sput; | 
 | 	} | 
 | 	d_set_d_op(sb->s_root, &sdcardfs_ci_dops); | 
 |  | 
 | 	/* link the upper and lower dentries */ | 
 | 	sb->s_root->d_fsdata = NULL; | 
 | 	err = new_dentry_private_data(sb->s_root); | 
 | 	if (err) | 
 | 		goto out_freeroot; | 
 |  | 
 | 	/* set the lower dentries for s_root */ | 
 | 	sdcardfs_set_lower_path(sb->s_root, &lower_path); | 
 |  | 
 | 	/* | 
 | 	 * No need to call interpose because we already have a positive | 
 | 	 * dentry, which was instantiated by d_make_root.  Just need to | 
 | 	 * d_rehash it. | 
 | 	 */ | 
 | 	d_rehash(sb->s_root); | 
 |  | 
 | 	/* setup permission policy */ | 
 | 	sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); | 
 | 	mutex_lock(&sdcardfs_super_list_lock); | 
 | 	if (sb_info->options.multiuser) { | 
 | 		setup_derived_state(d_inode(sb->s_root), PERM_PRE_ROOT, | 
 | 				sb_info->options.fs_user_id, AID_ROOT); | 
 | 		snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name); | 
 | 	} else { | 
 | 		setup_derived_state(d_inode(sb->s_root), PERM_ROOT, | 
 | 				sb_info->options.fs_user_id, AID_ROOT); | 
 | 		snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); | 
 | 	} | 
 | 	fixup_tmp_permissions(d_inode(sb->s_root)); | 
 | 	sb_info->sb = sb; | 
 | 	list_add(&sb_info->list, &sdcardfs_super_list); | 
 | 	mutex_unlock(&sdcardfs_super_list_lock); | 
 |  | 
 | 	sb_info->fscrypt_nb.notifier_call = sdcardfs_on_fscrypt_key_removed; | 
 | 	fscrypt_register_key_removal_notifier(&sb_info->fscrypt_nb); | 
 |  | 
 | 	if (!silent) | 
 | 		pr_info("sdcardfs: mounted on top of %s type %s\n", | 
 | 				dev_name, lower_sb->s_type->name); | 
 | 	goto out; /* all is well */ | 
 |  | 
 | 	/* no longer needed: free_dentry_private_data(sb->s_root); */ | 
 | out_freeroot: | 
 | 	dput(sb->s_root); | 
 | 	sb->s_root = NULL; | 
 | out_sput: | 
 | 	/* drop refs we took earlier */ | 
 | 	atomic_dec(&lower_sb->s_active); | 
 | out_freesbi: | 
 | 	kfree(SDCARDFS_SB(sb)); | 
 | 	sb->s_fs_info = NULL; | 
 | out_free: | 
 | 	path_put(&lower_path); | 
 |  | 
 | out: | 
 | 	return err; | 
 | } | 
 |  | 
 | struct sdcardfs_mount_private { | 
 | 	struct vfsmount *mnt; | 
 | 	const char *dev_name; | 
 | 	void *raw_data; | 
 | }; | 
 |  | 
 | static int __sdcardfs_fill_super( | 
 | 	struct super_block *sb, | 
 | 	void *_priv, int silent) | 
 | { | 
 | 	struct sdcardfs_mount_private *priv = _priv; | 
 |  | 
 | 	return sdcardfs_read_super(priv->mnt, | 
 | 		sb, priv->dev_name, priv->raw_data, silent); | 
 | } | 
 |  | 
 | static struct dentry *sdcardfs_mount(struct vfsmount *mnt, | 
 | 		struct file_system_type *fs_type, int flags, | 
 | 			    const char *dev_name, void *raw_data) | 
 | { | 
 | 	struct sdcardfs_mount_private priv = { | 
 | 		.mnt = mnt, | 
 | 		.dev_name = dev_name, | 
 | 		.raw_data = raw_data | 
 | 	}; | 
 |  | 
 | 	return mount_nodev(fs_type, flags, | 
 | 		&priv, __sdcardfs_fill_super); | 
 | } | 
 |  | 
 | static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type, | 
 | 		    int flags, const char *dev_name, void *raw_data) | 
 | { | 
 | 	WARN(1, "sdcardfs does not support mount. Use mount2.\n"); | 
 | 	return ERR_PTR(-EINVAL); | 
 | } | 
 |  | 
 | void *sdcardfs_alloc_mnt_data(void) | 
 | { | 
 | 	return kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL); | 
 | } | 
 |  | 
 | void sdcardfs_kill_sb(struct super_block *sb) | 
 | { | 
 | 	struct sdcardfs_sb_info *sbi; | 
 |  | 
 | 	if (sb->s_magic == SDCARDFS_SUPER_MAGIC && sb->s_fs_info) { | 
 | 		sbi = SDCARDFS_SB(sb); | 
 |  | 
 | 		fscrypt_unregister_key_removal_notifier(&sbi->fscrypt_nb); | 
 |  | 
 | 		mutex_lock(&sdcardfs_super_list_lock); | 
 | 		list_del(&sbi->list); | 
 | 		mutex_unlock(&sdcardfs_super_list_lock); | 
 | 	} | 
 | 	kill_anon_super(sb); | 
 | } | 
 |  | 
 | static struct file_system_type sdcardfs_fs_type = { | 
 | 	.owner		= THIS_MODULE, | 
 | 	.name		= SDCARDFS_NAME, | 
 | 	.mount		= sdcardfs_mount_wrn, | 
 | 	.mount2		= sdcardfs_mount, | 
 | 	.alloc_mnt_data = sdcardfs_alloc_mnt_data, | 
 | 	.kill_sb	= sdcardfs_kill_sb, | 
 | 	.fs_flags	= 0, | 
 | }; | 
 | MODULE_ALIAS_FS(SDCARDFS_NAME); | 
 |  | 
 | static int __init init_sdcardfs_fs(void) | 
 | { | 
 | 	int err; | 
 |  | 
 | 	pr_info("Registering sdcardfs " SDCARDFS_VERSION "\n"); | 
 |  | 
 | 	err = sdcardfs_init_inode_cache(); | 
 | 	if (err) | 
 | 		goto out; | 
 | 	err = sdcardfs_init_dentry_cache(); | 
 | 	if (err) | 
 | 		goto out; | 
 | 	err = packagelist_init(); | 
 | 	if (err) | 
 | 		goto out; | 
 | 	err = register_filesystem(&sdcardfs_fs_type); | 
 | out: | 
 | 	if (err) { | 
 | 		sdcardfs_destroy_inode_cache(); | 
 | 		sdcardfs_destroy_dentry_cache(); | 
 | 		packagelist_exit(); | 
 | 	} | 
 | 	return err; | 
 | } | 
 |  | 
 | static void __exit exit_sdcardfs_fs(void) | 
 | { | 
 | 	sdcardfs_destroy_inode_cache(); | 
 | 	sdcardfs_destroy_dentry_cache(); | 
 | 	packagelist_exit(); | 
 | 	unregister_filesystem(&sdcardfs_fs_type); | 
 | 	pr_info("Completed sdcardfs module unload\n"); | 
 | } | 
 |  | 
 | /* Original wrapfs authors */ | 
 | MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University (http://www.fsl.cs.sunysb.edu/)"); | 
 |  | 
 | /* Original sdcardfs authors */ | 
 | MODULE_AUTHOR("Woojoong Lee, Daeho Jeong, Kitae Lee, Yeongjin Gil System Memory Lab., Samsung Electronics"); | 
 |  | 
 | /* Current maintainer */ | 
 | MODULE_AUTHOR("Daniel Rosenberg, Google"); | 
 | MODULE_DESCRIPTION("Sdcardfs " SDCARDFS_VERSION); | 
 | MODULE_LICENSE("GPL"); | 
 |  | 
 | module_init(init_sdcardfs_fs); | 
 | module_exit(exit_sdcardfs_fs); |