blob: 6fa40c040ee8c3d438a6e6636da62f97d4e01710 [file] [log] [blame]
/*
* 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;
#if ANDROID_VERSION >= 70000
struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry));
struct packagelist_data *pkgl_dat = SDCARDFS_SB(dentry->d_sb)->pkgl_id;
#endif
if (flags & LOOKUP_RCU)
return -ECHILD;
spin_lock(&dentry->d_lock);
if (IS_ROOT(dentry)) {
spin_unlock(&dentry->d_lock);
return 1;
}
if (dentry->d_flags & DCACHE_WILL_INVALIDATE) {
dentry->d_flags &= ~DCACHE_WILL_INVALIDATE;
__d_drop(dentry);
spin_unlock(&dentry->d_lock);
return 0;
}
spin_unlock(&dentry->d_lock);
/* check uninitialized obb_dentry and
* whether the base obbpath has been changed or not */
if (is_obbpath_invalid(dentry)) {
d_drop(dentry);
return 0;
}
#if ANDROID_VERSION >= 70000
if (info->perm == PERM_ANDROID_OBB &&
atomic_read(&pkgl_dat->newly_created)) {
d_drop(dentry);
atomic_set(&pkgl_dat->newly_created, 0);
return 0;
}
#endif
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) {
d_drop(dentry);
goto out;
}
}
spin_lock(&lower_dentry->d_lock);
if (d_unhashed(lower_dentry)) {
spin_unlock(&lower_dentry->d_lock);
d_drop(dentry);
err = 0;
goto out;
}
spin_unlock(&lower_dentry->d_lock);
if (parent_lower_dentry != lower_cur_parent_dentry) {
d_drop(dentry);
err = 0;
goto out;
}
if (dentry == lower_dentry) {
err = 0;
panic("sdcardfs: dentry is equal to lower_dentry\n");
goto out;
}
if (dentry < lower_dentry) {
spin_lock(&dentry->d_lock);
spin_lock(&lower_dentry->d_lock);
} else {
spin_lock(&lower_dentry->d_lock);
spin_lock(&dentry->d_lock);
}
if (dentry->d_name.len != lower_dentry->d_name.len) {
__d_drop(dentry);
err = 0;
} else if (strncasecmp(dentry->d_name.name, lower_dentry->d_name.name,
dentry->d_name.len) != 0) {
__d_drop(dentry);
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);
}
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;
}
static void sdcardfs_d_release(struct dentry *dentry)
{
/* 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);
return;
}
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 = vfat_striptail_len(qstr);
len = qstr->len;
hash = init_name_hash();
while (len--)
//hash = partial_name_hash(nls_tolower(t, *name++), hash);
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 *parent,
const struct dentry *dentry,
unsigned int len, const char *str, const struct qstr *name)
{
/* This function is copy of vfat_cmpi */
// FIXME Should we support national language?
//struct nls_table *t = MSDOS_SB(parent->d_sb)->nls_io;
//unsigned int alen, blen;
/* A filename cannot end in '.' or we treat it like it has none */
/*
alen = vfat_striptail_len(name);
blen = __vfat_striptail_len(len, str);
if (alen == blen) {
if (nls_strnicmp(t, name->name, str, alen) == 0)
return 0;
}
*/
if (name->len == len) {
if (strncasecmp(name->name, str, len) == 0)
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_release = sdcardfs_d_release,
.d_hash = sdcardfs_hash_ci,
.d_compare = sdcardfs_cmp_ci,
.d_canonical_path = sdcardfs_canonical_path,
};