// SPDX-License-Identifier: GPL-2.0-or-later
/**
* eCryptfs: Linux filesystem encryption layer
*
* Copyright (C) 1997-2004 Erez Zadok
* Copyright (C) 2001-2004 Stony Brook University
* Copyright (C) 2004-2007 International Business Machines Corp.
* Author(s): Michael A. Halcrow <mahalcro@us.ibm.com>
* Michael C. Thompsion <mcthomps@us.ibm.com>
*/
#include <linux/file.h>
#include <linux/vmalloc.h>
#include <linux/pagemap.h>
#include <linux/dcache.h>
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/fs_stack.h>
#include <linux/slab.h>
#include <linux/xattr.h>
#include <asm/unaligned.h>
#include "ecryptfs_kernel.h"
static struct dentry *lock_parent(struct dentry *dentry)
{
struct dentry *dir;
dir = dget_parent(dentry);
inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
return dir;
}
static void unlock_dir(struct dentry *dir)
{
inode_unlock(d_inode(dir));
dput(dir);
}
static int ecryptfs_inode_test(struct inode *inode, void *lower_inode)
{
return ecryptfs_inode_to_lower(inode) == lower_inode;
}
static int ecryptfs_inode_set(struct inode *inode, void *opaque)
{
struct inode *lower_inode = opaque;
ecryptfs_set_inode_lower(inode, lower_inode);
fsstack_copy_attr_all(inode, lower_inode);
/* i_size will be overwritten for encrypted regular files */
fsstack_copy_inode_size(inode, lower_inode);
inode->i_ino = lower_inode->i_ino;
inode->i_mapping->a_ops = &ecryptfs_aops;
if (S_ISLNK(inode->i_mode))
inode->i_op = &ecryptfs_symlink_iops;
else if (S_ISDIR(inode->i_mode))
inode->i_op = &ecryptfs_dir_iops;
else
inode->i_op = &ecryptfs_main_iops;
if (S_ISDIR(inode->i_mode))
inode->i_fop = &ecryptfs_dir_fops;
else if (special_file(inode->i_mode))
init_special_inode(inode, inode->i_mode, inode->i_rdev);
else
inode->i_fop = &ecryptfs_main_fops;
return 0;
}
static struct inode *__ecryptfs_get_inode(struct inode *lower_inode,
struct super_block *sb)
{
struct inode *inode;
if (lower_inode->i_sb != ecryptfs_superblock_to_lower(sb))
return ERR_PTR(-EXDEV);
if (!igrab(lower_inode))
return ERR_PTR(-ESTALE);
inode = iget5_locked(sb, (unsigned long)lower_inode,
ecryptfs_inode_test, ecryptfs_inode_set,
lower_inode);
if (!inode) {
iput(lower_inode);
return ERR_PTR(-EACCES);
}
if (!(inode->i_state & I_NEW))
iput(lower_inode);
return inode;
}
struct inode *ecryptfs_get_inode(struct inode *lower_inode,
struct super_block *sb)
{
struct inode *inode = __ecryptfs_get_inode(lower_inode, sb);
if (!IS_ERR(inode) && (inode->i_state & I_NEW))
unlock_new_inode(inode);
return inode;
}
/**
* ecryptfs_interpose
* @lower_dentry: Existing dentry in the lower filesystem
* @dentry: ecryptfs' dentry
* @sb: ecryptfs's super_block
*
* Interposes upper and lower dentries.
*
* Returns zero on success; non-zero otherwise
*/
static int ecryptfs_interpose(struct dentry *lower_dentry,
struct dentry *dentry, struct super_block *sb)
{
struct inode *inode = ecryptfs_get_inode(d_inode(lower_dentry), sb);
if