blob: 153103f9d0409daf43e59867ca3dde78244c49e6 [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"
#ifdef CONFIG_EXT4CRYPT_SDP
extern
int __fscrypt_sdp_d_delete(const struct dentry *dentry, int dek_is_locked);
#endif
/*
* 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)
{
#ifdef CONFIG_EXT4CRYPT_SDP
struct sdcardfs_dentry_info *info = SDCARDFS_D(d);
struct path *lower_path = &info->lower_path;
unsigned long lower_fs_magic = lower_path->mnt->mnt_sb->s_magic;
/*
* Always delete sdcardfs dentries for lower SDP ones regardless of
* container lock state
*/
if (lower_fs_magic == EXT4_SUPER_MAGIC ||
lower_fs_magic == F2FS_SUPER_MAGIC) {
if (__fscrypt_sdp_d_delete(lower_path->dentry, 1))
return 1;
}
#endif
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();
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 *parent,
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,
};