| /* |
| * 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, |
| }; |
| |