/**
* eCryptfs: Linux filesystem encryption layer
*
* Copyright (C) 1997-2004 Erez Zadok
* Copyright (C) 2001-2004 Stony Brook University
* Copyright (C) 2004-2006 International Business Machines Corp.
* Author(s): Michael A. Halcrow <mahalcro@us.ibm.com>
* Michael C. Thompsion <mcthomps@us.ibm.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#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/crypto.h>
#include <linux/fs_stack.h>
#include "ecryptfs_kernel.h"
static struct dentry *lock_parent(struct dentry *dentry)
{
struct dentry *dir;
dir = dget(dentry->d_parent);
mutex_lock(&(dir->d_inode->i_mutex));
return dir;
}
static void unlock_parent(struct dentry *dentry)
{
mutex_unlock(&(dentry->d_parent->d_inode->i_mutex));
dput(dentry->d_parent);
}
static void unlock_dir(struct dentry *dir)
{
mutex_unlock(&dir->d_inode->i_mutex);
dput(dir);
}
/**
* ecryptfs_create_underlying_file
* @lower_dir_inode: inode of the parent in the lower fs of the new file
* @lower_dentry: New file's dentry in the lower fs
* @ecryptfs_dentry: New file's dentry in ecryptfs
* @mode: The mode of the new file
* @nd: nameidata of ecryptfs' parent's dentry & vfsmount
*
* Creates the file in the lower file system.
*
* Returns zero on success; non-zero on error condition
*/
static int
ecryptfs_create_underlying_file(struct inode *lower_dir_inode,
struct dentry *dentry, int mode,
struct nameidata *nd)
{
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry);
struct dentry *dentry_save;
struct vfsmount *vfsmount_save;
int rc;
dentry_save = nd->dentry;
vfsmount_save = nd->mnt;
nd->dentry = lower_dentry;
nd->mnt = lower_mnt;
rc = vfs_create(lower_dir_inode, lower_dentry, mode, nd);
nd->dentry = dentry_save;
nd->mnt = vfsmount_save;
return rc;
}
/**
* ecryptfs_do_create
* @directory_inode: inode of the new file's dentry's parent in ecryptfs
* @ecryptfs_dentry: New file's dentry in ecryptfs
* @mode: The mode of the new file
* @nd: nameidata of ecryptfs' parent's dentry & vfsmount
*
* Creates the underlying file and the eCryptfs inode which will link to
* it. It will also update the eCryptfs directory inode to mimic the
* stat of the lower directory inode.
*
* Returns zero on success; non-zero on error condition
*/
static int
ecryptfs_do_create(struct inode *directory_inode,
struct dentry *ecryptfs_dentry, int mode,
struct nameidata *nd)
{
int rc;
struct dentry *lower_dentry;
struct dentry *lower_dir_dentry;
lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry);
lower_dir_dentry = lock_parent(lower_dentry);
if (unlikely(IS_ERR(lower_dir_dentry))) {
ecryptfs_printk(KERN_ERR, "Error locking directory of "
"dentry\n");
rc = PTR_ERR(lower_dir_dentry);
goto out;
}
rc = ecryptfs_create_underlying_file(lower_dir_dentry->d_inode,
ecryptfs_dentry, mode, nd);
if (unlikely(rc)) {
ecryptfs_printk(KERN_ERR,
"Failure to create underlying file\n");
goto out_lock;
}
rc = ecryptfs_interpose(lower_dentry, ecryptfs_dentry,
directory_inode->i_sb, 0);
if (rc) {
ecryptfs_printk(KERN_ERR, "Failure in ecryptfs_interpose\n");
goto out_lock;
}
fsstack_copy_attr_times(directory_inode, lower_dir_dentry->d_inode);
fsstack_copy_inode_size(directory_inode, lower_dir_dentry->d_inode);
out_lock:
unlock_dir(lower_dir_dentry);
out:
return rc;
}
/**
* grow_file
* @ecryptfs_dentry: the ecryptfs dentry
* @lower_file: The lower file
* @inode: The ecryptfs inode
* @lower_inode: The lower inode
*
* This is the code which will grow the file to its correct size.
*/
static int grow_file(struct dentry *ecryptfs_dentry, struct file *lower_file,
struct inode *inode, struct inode *lower_inode)
{
int rc = 0;
struct file fake_file;
struct ecryptfs_file_info tmp_file_info;
memset(&fake_file, 0, sizeof(fake_file));
fake_file.f_path.dentry = ecryptfs_dentry;
memset(&tmp_file_info, 0, sizeof(tmp_file_info));
ecryptfs_set_file_private(&fake_file, &tmp_file_info);
ecryptfs_set_file_lower(&fake_file, lower_file);
rc = ecryptfs_fill_zeros(&fake_file, 1);
if (rc) {
ECRYPTFS_SET_FLAG(
ecryptfs_inode_to_private(i