| /* | 
 |  * fs/sdcardfs/lookup.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/delay.h" | 
 |  | 
 | /* The dentry cache is just so we have properly sized dentries */ | 
 | static struct kmem_cache *sdcardfs_dentry_cachep; | 
 |  | 
 | int sdcardfs_init_dentry_cache(void) | 
 | { | 
 | 	sdcardfs_dentry_cachep = | 
 | 		kmem_cache_create("sdcardfs_dentry", | 
 | 				  sizeof(struct sdcardfs_dentry_info), | 
 | 				  0, SLAB_RECLAIM_ACCOUNT, NULL); | 
 |  | 
 | 	return sdcardfs_dentry_cachep ? 0 : -ENOMEM; | 
 | } | 
 |  | 
 | void sdcardfs_destroy_dentry_cache(void) | 
 | { | 
 | 	kmem_cache_destroy(sdcardfs_dentry_cachep); | 
 | } | 
 |  | 
 | void free_dentry_private_data(struct dentry *dentry) | 
 | { | 
 | 	kmem_cache_free(sdcardfs_dentry_cachep, dentry->d_fsdata); | 
 | 	dentry->d_fsdata = NULL; | 
 | } | 
 |  | 
 | /* allocate new dentry private data */ | 
 | int new_dentry_private_data(struct dentry *dentry) | 
 | { | 
 | 	struct sdcardfs_dentry_info *info = SDCARDFS_D(dentry); | 
 |  | 
 | 	/* use zalloc to init dentry_info.lower_path */ | 
 | 	info = kmem_cache_zalloc(sdcardfs_dentry_cachep, GFP_ATOMIC); | 
 | 	if (!info) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	spin_lock_init(&info->lock); | 
 | 	dentry->d_fsdata = info; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | 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); | 
 | 	userid_t current_userid = SDCARDFS_I(inode)->data->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 */ | 
 | } | 
 |  | 
 | static int sdcardfs_inode_set(struct inode *inode, void *lower_inode) | 
 | { | 
 | 	/* we do actual inode initialization in sdcardfs_iget */ | 
 | 	return 0; | 
 | } | 
 |  | 
 | 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 */ | 
 |  | 
 | 	if (!igrab(lower_inode)) | 
 | 		return ERR_PTR(-ESTALE); | 
 |  | 
 | 	data.id = id; | 
 | 	data.lower_inode = lower_inode; | 
 | 	inode = iget5_locked(sb, /* our superblock */ | 
 | 			     /* | 
 | 			      * hashval: we use inode number, but we can | 
 | 			      * also use "(unsigned long)lower_inode" | 
 | 			      * instead. | 
 | 			      */ | 
 | 			     lower_inode->i_ino, /* hashval */ | 
 | 			     sdcardfs_inode_test, /* inode comparison function */ | 
 | 			     sdcardfs_inode_set, /* inode init function */ | 
 | 			     &data); /* data passed to test+set fxns */ | 
 | 	if (!inode) { | 
 | 		iput(lower_inode); | 
 | 		return ERR_PTR(-ENOMEM); | 
 | 	} | 
 | 	/* if found a cached inode, then just return it (after iput) */ | 
 | 	if (!(inode->i_state & I_NEW)) { | 
 | 		iput(lower_inode); | 
 | 		return inode; | 
 | 	} | 
 |  | 
 | 	/* initialize new inode */ | 
 | 	info = SDCARDFS_I(inode); | 
 |  | 
 | 	inode->i_ino = lower_inode->i_ino; | 
 | 	sdcardfs_set_lower_inode(inode, lower_inode); | 
 |  | 
 | 	inode_inc_iversion_raw(inode); | 
 |  | 
 | 	/* use different set of inode ops for symlinks & directories */ | 
 | 	if (S_ISDIR(lower_inode->i_mode)) | 
 | 		inode->i_op = &sdcardfs_dir_iops; | 
 | 	else if (S_ISLNK(lower_inode->i_mode)) | 
 | 		inode->i_op = &sdcardfs_symlink_iops; | 
 | 	else | 
 | 		inode->i_op = &sdcardfs_main_iops; | 
 |  | 
 | 	/* use different set of file ops for directories */ | 
 | 	if (S_ISDIR(lower_inode->i_mode)) | 
 | 		inode->i_fop = &sdcardfs_dir_fops; | 
 | 	else | 
 | 		inode->i_fop = &sdcardfs_main_fops; | 
 |  | 
 | 	inode->i_mapping->a_ops = &sdcardfs_aops; | 
 |  | 
 | 	inode->i_atime.tv_sec = 0; | 
 | 	inode->i_atime.tv_nsec = 0; | 
 | 	inode->i_mtime.tv_sec = 0; | 
 | 	inode->i_mtime.tv_nsec = 0; | 
 | 	inode->i_ctime.tv_sec = 0; | 
 | 	inode->i_ctime.tv_nsec = 0; | 
 |  | 
 | 	/* properly initialize special inodes */ | 
 | 	if (S_ISBLK(lower_inode->i_mode) || S_ISCHR(lower_inode->i_mode) || | 
 | 	    S_ISFIFO(lower_inode->i_mode) || S_ISSOCK(lower_inode->i_mode)) | 
 | 		init_special_inode(inode, lower_inode->i_mode, | 
 | 				   lower_inode->i_rdev); | 
 |  | 
 | 	/* all well, copy inode attributes */ | 
 | 	sdcardfs_copy_and_fix_attrs(inode, lower_inode); | 
 | 	fsstack_copy_inode_size(inode, lower_inode); | 
 |  | 
 | 	unlock_new_inode(inode); | 
 | 	return inode; | 
 | } | 
 |  | 
 | /* | 
 |  * Helper interpose routine, called directly by ->lookup to handle | 
 |  * spliced dentries. | 
 |  */ | 
 | static struct dentry *__sdcardfs_interpose(struct dentry *dentry, | 
 | 					 struct super_block *sb, | 
 | 					 struct path *lower_path, | 
 | 					 userid_t id) | 
 | { | 
 | 	struct inode *inode; | 
 | 	struct inode *lower_inode; | 
 | 	struct super_block *lower_sb; | 
 | 	struct dentry *ret_dentry; | 
 |  | 
 | 	lower_inode = d_inode(lower_path->dentry); | 
 | 	lower_sb = sdcardfs_lower_super(sb); | 
 |  | 
 | 	/* check that the lower file system didn't cross a mount point */ | 
 | 	if (lower_inode->i_sb != lower_sb) { | 
 | 		ret_dentry = ERR_PTR(-EXDEV); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * We allocate our new inode below by calling sdcardfs_iget, | 
 | 	 * which will initialize some of the new inode's fields | 
 | 	 */ | 
 |  | 
 | 	/* inherit lower inode number for sdcardfs's inode */ | 
 | 	inode = sdcardfs_iget(sb, lower_inode, id); | 
 | 	if (IS_ERR(inode)) { | 
 | 		ret_dentry = ERR_CAST(inode); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	ret_dentry = d_splice_alias(inode, dentry); | 
 | 	dentry = ret_dentry ?: dentry; | 
 | 	if (!IS_ERR(dentry)) | 
 | 		update_derived_permission_lock(dentry); | 
 | out: | 
 | 	return ret_dentry; | 
 | } | 
 |  | 
 | /* | 
 |  * Connect an sdcardfs inode dentry/inode with several lower ones.  This is | 
 |  * the classic stackable file system "vnode interposition" action. | 
 |  * | 
 |  * @dentry: sdcardfs's dentry which interposes on lower one | 
 |  * @sb: sdcardfs's super_block | 
 |  * @lower_path: the lower path (caller does path_get/put) | 
 |  */ | 
 | int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, | 
 | 		     struct path *lower_path, userid_t id) | 
 | { | 
 | 	struct dentry *ret_dentry; | 
 |  | 
 | 	ret_dentry = __sdcardfs_interpose(dentry, sb, lower_path, id); | 
 | 	return PTR_ERR(ret_dentry); | 
 | } | 
 |  | 
 | struct sdcardfs_name_data { | 
 | 	struct dir_context ctx; | 
 | 	const struct qstr *to_find; | 
 | 	char *name; | 
 | 	bool found; | 
 | }; | 
 |  | 
 | static int sdcardfs_name_match(struct dir_context *ctx, const char *name, | 
 | 		int namelen, loff_t offset, u64 ino, unsigned int d_type) | 
 | { | 
 | 	struct sdcardfs_name_data *buf = container_of(ctx, struct sdcardfs_name_data, ctx); | 
 | 	struct qstr candidate = QSTR_INIT(name, namelen); | 
 |  | 
 | 	if (qstr_case_eq(buf->to_find, &candidate)) { | 
 | 		memcpy(buf->name, name, namelen); | 
 | 		buf->name[namelen] = 0; | 
 | 		buf->found = true; | 
 | 		return 1; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* | 
 |  * Main driver function for sdcardfs's lookup. | 
 |  * | 
 |  * Returns: NULL (ok), ERR_PTR if an error occurred. | 
 |  * 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, userid_t id) | 
 | { | 
 | 	int err = 0; | 
 | 	struct vfsmount *lower_dir_mnt; | 
 | 	struct dentry *lower_dir_dentry = NULL; | 
 | 	struct dentry *lower_dentry; | 
 | 	const struct qstr *name; | 
 | 	struct path lower_path; | 
 | 	struct qstr dname; | 
 | 	struct dentry *ret_dentry = NULL; | 
 | 	struct sdcardfs_sb_info *sbi; | 
 |  | 
 | 	sbi = SDCARDFS_SB(dentry->d_sb); | 
 | 	/* must initialize dentry operations */ | 
 | 	d_set_d_op(dentry, &sdcardfs_ci_dops); | 
 |  | 
 | 	if (IS_ROOT(dentry)) | 
 | 		goto out; | 
 |  | 
 | 	name = &dentry->d_name; | 
 |  | 
 | 	/* now start the actual lookup procedure */ | 
 | 	lower_dir_dentry = lower_parent_path->dentry; | 
 | 	lower_dir_mnt = lower_parent_path->mnt; | 
 |  | 
 | 	/* Use vfs_path_lookup to check if the dentry exists or not */ | 
 | 	err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name->name, 0, | 
 | 				&lower_path); | 
 | 	/* check for other cases */ | 
 | 	if (err == -ENOENT) { | 
 | 		struct file *file; | 
 | 		const struct cred *cred = current_cred(); | 
 |  | 
 | 		struct sdcardfs_name_data buffer = { | 
 | 			.ctx.actor = sdcardfs_name_match, | 
 | 			.to_find = name, | 
 | 			.name = __getname(), | 
 | 			.found = false, | 
 | 		}; | 
 |  | 
 | 		if (!buffer.name) { | 
 | 			err = -ENOMEM; | 
 | 			goto out; | 
 | 		} | 
 | 		file = dentry_open(lower_parent_path, O_RDONLY, cred); | 
 | 		if (IS_ERR(file)) { | 
 | 			err = PTR_ERR(file); | 
 | 			goto put_name; | 
 | 		} | 
 | 		err = iterate_dir(file, &buffer.ctx); | 
 | 		fput(file); | 
 | 		if (err) | 
 | 			goto put_name; | 
 |  | 
 | 		if (buffer.found) | 
 | 			err = vfs_path_lookup(lower_dir_dentry, | 
 | 						lower_dir_mnt, | 
 | 						buffer.name, 0, | 
 | 						&lower_path); | 
 | 		else | 
 | 			err = -ENOENT; | 
 | put_name: | 
 | 		__putname(buffer.name); | 
 | 	} | 
 |  | 
 | 	/* no error: handle positive dentries */ | 
 | 	if (!err) { | 
 | 		/* check if the dentry is an obb dentry | 
 | 		 * if true, the lower_inode must be replaced with | 
 | 		 * the inode of the graft path | 
 | 		 */ | 
 |  | 
 | 		if (need_graft_path(dentry)) { | 
 |  | 
 | 			/* setup_obb_dentry() | 
 | 			 * The lower_path will be stored to the dentry's orig_path | 
 | 			 * and the base obbpath will be copyed to the lower_path variable. | 
 | 			 * if an error returned, there's no change in the lower_path | 
 | 			 * returns: -ERRNO if error (0: no error) | 
 | 			 */ | 
 | 			err = setup_obb_dentry(dentry, &lower_path); | 
 |  | 
 | 			if (err) { | 
 | 				/* if the sbi->obbpath is not available, we can optionally | 
 | 				 * setup the lower_path with its orig_path. | 
 | 				 * but, the current implementation just returns an error | 
 | 				 * because the sdcard daemon also regards this case as | 
 | 				 * a lookup fail. | 
 | 				 */ | 
 | 				pr_info("sdcardfs: base obbpath is not available\n"); | 
 | 				sdcardfs_put_reset_orig_path(dentry); | 
 | 				goto out; | 
 | 			} | 
 | 		} | 
 |  | 
 | 		sdcardfs_set_lower_path(dentry, &lower_path); | 
 | 		ret_dentry = | 
 | 			__sdcardfs_interpose(dentry, dentry->d_sb, &lower_path, id); | 
 | 		if (IS_ERR(ret_dentry)) { | 
 | 			err = PTR_ERR(ret_dentry); | 
 | 			 /* path_put underlying path on error */ | 
 | 			sdcardfs_put_reset_lower_path(dentry); | 
 | 		} | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * We don't consider ENOENT an error, and we want to return a | 
 | 	 * negative dentry. | 
 | 	 */ | 
 | 	if (err && err != -ENOENT) | 
 | 		goto out; | 
 |  | 
 | 	/* instatiate a new negative dentry */ | 
 | 	dname.name = name->name; | 
 | 	dname.len = name->len; | 
 |  | 
 | 	/* See if the low-level filesystem might want | 
 | 	 * to use its own hash | 
 | 	 */ | 
 | 	lower_dentry = d_hash_and_lookup(lower_dir_dentry, &dname); | 
 | 	if (IS_ERR(lower_dentry)) | 
 | 		return lower_dentry; | 
 |  | 
 | 	if (!lower_dentry) { | 
 | 		/* We called vfs_path_lookup earlier, and did not get a negative | 
 | 		 * dentry then. Don't confuse the lower filesystem by forcing | 
 | 		 * one on it now... | 
 | 		 */ | 
 | 		err = -ENOENT; | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	lower_path.dentry = lower_dentry; | 
 | 	lower_path.mnt = mntget(lower_dir_mnt); | 
 | 	sdcardfs_set_lower_path(dentry, &lower_path); | 
 |  | 
 | 	/* | 
 | 	 * If the intent is to create a file, then don't return an error, so | 
 | 	 * the VFS will continue the process of making this negative dentry | 
 | 	 * into a positive one. | 
 | 	 */ | 
 | 	if (flags & (LOOKUP_CREATE|LOOKUP_RENAME_TARGET)) | 
 | 		err = 0; | 
 |  | 
 | out: | 
 | 	if (err) | 
 | 		return ERR_PTR(err); | 
 | 	return ret_dentry; | 
 | } | 
 |  | 
 | /* | 
 |  * On success: | 
 |  * fills dentry object appropriate values and returns NULL. | 
 |  * On fail (== error) | 
 |  * returns error ptr | 
 |  * | 
 |  * @dir : Parent inode. | 
 |  * @dentry : Target dentry to lookup. we should set each of fields. | 
 |  *	     (dentry->d_name is initialized already) | 
 |  * @nd : nameidata of parent inode | 
 |  */ | 
 | struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, | 
 | 			     unsigned int flags) | 
 | { | 
 | 	struct dentry *ret = NULL, *parent; | 
 | 	struct path lower_parent_path; | 
 | 	int err = 0; | 
 | 	const struct cred *saved_cred = NULL; | 
 |  | 
 | 	parent = dget_parent(dentry); | 
 |  | 
 | 	if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { | 
 | 		ret = ERR_PTR(-EACCES); | 
 | 		goto out_err; | 
 | 	} | 
 |  | 
 | 	/* save current_cred and override it */ | 
 | 	saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb), | 
 | 						SDCARDFS_I(dir)->data); | 
 | 	if (!saved_cred) { | 
 | 		ret = ERR_PTR(-ENOMEM); | 
 | 		goto out_err; | 
 | 	} | 
 |  | 
 | 	sdcardfs_get_lower_path(parent, &lower_parent_path); | 
 |  | 
 | 	/* allocate dentry private data.  We free it in ->d_release */ | 
 | 	err = new_dentry_private_data(dentry); | 
 | 	if (err) { | 
 | 		ret = ERR_PTR(err); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path, | 
 | 				SDCARDFS_I(dir)->data->userid); | 
 | 	if (IS_ERR(ret)) | 
 | 		goto out; | 
 | 	if (ret) | 
 | 		dentry = ret; | 
 | 	if (d_inode(dentry)) { | 
 | 		fsstack_copy_attr_times(d_inode(dentry), | 
 | 					sdcardfs_lower_inode(d_inode(dentry))); | 
 | 		/* get derived permission */ | 
 | 		get_derived_permission(parent, dentry); | 
 | 		fixup_tmp_permissions(d_inode(dentry)); | 
 | 		fixup_lower_ownership(dentry, dentry->d_name.name); | 
 | 	} | 
 | 	/* update parent directory's atime */ | 
 | 	fsstack_copy_attr_atime(d_inode(parent), | 
 | 				sdcardfs_lower_inode(d_inode(parent))); | 
 |  | 
 | out: | 
 | 	sdcardfs_put_lower_path(parent, &lower_parent_path); | 
 | 	revert_fsids(saved_cred); | 
 | out_err: | 
 | 	dput(parent); | 
 | 	return ret; | 
 | } |