| /* | 
 |  * fs/sdcardfs/dentry.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/ctype.h" | 
 |  | 
 | /* | 
 |  * returns: -ERRNO if error (returned to user) | 
 |  *          0: tell VFS to invalidate dentry | 
 |  *          1: dentry is valid | 
 |  */ | 
 | static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) | 
 | { | 
 | 	int err = 1; | 
 | 	struct path parent_lower_path, lower_path; | 
 | 	struct dentry *parent_dentry = NULL; | 
 | 	struct dentry *parent_lower_dentry = NULL; | 
 | 	struct dentry *lower_cur_parent_dentry = NULL; | 
 | 	struct dentry *lower_dentry = NULL; | 
 | 	struct inode *inode; | 
 | 	struct sdcardfs_inode_data *data; | 
 |  | 
 | 	if (flags & LOOKUP_RCU) | 
 | 		return -ECHILD; | 
 |  | 
 | 	spin_lock(&dentry->d_lock); | 
 | 	if (IS_ROOT(dentry)) { | 
 | 		spin_unlock(&dentry->d_lock); | 
 | 		return 1; | 
 | 	} | 
 | 	spin_unlock(&dentry->d_lock); | 
 |  | 
 | 	/* check uninitialized obb_dentry and | 
 | 	 * whether the base obbpath has been changed or not | 
 | 	 */ | 
 | 	if (is_obbpath_invalid(dentry)) { | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	parent_dentry = dget_parent(dentry); | 
 | 	sdcardfs_get_lower_path(parent_dentry, &parent_lower_path); | 
 | 	sdcardfs_get_real_lower(dentry, &lower_path); | 
 | 	parent_lower_dentry = parent_lower_path.dentry; | 
 | 	lower_dentry = lower_path.dentry; | 
 | 	lower_cur_parent_dentry = dget_parent(lower_dentry); | 
 |  | 
 | 	if ((lower_dentry->d_flags & DCACHE_OP_REVALIDATE)) { | 
 | 		err = lower_dentry->d_op->d_revalidate(lower_dentry, flags); | 
 | 		if (err == 0) { | 
 | 			goto out; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	spin_lock(&lower_dentry->d_lock); | 
 | 	if (d_unhashed(lower_dentry)) { | 
 | 		spin_unlock(&lower_dentry->d_lock); | 
 | 		err = 0; | 
 | 		goto out; | 
 | 	} | 
 | 	spin_unlock(&lower_dentry->d_lock); | 
 |  | 
 | 	if (parent_lower_dentry != lower_cur_parent_dentry) { | 
 | 		err = 0; | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	if (dentry < lower_dentry) { | 
 | 		spin_lock(&dentry->d_lock); | 
 | 		spin_lock_nested(&lower_dentry->d_lock, DENTRY_D_LOCK_NESTED); | 
 | 	} else { | 
 | 		spin_lock(&lower_dentry->d_lock); | 
 | 		spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); | 
 | 	} | 
 |  | 
 | 	if (!qstr_case_eq(&dentry->d_name, &lower_dentry->d_name)) { | 
 | 		err = 0; | 
 | 	} | 
 |  | 
 | 	if (dentry < lower_dentry) { | 
 | 		spin_unlock(&lower_dentry->d_lock); | 
 | 		spin_unlock(&dentry->d_lock); | 
 | 	} else { | 
 | 		spin_unlock(&dentry->d_lock); | 
 | 		spin_unlock(&lower_dentry->d_lock); | 
 | 	} | 
 | 	if (!err) | 
 | 		goto out; | 
 |  | 
 | 	/* If our top's inode is gone, we may be out of date */ | 
 | 	inode = igrab(d_inode(dentry)); | 
 | 	if (inode) { | 
 | 		data = top_data_get(SDCARDFS_I(inode)); | 
 | 		if (!data || data->abandoned) { | 
 | 			err = 0; | 
 | 		} | 
 | 		if (data) | 
 | 			data_put(data); | 
 | 		iput(inode); | 
 | 	} | 
 |  | 
 | out: | 
 | 	dput(parent_dentry); | 
 | 	dput(lower_cur_parent_dentry); | 
 | 	sdcardfs_put_lower_path(parent_dentry, &parent_lower_path); | 
 | 	sdcardfs_put_real_lower(dentry, &lower_path); | 
 | 	return err; | 
 | } | 
 |  | 
 | /* 1 = delete, 0 = cache */ | 
 | static int sdcardfs_d_delete(const struct dentry *d) | 
 | { | 
 | 	return SDCARDFS_SB(d->d_sb)->options.nocache ? 1 : 0; | 
 | } | 
 |  | 
 | static void sdcardfs_d_release(struct dentry *dentry) | 
 | { | 
 | 	if (!dentry || !dentry->d_fsdata) | 
 | 		return; | 
 | 	/* release and reset the lower paths */ | 
 | 	if (has_graft_path(dentry)) | 
 | 		sdcardfs_put_reset_orig_path(dentry); | 
 | 	sdcardfs_put_reset_lower_path(dentry); | 
 | 	free_dentry_private_data(dentry); | 
 | } | 
 |  | 
 | static int sdcardfs_hash_ci(const struct dentry *dentry, | 
 | 				struct qstr *qstr) | 
 | { | 
 | 	/* | 
 | 	 * This function is copy of vfat_hashi. | 
 | 	 * FIXME Should we support national language? | 
 | 	 *       Refer to vfat_hashi() | 
 | 	 * struct nls_table *t = MSDOS_SB(dentry->d_sb)->nls_io; | 
 | 	 */ | 
 | 	const unsigned char *name; | 
 | 	unsigned int len; | 
 | 	unsigned long hash; | 
 |  | 
 | 	name = qstr->name; | 
 | 	len = qstr->len; | 
 |  | 
 | 	hash = init_name_hash(dentry); | 
 | 	while (len--) | 
 | 		hash = partial_name_hash(tolower(*name++), hash); | 
 | 	qstr->hash = end_name_hash(hash); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* | 
 |  * Case insensitive compare of two vfat names. | 
 |  */ | 
 | static int sdcardfs_cmp_ci(const struct dentry *dentry, | 
 | 		unsigned int len, const char *str, const struct qstr *name) | 
 | { | 
 | 	/* FIXME Should we support national language? */ | 
 |  | 
 | 	if (name->len == len) { | 
 | 		if (str_n_case_eq(name->name, str, len)) | 
 | 			return 0; | 
 | 	} | 
 | 	return 1; | 
 | } | 
 |  | 
 | static void sdcardfs_canonical_path(const struct path *path, | 
 | 				struct path *actual_path) | 
 | { | 
 | 	sdcardfs_get_real_lower(path->dentry, actual_path); | 
 | } | 
 |  | 
 | const struct dentry_operations sdcardfs_ci_dops = { | 
 | 	.d_revalidate	= sdcardfs_d_revalidate, | 
 | 	.d_delete	= sdcardfs_d_delete, | 
 | 	.d_release	= sdcardfs_d_release, | 
 | 	.d_hash	= sdcardfs_hash_ci, | 
 | 	.d_compare	= sdcardfs_cmp_ci, | 
 | 	.d_canonical_path = sdcardfs_canonical_path, | 
 | }; | 
 |  |