/** * ecryptfs_lookup_one_lower * @ecryptfs_dentry: The eCryptfs dentry that we are looking up * @lower_dir_dentry: lower parent directory * * Get the lower dentry from vfs. If lower dentry does not exist yet, * create it. */ static struct dentry * ecryptfs_lookup_one_lower(struct dentry *ecryptfs_dentry, struct dentry *lower_dir_dentry) { struct nameidata nd; struct vfsmount *lower_mnt; struct qstr *name; int err; name = &ecryptfs_dentry->d_name; lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt( ecryptfs_dentry->d_parent)); err = vfs_path_lookup(lower_dir_dentry, lower_mnt, name->name , 0, &nd); mntput(lower_mnt); if (!err) { /* we dont need the mount */ mntput(nd.path.mnt); return nd.path.dentry; } if (err != -ENOENT) return ERR_PTR(err); /* create a new lower dentry */ return ecryptfs_new_lower_dentry(name, lower_dir_dentry, &nd); }
/** * ecryptfs_create_underlying_file * @lower_dir_inode: inode of the parent in the lower fs of the new file * @dentry: New file's dentry * @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; unsigned int flags_save; int rc; if (nd) { dentry_save = nd->path.dentry; vfsmount_save = nd->path.mnt; flags_save = nd->flags; nd->path.dentry = lower_dentry; nd->path.mnt = lower_mnt; nd->flags &= ~LOOKUP_OPEN; } rc = vfs_create(lower_dir_inode, lower_dentry, mode, nd); if (nd) { nd->path.dentry = dentry_save; nd->path.mnt = vfsmount_save; nd->flags = flags_save; } return rc; }
/** * ecryptfs_d_revalidate - revalidate an ecryptfs dentry * @dentry: The ecryptfs dentry * @nd: The associated nameidata * * Called when the VFS needs to revalidate a dentry. This * is called whenever a name lookup finds a dentry in the * dcache. Most filesystems leave this as NULL, because all their * dentries in the dcache are valid. * * Returns 1 if valid, 0 otherwise. * */ static int ecryptfs_d_revalidate(struct dentry *dentry, 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 = 1; if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate) goto out; dentry_save = nd->dentry; vfsmount_save = nd->mnt; nd->dentry = lower_dentry; nd->mnt = lower_mnt; rc = lower_dentry->d_op->d_revalidate(lower_dentry, nd); nd->dentry = dentry_save; nd->mnt = vfsmount_save; if (dentry->d_inode) { struct inode *lower_inode = ecryptfs_inode_to_lower(dentry->d_inode); fsstack_copy_attr_all(dentry->d_inode, lower_inode, NULL); } out: return rc; }
static int ecryptfs_d_revalidate(struct dentry *dentry, struct nameidata *nd) { struct dentry *lower_dentry; struct vfsmount *lower_mnt; struct dentry *dentry_save = NULL; struct vfsmount *vfsmount_save = NULL; int rc = 1; if (nd && nd->flags & LOOKUP_RCU) return -ECHILD; lower_dentry = ecryptfs_dentry_to_lower(dentry); lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate) goto out; if (nd) { dentry_save = nd->path.dentry; vfsmount_save = nd->path.mnt; nd->path.dentry = lower_dentry; nd->path.mnt = lower_mnt; } rc = lower_dentry->d_op->d_revalidate(lower_dentry, nd); if (nd) { nd->path.dentry = dentry_save; nd->path.mnt = vfsmount_save; } if (dentry->d_inode) { struct inode *lower_inode = ecryptfs_inode_to_lower(dentry->d_inode); fsstack_copy_attr_all(dentry->d_inode, lower_inode); } out: return rc; }
static int ecryptfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) { int rc; struct dentry *lower_dentry; struct vfsmount *lower_mnt; struct dentry *lower_dir_dentry; lower_dentry = ecryptfs_dentry_to_lower(dentry); lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); lower_dir_dentry = lock_parent(lower_dentry); rc = vfs_mknod(lower_dir_dentry->d_inode, lower_dentry, lower_mnt, mode, dev); if (rc || !lower_dentry->d_inode) goto out; rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0); if (rc) goto out; fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); fsstack_copy_inode_size(dir, lower_dir_dentry->d_inode); out: unlock_dir(lower_dir_dentry); if (!dentry->d_inode) d_drop(dentry); return rc; }
/** * ecryptfs_do_create2 * @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 the new eCryptfs inode on success; an ERR_PTR on error condition */ static struct inode * ecryptfs_do_create2(struct inode *directory_inode, struct dentry *ecryptfs_dentry, umode_t mode, struct nameidata *nd) { int rc; struct dentry *lower_dentry; struct dentry *lower_dir_dentry; struct vfsmount *lower_mnt = NULL; struct inode *inode = NULL; struct dentry *dentry_save = NULL; struct vfsmount *vfsmount_save = NULL; lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry); lower_mnt = ecryptfs_dentry_to_lower_mnt(ecryptfs_dentry); if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate) goto out; lower_dir_dentry = lock_parent(lower_dentry); if (IS_ERR(lower_dir_dentry)) { ecryptfs_printk(KERN_ERR, "Error locking directory of " "dentry\n"); inode = ERR_CAST(lower_dir_dentry); goto out; } if (nd) { dentry_save = nd->path.dentry; vfsmount_save = nd->path.mnt; nd->path.dentry = lower_dentry; nd->path.mnt = lower_mnt; } rc = vfs_create(lower_dir_dentry->d_inode, lower_dentry, mode, nd); if (nd) { nd->path.dentry = dentry_save; nd->path.mnt = vfsmount_save; } if (rc) { printk(KERN_ERR "%s: Failure to create dentry in lower fs; " "rc = [%d]\n", __func__, rc); inode = ERR_PTR(rc); goto out_lock; } inode = __ecryptfs_get_inode(lower_dentry->d_inode, directory_inode->i_sb); if (IS_ERR(inode)) { vfs_unlink(lower_dir_dentry->d_inode, lower_dentry); 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 inode; }
static int ecryptfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) { struct dentry *lower_old_dentry; struct vfsmount *lower_old_mnt; struct dentry *lower_new_dentry; struct vfsmount *lower_new_mnt; struct dentry *lower_dir_dentry; u64 file_size_save; int rc; file_size_save = i_size_read(old_dentry->d_inode); lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry); lower_old_mnt = ecryptfs_dentry_to_lower_mnt(old_dentry); lower_new_dentry = ecryptfs_dentry_to_lower(new_dentry); lower_new_mnt = ecryptfs_dentry_to_lower_mnt(new_dentry); dget(lower_old_dentry); dget(lower_new_dentry); lower_dir_dentry = lock_parent(lower_new_dentry); rc = vfs_link(lower_old_dentry, lower_old_mnt, lower_dir_dentry->d_inode, lower_new_dentry, lower_new_mnt); if (rc || !lower_new_dentry->d_inode) goto out_lock; rc = ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb, 0); if (rc) goto out_lock; fsstack_copy_attr_times(dir, lower_new_dentry->d_inode); fsstack_copy_inode_size(dir, lower_new_dentry->d_inode); old_dentry->d_inode->i_nlink = ecryptfs_inode_to_lower(old_dentry->d_inode)->i_nlink; i_size_write(new_dentry->d_inode, file_size_save); out_lock: unlock_dir(lower_dir_dentry); dput(lower_new_dentry); dput(lower_old_dentry); d_drop(lower_old_dentry); d_drop(new_dentry); d_drop(old_dentry); return rc; }
/** * ecryptfs_d_release * @dentry: The ecryptfs dentry * * Called when a dentry is really deallocated. */ static void ecryptfs_d_release(struct dentry *dentry) { if (ecryptfs_dentry_to_private(dentry)) { if (ecryptfs_dentry_to_lower(dentry)) { dput(ecryptfs_dentry_to_lower(dentry)); mntput(ecryptfs_dentry_to_lower_mnt(dentry)); } kmem_cache_free(ecryptfs_dentry_info_cache, ecryptfs_dentry_to_private(dentry)); } return; }
/** * ecryptfs_lookup_interpose - Dentry interposition for a lookup */ static int ecryptfs_lookup_interpose(struct dentry *dentry, struct dentry *lower_dentry, struct inode *dir_inode) { struct inode *inode, *lower_inode = lower_dentry->d_inode; struct ecryptfs_dentry_info *dentry_info; struct vfsmount *lower_mnt; int rc = 0; lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt(dentry->d_parent)); fsstack_copy_attr_atime(dir_inode, lower_dentry->d_parent->d_inode); BUG_ON(!lower_dentry->d_count); dentry_info = kmem_cache_alloc(ecryptfs_dentry_info_cache, GFP_KERNEL); ecryptfs_set_dentry_private(dentry, dentry_info); if (!dentry_info) { printk(KERN_ERR "%s: Out of memory whilst attempting " "to allocate ecryptfs_dentry_info struct\n", __func__); dput(lower_dentry); mntput(lower_mnt); d_drop(dentry); return -ENOMEM; } ecryptfs_set_dentry_lower(dentry, lower_dentry); ecryptfs_set_dentry_lower_mnt(dentry, lower_mnt); if (!lower_dentry->d_inode) { /* We want to add because we couldn't find in lower */ d_add(dentry, NULL); return 0; } inode = __ecryptfs_get_inode(lower_inode, dir_inode->i_sb); if (IS_ERR(inode)) { printk(KERN_ERR "%s: Error interposing; rc = [%ld]\n", __func__, PTR_ERR(inode)); return PTR_ERR(inode); } if (S_ISREG(inode->i_mode)) { rc = ecryptfs_i_size_read(dentry, inode); if (rc) { make_bad_inode(inode); return rc; } } if (inode->i_state & I_NEW) unlock_new_inode(inode); d_add(dentry, inode); return rc; }
static int ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { int rc; struct dentry *lower_old_dentry; struct vfsmount *lower_old_mnt; struct dentry *lower_new_dentry; struct vfsmount *lower_new_mnt; struct dentry *lower_old_dir_dentry; struct dentry *lower_new_dir_dentry; lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry); lower_old_mnt = ecryptfs_dentry_to_lower_mnt(old_dentry); lower_new_dentry = ecryptfs_dentry_to_lower(new_dentry); lower_new_mnt = ecryptfs_dentry_to_lower_mnt(new_dentry); dget(lower_old_dentry); dget(lower_new_dentry); lower_old_dir_dentry = dget_parent(lower_old_dentry); lower_new_dir_dentry = dget_parent(lower_new_dentry); lock_rename(lower_old_dir_dentry, lower_new_dir_dentry); rc = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry, lower_old_mnt, lower_new_dir_dentry->d_inode, lower_new_dentry, lower_new_mnt); if (rc) goto out_lock; fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode, NULL); if (new_dir != old_dir) fsstack_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode, NULL); out_lock: unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); dput(lower_new_dentry->d_parent); dput(lower_old_dentry->d_parent); dput(lower_new_dentry); dput(lower_old_dentry); return rc; }
/** * ecryptfs_lookup_interpose - Dentry interposition for a lookup */ static struct dentry *ecryptfs_lookup_interpose(struct dentry *dentry, struct dentry *lower_dentry) { struct inode *inode, *lower_inode = d_inode(lower_dentry); struct ecryptfs_dentry_info *dentry_info; struct vfsmount *lower_mnt; int rc = 0; dentry_info = kmem_cache_alloc(ecryptfs_dentry_info_cache, GFP_KERNEL); if (!dentry_info) { printk(KERN_ERR "%s: Out of memory whilst attempting " "to allocate ecryptfs_dentry_info struct\n", __func__); dput(lower_dentry); return ERR_PTR(-ENOMEM); } lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt(dentry->d_parent)); fsstack_copy_attr_atime(d_inode(dentry->d_parent), d_inode(lower_dentry->d_parent)); BUG_ON(!d_count(lower_dentry)); ecryptfs_set_dentry_private(dentry, dentry_info); dentry_info->lower_path.mnt = lower_mnt; dentry_info->lower_path.dentry = lower_dentry; if (d_really_is_negative(lower_dentry)) { /* We want to add because we couldn't find in lower */ d_add(dentry, NULL); return NULL; } inode = __ecryptfs_get_inode(lower_inode, dentry->d_sb); if (IS_ERR(inode)) { printk(KERN_ERR "%s: Error interposing; rc = [%ld]\n", __func__, PTR_ERR(inode)); return ERR_CAST(inode); } if (S_ISREG(inode->i_mode)) { rc = ecryptfs_i_size_read(dentry, inode); if (rc) { make_bad_inode(inode); return ERR_PTR(rc); } } if (inode->i_state & I_NEW) unlock_new_inode(inode); return d_splice_alias(inode, dentry); }
/** * ecryptfs_d_release * @dentry: The ecryptfs dentry * * Called when a dentry is really deallocated. */ static void ecryptfs_d_release(struct dentry *dentry) { struct dentry *lower_dentry; lower_dentry = ecryptfs_dentry_to_lower(dentry); if (ecryptfs_dentry_to_private(dentry)) kmem_cache_free(ecryptfs_dentry_info_cache, ecryptfs_dentry_to_private(dentry)); if (lower_dentry) { struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); mntput(lower_mnt); dput(lower_dentry); } return; }
/** * ecryptfs_create_underlying_file * @lower_dir_inode: inode of the parent in the lower fs of the new file * @dentry: New file's dentry * @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->path.dentry; vfsmount_save = nd->path.mnt; nd->path.dentry = lower_dentry; nd->path.mnt = lower_mnt; rc = vfs_create(lower_dir_inode, lower_dentry, mode, nd); nd->path.dentry = dentry_save; nd->path.mnt = vfsmount_save; return rc; }
static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { int rc; struct dentry *lower_dentry; struct vfsmount *lower_mnt; struct dentry *lower_dir_dentry; umode_t mode; char *encoded_symname; int encoded_symlen; struct ecryptfs_crypt_stat *crypt_stat = NULL; lower_dentry = ecryptfs_dentry_to_lower(dentry); dget(lower_dentry); lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); lower_dir_dentry = lock_parent(lower_dentry); mode = S_IALLUGO; encoded_symlen = ecryptfs_encode_filename(crypt_stat, symname, strlen(symname), &encoded_symname); if (encoded_symlen < 0) { rc = encoded_symlen; goto out_lock; } rc = vfs_symlink(lower_dir_dentry->d_inode, lower_dentry, lower_mnt, encoded_symname, mode); kfree(encoded_symname); if (rc || !lower_dentry->d_inode) goto out_lock; rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0); if (rc) goto out_lock; fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); fsstack_copy_inode_size(dir, lower_dir_dentry->d_inode); out_lock: unlock_dir(lower_dir_dentry); dput(lower_dentry); if (!dentry->d_inode) d_drop(dentry); return rc; }
/** * ecryptfs_read_update_atime * * generic_file_read_iter updates the atime of upper layer inode. But, it * doesn't give us a chance to update the atime of the lower layer * inode. This function is a wrapper to generic_file_read_iter. It * updates the atime of the lower level inode if generic_file_read_iter * returns without any errors. This is to be used only for file reads. * The function to be used for directory reads is ecryptfs_read. */ static ssize_t ecryptfs_read_update_atime(struct kiocb *iocb, struct iov_iter *iter, loff_t pos) { ssize_t rc; struct path lower; struct file *file = iocb->ki_filp; rc = generic_file_read_iter(iocb, iter, pos); /* * Even though this is a async interface, we need to wait * for IO to finish to update atime */ if (-EIOCBQUEUED == rc) rc = wait_on_sync_kiocb(iocb); if (rc >= 0) { lower.dentry = ecryptfs_dentry_to_lower(file->f_path.dentry); lower.mnt = ecryptfs_dentry_to_lower_mnt(file->f_path.dentry); touch_atime(&lower); } return rc; }
static int ecryptfs_unlink(struct inode *dir, struct dentry *dentry) { int rc = 0; struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); struct inode *lower_dir_inode = ecryptfs_inode_to_lower(dir); lock_parent(lower_dentry); rc = vfs_unlink(lower_dir_inode, lower_dentry, lower_mnt); if (rc) { printk(KERN_ERR "Error in vfs_unlink; rc = [%d]\n", rc); goto out_unlock; } fsstack_copy_attr_times(dir, lower_dir_inode); dentry->d_inode->i_nlink = ecryptfs_inode_to_lower(dentry->d_inode)->i_nlink; dentry->d_inode->i_ctime = dir->i_ctime; d_drop(dentry); out_unlock: unlock_parent(lower_dentry); return rc; }
/** * ecryptfs_read_update_atime * * generic_file_read updates the atime of upper layer inode. But, it * doesn't give us a chance to update the atime of the lower layer * inode. This function is a wrapper to generic_file_read. It * updates the atime of the lower level inode if generic_file_read * returns without any errors. This is to be used only for file reads. * The function to be used for directory reads is ecryptfs_read. */ static ssize_t ecryptfs_read_update_atime(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos) { ssize_t rc; struct dentry *lower_dentry; struct vfsmount *lower_vfsmount; struct file *file = iocb->ki_filp; rc = generic_file_aio_read(iocb, iov, nr_segs, pos); /* * Even though this is a async interface, we need to wait * for IO to finish to update atime */ if (-EIOCBQUEUED == rc) rc = wait_on_sync_kiocb(iocb); if (rc >= 0) { lower_dentry = ecryptfs_dentry_to_lower(file->f_path.dentry); lower_vfsmount = ecryptfs_dentry_to_lower_mnt(file->f_path.dentry); touch_atime(lower_vfsmount, lower_dentry); } return rc; }
/** * ecryptfs_d_revalidate - revalidate an ecryptfs dentry * @dentry: The ecryptfs dentry * @flags: lookup flags * * Called when the VFS needs to revalidate a dentry. This * is called whenever a name lookup finds a dentry in the * dcache. Most filesystems leave this as NULL, because all their * dentries in the dcache are valid. * * Returns 1 if valid, 0 otherwise. * */ static int ecryptfs_d_revalidate(struct dentry *dentry, unsigned int flags) { struct dentry *lower_dentry; struct vfsmount *lower_mnt; int rc = 1; if (flags & LOOKUP_RCU) return -ECHILD; lower_dentry = ecryptfs_dentry_to_lower(dentry); lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate) goto out; rc = lower_dentry->d_op->d_revalidate(lower_dentry, flags); if (dentry->d_inode) { struct inode *lower_inode = ecryptfs_inode_to_lower(dentry->d_inode); fsstack_copy_attr_all(dentry->d_inode, lower_inode); } out: return rc; }
/** * ecryptfs_show_options * * Prints the directory we are currently mounted over. * Returns zero on success; non-zero otherwise */ static int ecryptfs_show_options(struct seq_file *m, struct vfsmount *mnt) { struct super_block *sb = mnt->mnt_sb; struct dentry *lower_root_dentry = ecryptfs_dentry_to_lower(sb->s_root); struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(sb->s_root); char *tmp_page; char *path; int rc = 0; tmp_page = (char *)__get_free_page(GFP_KERNEL); if (!tmp_page) { rc = -ENOMEM; goto out; } path = d_path(lower_root_dentry, lower_mnt, tmp_page, PAGE_SIZE); if (IS_ERR(path)) { rc = PTR_ERR(path); goto out; } seq_printf(m, ",dir=%s", path); free_page((unsigned long)tmp_page); out: return rc; }
static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry) { struct dentry *lower_dentry; struct vfsmount *lower_mnt; struct dentry *lower_dir_dentry; int rc; lower_dentry = ecryptfs_dentry_to_lower(dentry); lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); dget(dentry); lower_dir_dentry = lock_parent(lower_dentry); dget(lower_dentry); rc = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry, lower_mnt); dput(lower_dentry); if (!rc) d_delete(lower_dentry); fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); dir->i_nlink = lower_dir_dentry->d_inode->i_nlink; unlock_dir(lower_dir_dentry); if (!rc) d_drop(dentry); dput(dentry); return rc; }
/** * ecryptfs_lookup_and_interpose_lower - Perform a lookup */ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry, struct dentry *lower_dentry, struct inode *ecryptfs_dir_inode, struct nameidata *ecryptfs_nd) { struct dentry *lower_dir_dentry; struct vfsmount *lower_mnt; struct inode *lower_inode; struct ecryptfs_mount_crypt_stat *mount_crypt_stat; struct ecryptfs_crypt_stat *crypt_stat; char *page_virt = NULL; u64 file_size; int rc = 0; lower_dir_dentry = lower_dentry->d_parent; lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt( ecryptfs_dentry->d_parent)); lower_inode = lower_dentry->d_inode; fsstack_copy_attr_atime(ecryptfs_dir_inode, lower_dir_dentry->d_inode); BUG_ON(!atomic_read(&lower_dentry->d_count)); ecryptfs_set_dentry_private(ecryptfs_dentry, kmem_cache_alloc(ecryptfs_dentry_info_cache, GFP_KERNEL)); if (!ecryptfs_dentry_to_private(ecryptfs_dentry)) { rc = -ENOMEM; printk(KERN_ERR "%s: Out of memory whilst attempting " "to allocate ecryptfs_dentry_info struct\n", __func__); goto out_put; } ecryptfs_set_dentry_lower(ecryptfs_dentry, lower_dentry); ecryptfs_set_dentry_lower_mnt(ecryptfs_dentry, lower_mnt); if (!lower_dentry->d_inode) { /* We want to add because we couldn't find in lower */ d_add(ecryptfs_dentry, NULL); goto out; } rc = ecryptfs_interpose(lower_dentry, ecryptfs_dentry, ecryptfs_dir_inode->i_sb, 1); if (rc) { printk(KERN_ERR "%s: Error interposing; rc = [%d]\n", __func__, rc); goto out; } if (S_ISDIR(lower_inode->i_mode)) goto out; if (S_ISLNK(lower_inode->i_mode)) goto out; if (special_file(lower_inode->i_mode)) goto out; if (!ecryptfs_nd) goto out; /* Released in this function */ page_virt = kmem_cache_zalloc(ecryptfs_header_cache_2, GFP_USER); if (!page_virt) { printk(KERN_ERR "%s: Cannot kmem_cache_zalloc() a page\n", __func__); rc = -ENOMEM; goto out; } if (!ecryptfs_inode_to_private(ecryptfs_dentry->d_inode)->lower_file) { rc = ecryptfs_init_persistent_file(ecryptfs_dentry); if (rc) { printk(KERN_ERR "%s: Error attempting to initialize " "the persistent file for the dentry with name " "[%s]; rc = [%d]\n", __func__, ecryptfs_dentry->d_name.name, rc); goto out_free_kmem; } } crypt_stat = &ecryptfs_inode_to_private( ecryptfs_dentry->d_inode)->crypt_stat; /* TODO: lock for crypt_stat comparison */ if (!(crypt_stat->flags & ECRYPTFS_POLICY_APPLIED)) ecryptfs_set_default_sizes(crypt_stat); rc = ecryptfs_read_and_validate_header_region(page_virt, ecryptfs_dentry->d_inode); if (rc) { rc = ecryptfs_read_and_validate_xattr_region(page_virt, ecryptfs_dentry); if (rc) { rc = 0; goto out_free_kmem; } crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR; } mount_crypt_stat = &ecryptfs_superblock_to_private( ecryptfs_dentry->d_sb)->mount_crypt_stat; if (mount_crypt_stat->flags & ECRYPTFS_ENCRYPTED_VIEW_ENABLED) { if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR) file_size = (crypt_stat->num_header_bytes_at_front + i_size_read(lower_dentry->d_inode)); else file_size = i_size_read(lower_dentry->d_inode); } else { file_size = get_unaligned_be64(page_virt); } i_size_write(ecryptfs_dentry->d_inode, (loff_t)file_size); out_free_kmem: kmem_cache_free(ecryptfs_header_cache_2, page_virt); goto out; out_put: dput(lower_dentry); mntput(lower_mnt); d_drop(ecryptfs_dentry); out: return rc; }
/** * ecryptfs_lookup_interpose - Dentry interposition for a lookup */ static int ecryptfs_lookup_interpose(struct dentry *dentry, struct dentry *lower_dentry, struct inode *dir_inode) { struct inode *inode, *lower_inode = lower_dentry->d_inode; struct ecryptfs_dentry_info *dentry_info; struct vfsmount *lower_mnt; int rc = 0; dentry_info = kmem_cache_alloc(ecryptfs_dentry_info_cache, GFP_KERNEL); if (!dentry_info) { printk(KERN_ERR "%s: Out of memory whilst attempting " "to allocate ecryptfs_dentry_info struct\n", __func__); dput(lower_dentry); return -ENOMEM; } lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt(dentry->d_parent)); fsstack_copy_attr_atime(dir_inode, lower_dentry->d_parent->d_inode); BUG_ON(!lower_dentry->d_count); ecryptfs_set_dentry_private(dentry, dentry_info); ecryptfs_set_dentry_lower(dentry, lower_dentry); ecryptfs_set_dentry_lower_mnt(dentry, lower_mnt); if (!lower_dentry->d_inode) { /* We want to add because we couldn't find in lower */ d_add(dentry, NULL); return 0; } inode = __ecryptfs_get_inode(lower_inode, dir_inode->i_sb); if (IS_ERR(inode)) { printk(KERN_ERR "%s: Error interposing; rc = [%ld]\n", __func__, PTR_ERR(inode)); return PTR_ERR(inode); } if (S_ISREG(inode->i_mode)) { rc = ecryptfs_i_size_read(dentry, inode); if (rc) { make_bad_inode(inode); return rc; } } #ifdef CONFIG_SDP if (S_ISDIR(inode->i_mode) && dentry) { if(IS_UNDER_ROOT(dentry)) { struct ecryptfs_mount_crypt_stat *mount_crypt_stat = &ecryptfs_superblock_to_private(inode->i_sb)->mount_crypt_stat; printk("Lookup a directoy under root directory of current partition.\n"); if(is_chamber_directory(mount_crypt_stat, (char *)dentry->d_name.name)) { /* * When this directory is under ROOT directory and the name is registered * as Chamber. */ printk("This is a chamber directory\n"); set_chamber_flag(inode); } } else if(IS_SENSITIVE_DENTRY(dentry->d_parent)) { /* * When parent directory is sensitive */ struct ecryptfs_crypt_stat *crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat; printk("Parent %s is sensitive. so this directory is sensitive too\n", dentry->d_parent->d_name.name); crypt_stat->flags |= ECRYPTFS_DEK_IS_SENSITIVE; } } #endif if (inode->i_state & I_NEW) unlock_new_inode(inode); d_add(dentry, inode); return rc; }
/** * ecryptfs_lookup_and_interpose_lower - Perform a lookup */ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry, struct dentry *lower_dentry, struct inode *ecryptfs_dir_inode) { struct dentry *lower_dir_dentry; struct vfsmount *lower_mnt; struct inode *lower_inode; struct ecryptfs_crypt_stat *crypt_stat; char *page_virt = NULL; int put_lower = 0, rc = 0; lower_dir_dentry = lower_dentry->d_parent; lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt( ecryptfs_dentry->d_parent)); lower_inode = lower_dentry->d_inode; fsstack_copy_attr_atime(ecryptfs_dir_inode, lower_dir_dentry->d_inode); BUG_ON(!lower_dentry->d_count); ecryptfs_set_dentry_private(ecryptfs_dentry, kmem_cache_alloc(ecryptfs_dentry_info_cache, GFP_KERNEL)); if (!ecryptfs_dentry_to_private(ecryptfs_dentry)) { rc = -ENOMEM; printk(KERN_ERR "%s: Out of memory whilst attempting " "to allocate ecryptfs_dentry_info struct\n", __func__); goto out_put; } ecryptfs_set_dentry_lower(ecryptfs_dentry, lower_dentry); ecryptfs_set_dentry_lower_mnt(ecryptfs_dentry, lower_mnt); if (!lower_dentry->d_inode) { /* We want to add because we couldn't find in lower */ d_add(ecryptfs_dentry, NULL); goto out; } rc = ecryptfs_interpose(lower_dentry, ecryptfs_dentry, ecryptfs_dir_inode->i_sb, ECRYPTFS_INTERPOSE_FLAG_D_ADD); if (rc) { printk(KERN_ERR "%s: Error interposing; rc = [%d]\n", __func__, rc); goto out; } if (S_ISDIR(lower_inode->i_mode)) goto out; if (S_ISLNK(lower_inode->i_mode)) goto out; if (special_file(lower_inode->i_mode)) goto out; /* Released in this function */ page_virt = kmem_cache_zalloc(ecryptfs_header_cache_2, GFP_USER); if (!page_virt) { printk(KERN_ERR "%s: Cannot kmem_cache_zalloc() a page\n", __func__); rc = -ENOMEM; goto out; } rc = ecryptfs_get_lower_file(ecryptfs_dentry); if (rc) { printk(KERN_ERR "%s: Error attempting to initialize " "the lower file for the dentry with name " "[%s]; rc = [%d]\n", __func__, ecryptfs_dentry->d_name.name, rc); goto out_free_kmem; } put_lower = 1; crypt_stat = &ecryptfs_inode_to_private( ecryptfs_dentry->d_inode)->crypt_stat; /* TODO: lock for crypt_stat comparison */ if (!(crypt_stat->flags & ECRYPTFS_POLICY_APPLIED)) ecryptfs_set_default_sizes(crypt_stat); rc = ecryptfs_read_and_validate_header_region(page_virt, ecryptfs_dentry->d_inode); if (rc) { memset(page_virt, 0, PAGE_CACHE_SIZE); rc = ecryptfs_read_and_validate_xattr_region(page_virt, ecryptfs_dentry); if (rc) { rc = 0; goto out_free_kmem; } crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR; } ecryptfs_i_size_init(page_virt, ecryptfs_dentry->d_inode); out_free_kmem: kmem_cache_free(ecryptfs_header_cache_2, page_virt); goto out; out_put: dput(lower_dentry); mntput(lower_mnt); d_drop(ecryptfs_dentry); out: if (put_lower) ecryptfs_put_lower_file(ecryptfs_dentry->d_inode); return rc; }
/** * ecryptfs_open * @inode: inode speciying file to open * @file: Structure to return filled in * * Opens the file specified by inode. * * Returns zero on success; non-zero otherwise */ static int ecryptfs_open(struct inode *inode, struct file *file) { int rc = 0; struct ecryptfs_crypt_stat *crypt_stat = NULL; struct ecryptfs_mount_crypt_stat *mount_crypt_stat; struct dentry *ecryptfs_dentry = file->f_path.dentry; /* Private value of ecryptfs_dentry allocated in * ecryptfs_lookup() */ struct dentry *lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry); struct inode *lower_inode = NULL; struct file *lower_file = NULL; struct vfsmount *lower_mnt; struct ecryptfs_file_info *file_info; int lower_flags; /* Released in ecryptfs_release or end of function if failure */ file_info = kmem_cache_alloc(ecryptfs_file_info_cache, GFP_KERNEL); ecryptfs_set_file_private(file, file_info); if (!file_info) { ecryptfs_printk(KERN_ERR, "Error attempting to allocate memory\n"); rc = -ENOMEM; goto out; } memset(file_info, 0, sizeof(*file_info)); lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry); crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat; mount_crypt_stat = &ecryptfs_superblock_to_private( ecryptfs_dentry->d_sb)->mount_crypt_stat; mutex_lock(&crypt_stat->cs_mutex); if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_POLICY_APPLIED)) { ecryptfs_printk(KERN_DEBUG, "Setting flags for stat...\n"); /* Policy code enabled in future release */ ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_POLICY_APPLIED); ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED); } mutex_unlock(&crypt_stat->cs_mutex); lower_flags = file->f_flags; if ((lower_flags & O_ACCMODE) == O_WRONLY) lower_flags = (lower_flags & O_ACCMODE) | O_RDWR; if (file->f_flags & O_APPEND) lower_flags &= ~O_APPEND; lower_mnt = ecryptfs_dentry_to_lower_mnt(ecryptfs_dentry); /* Corresponding fput() in ecryptfs_release() */ if ((rc = ecryptfs_open_lower_file(&lower_file, lower_dentry, lower_mnt, lower_flags))) { ecryptfs_printk(KERN_ERR, "Error opening lower file\n"); goto out_puts; } ecryptfs_set_file_lower(file, lower_file); /* Isn't this check the same as the one in lookup? */ lower_inode = lower_dentry->d_inode; if (S_ISDIR(ecryptfs_dentry->d_inode->i_mode)) { ecryptfs_printk(KERN_DEBUG, "This is a directory\n"); ECRYPTFS_CLEAR_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED); rc = 0; goto out; } mutex_lock(&crypt_stat->cs_mutex); if (i_size_read(lower_inode) < ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE) { if (!(mount_crypt_stat->flags & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)) { rc = -EIO; printk(KERN_WARNING "Attempt to read file that is " "not in a valid eCryptfs format, and plaintext " "passthrough mode is not enabled; returning " "-EIO\n"); mutex_unlock(&crypt_stat->cs_mutex); goto out_puts; } crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED); rc = 0; mutex_unlock(&crypt_stat->cs_mutex); goto out; } else if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_POLICY_APPLIED) || !ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_KEY_VALID)) { rc = ecryptfs_read_headers(ecryptfs_dentry, lower_file); if (rc) { ecryptfs_printk(KERN_DEBUG, "Valid headers not found\n"); if (!(mount_crypt_stat->flags & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)) { rc = -EIO; printk(KERN_WARNING "Attempt to read file that " "is not in a valid eCryptfs format, " "and plaintext passthrough mode is not " "enabled; returning -EIO\n"); mutex_unlock(&crypt_stat->cs_mutex); goto out_puts; } ECRYPTFS_CLEAR_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED); rc = 0; mutex_unlock(&crypt_stat->cs_mutex); goto out; } } mutex_unlock(&crypt_stat->cs_mutex); ecryptfs_printk(KERN_DEBUG, "inode w/ addr = [0x%p], i_ino = [0x%.16x] " "size: [0x%.16x]\n", inode, inode->i_ino, i_size_read(inode)); ecryptfs_set_file_lower(file, lower_file); goto out; out_puts: mntput(lower_mnt); dput(lower_dentry); kmem_cache_free(ecryptfs_file_info_cache, ecryptfs_file_to_private(file)); out: return rc; }
/** * ecryptfs_lookup * @dir: inode * @dentry: The dentry * @nd: nameidata, may be NULL * * Find a file on disk. If the file does not exist, then we'll add it to the * dentry cache and continue on to read it from the disk. */ static struct dentry *ecryptfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { int rc = 0; struct dentry *lower_dir_dentry; struct dentry *lower_dentry; struct vfsmount *lower_mnt; char *encoded_name; int encoded_namelen; struct ecryptfs_crypt_stat *crypt_stat = NULL; struct ecryptfs_mount_crypt_stat *mount_crypt_stat; char *page_virt = NULL; struct inode *lower_inode; u64 file_size; lower_dir_dentry = ecryptfs_dentry_to_lower(dentry->d_parent); dentry->d_op = &ecryptfs_dops; if ((dentry->d_name.len == 1 && !strcmp(dentry->d_name.name, ".")) || (dentry->d_name.len == 2 && !strcmp(dentry->d_name.name, ".."))) { d_drop(dentry); goto out; } encoded_namelen = ecryptfs_encode_filename(crypt_stat, dentry->d_name.name, dentry->d_name.len, &encoded_name); if (encoded_namelen < 0) { rc = encoded_namelen; d_drop(dentry); goto out; } ecryptfs_printk(KERN_DEBUG, "encoded_name = [%s]; encoded_namelen " "= [%d]\n", encoded_name, encoded_namelen); lower_dentry = lookup_one_len(encoded_name, lower_dir_dentry, encoded_namelen - 1); kfree(encoded_name); if (IS_ERR(lower_dentry)) { ecryptfs_printk(KERN_ERR, "ERR from lower_dentry\n"); rc = PTR_ERR(lower_dentry); d_drop(dentry); goto out; } lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt(dentry->d_parent)); ecryptfs_printk(KERN_DEBUG, "lower_dentry = [%p]; lower_dentry->" "d_name.name = [%s]\n", lower_dentry, lower_dentry->d_name.name); lower_inode = lower_dentry->d_inode; fsstack_copy_attr_atime(dir, lower_dir_dentry->d_inode); BUG_ON(!atomic_read(&lower_dentry->d_count)); ecryptfs_set_dentry_private(dentry, kmem_cache_alloc(ecryptfs_dentry_info_cache, GFP_KERNEL)); if (!ecryptfs_dentry_to_private(dentry)) { rc = -ENOMEM; ecryptfs_printk(KERN_ERR, "Out of memory whilst attempting " "to allocate ecryptfs_dentry_info struct\n"); goto out_dput; } ecryptfs_set_dentry_lower(dentry, lower_dentry); ecryptfs_set_dentry_lower_mnt(dentry, lower_mnt); if (!lower_dentry->d_inode) { /* We want to add because we couldn't find in lower */ d_add(dentry, NULL); goto out; } rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, ECRYPTFS_INTERPOSE_FLAG_D_ADD); if (rc) { ecryptfs_printk(KERN_ERR, "Error interposing\n"); goto out; } if (S_ISDIR(lower_inode->i_mode)) { ecryptfs_printk(KERN_DEBUG, "Is a directory; returning\n"); goto out; } if (S_ISLNK(lower_inode->i_mode)) { ecryptfs_printk(KERN_DEBUG, "Is a symlink; returning\n"); goto out; } if (special_file(lower_inode->i_mode)) { ecryptfs_printk(KERN_DEBUG, "Is a special file; returning\n"); goto out; } if (!nd) { ecryptfs_printk(KERN_DEBUG, "We have a NULL nd, just leave" "as we *think* we are about to unlink\n"); goto out; } /* Released in this function */ page_virt = kmem_cache_zalloc(ecryptfs_header_cache_2, GFP_USER); if (!page_virt) { rc = -ENOMEM; ecryptfs_printk(KERN_ERR, "Cannot ecryptfs_kmalloc a page\n"); goto out; } crypt_stat = &ecryptfs_inode_to_private(dentry->d_inode)->crypt_stat; if (!(crypt_stat->flags & ECRYPTFS_POLICY_APPLIED)) ecryptfs_set_default_sizes(crypt_stat); if (!ecryptfs_inode_to_private(dentry->d_inode)->lower_file) { rc = ecryptfs_init_persistent_file(dentry); if (rc) { printk(KERN_ERR "%s: Error attempting to initialize " "the persistent file for the dentry with name " "[%s]; rc = [%d]\n", __func__, dentry->d_name.name, rc); goto out; } } rc = ecryptfs_read_and_validate_header_region(page_virt, dentry->d_inode); if (rc) { rc = ecryptfs_read_and_validate_xattr_region(page_virt, dentry); if (rc) { printk(KERN_DEBUG "Valid metadata not found in header " "region or xattr region; treating file as " "unencrypted\n"); rc = 0; kmem_cache_free(ecryptfs_header_cache_2, page_virt); goto out; } crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR; } mount_crypt_stat = &ecryptfs_superblock_to_private( dentry->d_sb)->mount_crypt_stat; if (mount_crypt_stat->flags & ECRYPTFS_ENCRYPTED_VIEW_ENABLED) { if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR) file_size = (crypt_stat->num_header_bytes_at_front + i_size_read(lower_dentry->d_inode)); else file_size = i_size_read(lower_dentry->d_inode); } else { file_size = get_unaligned_be64(page_virt); } i_size_write(dentry->d_inode, (loff_t)file_size); kmem_cache_free(ecryptfs_header_cache_2, page_virt); goto out; out_dput: dput(lower_dentry); d_drop(dentry); out: return ERR_PTR(rc); }