From: Michael Halcrow Associate vfsmount with dentry rather than superblock. Update nameidata handling to preserve dentry and vfsmount on calls to lower filesystem. Signed-off-by: Michael Halcrow Signed-off-by: Andrew Morton --- fs/ecryptfs/crypto.c | 5 +- fs/ecryptfs/dentry.c | 18 +++++---- fs/ecryptfs/ecryptfs_kernel.h | 19 ++++++++- fs/ecryptfs/file.c | 5 +- fs/ecryptfs/inode.c | 63 ++++++++++++++++++-------------- fs/ecryptfs/main.c | 4 +- fs/ecryptfs/super.c | 19 +++------ 7 files changed, 78 insertions(+), 55 deletions(-) diff -puN fs/ecryptfs/crypto.c~ecryptfs-associate-vfsmount-with-dentry-rather-than-superblock fs/ecryptfs/crypto.c --- a/fs/ecryptfs/crypto.c~ecryptfs-associate-vfsmount-with-dentry-rather-than-superblock +++ a/fs/ecryptfs/crypto.c @@ -1143,14 +1143,13 @@ int ecryptfs_cipher_code_to_string(char * Returns zero on success; non-zero otherwise */ int ecryptfs_read_header_region(char *data, struct dentry *dentry, - struct nameidata *nd) + struct vfsmount *mnt) { - struct vfsmount *mnt; struct file *file; mm_segment_t oldfs; int rc; - mnt = mntget(nd->mnt); + mnt = mntget(mnt); file = dentry_open(dentry, mnt, O_RDONLY); if (IS_ERR(file)) { ecryptfs_printk(KERN_DEBUG, "Error opening file to " diff -puN fs/ecryptfs/dentry.c~ecryptfs-associate-vfsmount-with-dentry-rather-than-superblock fs/ecryptfs/dentry.c --- a/fs/ecryptfs/dentry.c~ecryptfs-associate-vfsmount-with-dentry-rather-than-superblock +++ a/fs/ecryptfs/dentry.c @@ -41,17 +41,21 @@ */ 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; - struct dentry *lower_dentry; - struct nameidata lower_nd; - lower_dentry = ecryptfs_dentry_to_lower(dentry); if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate) goto out; - memcpy(&lower_nd, nd, sizeof(struct nameidata)); - lower_nd.dentry = lower_dentry; - lower_nd.mnt = ecryptfs_superblock_to_private(dentry->d_sb)->lower_mnt; - rc = lower_dentry->d_op->d_revalidate(lower_dentry, &lower_nd); + 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; out: return rc; } diff -puN fs/ecryptfs/ecryptfs_kernel.h~ecryptfs-associate-vfsmount-with-dentry-rather-than-superblock fs/ecryptfs/ecryptfs_kernel.h --- a/fs/ecryptfs/ecryptfs_kernel.h~ecryptfs-associate-vfsmount-with-dentry-rather-than-superblock +++ a/fs/ecryptfs/ecryptfs_kernel.h @@ -216,9 +216,11 @@ struct ecryptfs_inode_info { struct ecryptfs_crypt_stat crypt_stat; }; -/* dentry private data. */ +/* dentry private data. Each dentry must keep track of a lower + * vfsmount too. */ struct ecryptfs_dentry_info { struct dentry *wdi_dentry; + struct vfsmount *lower_mnt; struct ecryptfs_crypt_stat *crypt_stat; }; @@ -243,7 +245,6 @@ struct ecryptfs_mount_crypt_stat { /* superblock private data. */ struct ecryptfs_sb_info { struct super_block *wsi_sb; - struct vfsmount *lower_mnt; struct ecryptfs_mount_crypt_stat mount_crypt_stat; }; @@ -362,6 +363,18 @@ ecryptfs_set_dentry_lower(struct dentry lower_dentry; } +static inline struct vfsmount * +ecryptfs_dentry_to_lower_mnt(struct dentry *dentry) +{ + return ((struct ecryptfs_dentry_info *)dentry->d_fsdata)->lower_mnt; +} + +static inline void +ecryptfs_set_dentry_lower_mnt(struct dentry *dentry, struct vfsmount *lower_mnt) +{ + ((struct ecryptfs_dentry_info *)dentry->d_fsdata)->lower_mnt = + lower_mnt; +} #define ecryptfs_printk(type, fmt, arg...) \ __ecryptfs_printk(type "%s: " fmt, __FUNCTION__, ## arg); @@ -445,7 +458,7 @@ int ecryptfs_read_headers(struct dentry int ecryptfs_new_file_context(struct dentry *ecryptfs_dentry); int contains_ecryptfs_marker(char *data); int ecryptfs_read_header_region(char *data, struct dentry *dentry, - struct nameidata *nd); + struct vfsmount *mnt); u16 ecryptfs_code_for_cipher_string(struct ecryptfs_crypt_stat *crypt_stat); int ecryptfs_cipher_code_to_string(char *str, u16 cipher_code); void ecryptfs_set_default_sizes(struct ecryptfs_crypt_stat *crypt_stat); diff -puN fs/ecryptfs/file.c~ecryptfs-associate-vfsmount-with-dentry-rather-than-superblock fs/ecryptfs/file.c --- a/fs/ecryptfs/file.c~ecryptfs-associate-vfsmount-with-dentry-rather-than-superblock +++ a/fs/ecryptfs/file.c @@ -117,8 +117,7 @@ static ssize_t ecryptfs_read_update_atim rc = wait_on_sync_kiocb(iocb); if (rc >= 0) { lower_dentry = ecryptfs_dentry_to_lower(file->f_dentry); - lower_vfsmount = ecryptfs_superblock_to_private( - file->f_dentry->d_inode->i_sb)->lower_mnt; + lower_vfsmount = ecryptfs_dentry_to_lower_mnt(file->f_dentry); touch_atime(lower_vfsmount, lower_dentry); } return rc; @@ -248,7 +247,7 @@ static int ecryptfs_open(struct inode *i lower_flags = (lower_flags & O_ACCMODE) | O_RDWR; if (file->f_flags & O_APPEND) lower_flags &= ~O_APPEND; - lower_mnt = ecryptfs_superblock_to_private(inode->i_sb)->lower_mnt; + lower_mnt = ecryptfs_dentry_to_lower_mnt(ecryptfs_dentry); mntget(lower_mnt); /* Corresponding fput() in ecryptfs_release() */ lower_file = dentry_open(lower_dentry, lower_mnt, lower_flags); diff -puN fs/ecryptfs/inode.c~ecryptfs-associate-vfsmount-with-dentry-rather-than-superblock fs/ecryptfs/inode.c --- a/fs/ecryptfs/inode.c~ecryptfs-associate-vfsmount-with-dentry-rather-than-superblock +++ a/fs/ecryptfs/inode.c @@ -101,7 +101,7 @@ void ecryptfs_copy_attr_all(struct inode * @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 & vfsmnt + * @nd: nameidata of ecryptfs' parent's dentry & vfsmount * * Creates the file in the lower file system. * @@ -109,18 +109,22 @@ void ecryptfs_copy_attr_all(struct inode */ static int ecryptfs_create_underlying_file(struct inode *lower_dir_inode, - struct dentry *lower_dentry, - struct dentry *ecryptfs_dentry, int mode, + 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; - struct nameidata lower_nd; - memcpy(&lower_nd, nd, sizeof(struct nameidata)); - lower_nd.dentry = lower_dentry; - lower_nd.mnt = ecryptfs_superblock_to_private( - ecryptfs_dentry->d_sb)->lower_mnt; - rc = vfs_create(lower_dir_inode, lower_dentry, mode, &lower_nd); + 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; } @@ -129,7 +133,7 @@ ecryptfs_create_underlying_file(struct i * @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 & vfsmnt + * @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 @@ -155,8 +159,7 @@ ecryptfs_do_create(struct inode *directo goto out; } rc = ecryptfs_create_underlying_file(lower_dir_dentry->d_inode, - lower_dentry, ecryptfs_dentry, - mode, nd); + ecryptfs_dentry, mode, nd); if (unlikely(rc)) { ecryptfs_printk(KERN_ERR, "Failure to create underlying file\n"); @@ -248,7 +251,7 @@ static int ecryptfs_initialize_file(stru #if BITS_PER_LONG != 32 lower_flags |= O_LARGEFILE; #endif - lower_mnt = ecryptfs_superblock_to_private(inode->i_sb)->lower_mnt; + lower_mnt = ecryptfs_dentry_to_lower_mnt(ecryptfs_dentry); mntget(lower_mnt); /* Corresponding fput() at end of this function */ lower_file = dentry_open(tlower_dentry, lower_mnt, lower_flags); @@ -332,6 +335,7 @@ static struct dentry *ecryptfs_lookup(st int rc = 0; struct dentry *lower_dir_dentry; struct dentry *lower_dentry; + struct vfsmount *lower_mnt; struct dentry *tlower_dentry = NULL; char *encoded_name; unsigned int encoded_namelen; @@ -358,6 +362,7 @@ static struct dentry *ecryptfs_lookup(st lower_dentry = lookup_one_len(encoded_name, lower_dir_dentry, encoded_namelen - 1); kfree(encoded_name); + lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt(dentry->d_parent)); if (IS_ERR(lower_dentry)) { ecryptfs_printk(KERN_ERR, "ERR from lower_dentry\n"); rc = PTR_ERR(lower_dentry); @@ -379,6 +384,7 @@ static struct dentry *ecryptfs_lookup(st 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); @@ -419,7 +425,7 @@ static struct dentry *ecryptfs_lookup(st goto out_dput; } memset(page_virt, 0, PAGE_CACHE_SIZE); - rc = ecryptfs_read_header_region(page_virt, tlower_dentry, nd); + rc = ecryptfs_read_header_region(page_virt, tlower_dentry, nd->mnt); crypt_stat = &ecryptfs_inode_to_private(dentry->d_inode)->crypt_stat; if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_POLICY_APPLIED)) ecryptfs_set_default_sizes(crypt_stat); @@ -801,6 +807,7 @@ int ecryptfs_truncate(struct dentry *den int rc = 0; struct inode *inode = dentry->d_inode; struct dentry *lower_dentry; + struct vfsmount *lower_mnt; struct file fake_ecryptfs_file, *lower_file = NULL; struct ecryptfs_crypt_stat *crypt_stat; loff_t i_size = i_size_read(inode); @@ -826,10 +833,9 @@ int ecryptfs_truncate(struct dentry *den lower_dentry = ecryptfs_dentry_to_lower(dentry); /* This dget & mntget is released through fput at out_fput: */ dget(lower_dentry); - mntget(ecryptfs_superblock_to_private(inode->i_sb)->lower_mnt); - lower_file = dentry_open( - lower_dentry, - ecryptfs_superblock_to_private(inode->i_sb)->lower_mnt, O_RDWR); + lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); + mntget(lower_mnt); + lower_file = dentry_open(lower_dentry, lower_mnt, O_RDWR); if (unlikely(IS_ERR(lower_file))) { rc = PTR_ERR(lower_file); goto out_free; @@ -885,15 +891,20 @@ out: static int ecryptfs_permission(struct inode *inode, int mask, struct nameidata *nd) { - struct inode *lower_inode; - int rc = 0; + int rc; - lower_inode = ecryptfs_inode_to_lower(inode); - if (nd) - ecryptfs_printk(KERN_DEBUG, "nd->dentry = [%p]\n", - nd->dentry); - rc = permission(lower_inode, mask, nd); - return rc; + if (nd) { + struct vfsmount *vfsmnt_save = nd->mnt; + struct dentry *dentry_save = nd->dentry; + + nd->mnt = ecryptfs_dentry_to_lower_mnt(nd->dentry); + nd->dentry = ecryptfs_dentry_to_lower(nd->dentry); + rc = permission(ecryptfs_inode_to_lower(inode), mask, nd); + nd->mnt = vfsmnt_save; + nd->dentry = dentry_save; + } else + rc = permission(ecryptfs_inode_to_lower(inode), mask, NULL); + return rc; } /** diff -puN fs/ecryptfs/main.c~ecryptfs-associate-vfsmount-with-dentry-rather-than-superblock fs/ecryptfs/main.c --- a/fs/ecryptfs/main.c~ecryptfs-associate-vfsmount-with-dentry-rather-than-superblock +++ a/fs/ecryptfs/main.c @@ -440,6 +440,7 @@ static int ecryptfs_read_super(struct su int rc; struct nameidata nd; struct dentry *lower_root; + struct vfsmount *lower_mnt; memset(&nd, 0, sizeof(struct nameidata)); rc = path_lookup(dev_name, LOOKUP_FOLLOW, &nd); @@ -448,16 +449,17 @@ static int ecryptfs_read_super(struct su goto out_free; } lower_root = nd.dentry; - ecryptfs_superblock_to_private(sb)->lower_mnt = nd.mnt; if (!lower_root->d_inode) { ecryptfs_printk(KERN_WARNING, "No directory to interpose on\n"); rc = -ENOENT; goto out_free; } + lower_mnt = nd.mnt; ecryptfs_set_superblock_lower(sb, lower_root->d_sb); sb->s_maxbytes = lower_root->d_sb->s_maxbytes; ecryptfs_set_dentry_lower(sb->s_root, lower_root); + ecryptfs_set_dentry_lower_mnt(sb->s_root, lower_mnt); if ((rc = ecryptfs_interpose(lower_root, sb->s_root, sb, 0))) goto out_free; rc = 0; diff -puN fs/ecryptfs/super.c~ecryptfs-associate-vfsmount-with-dentry-rather-than-superblock fs/ecryptfs/super.c --- a/fs/ecryptfs/super.c~ecryptfs-associate-vfsmount-with-dentry-rather-than-superblock +++ a/fs/ecryptfs/super.c @@ -108,7 +108,6 @@ static void ecryptfs_put_super(struct su { struct ecryptfs_sb_info *sb_info = ecryptfs_superblock_to_private(sb); - mntput(sb_info->lower_mnt); ecryptfs_destruct_mount_crypt_stat(&sb_info->mount_crypt_stat); kmem_cache_free(ecryptfs_sb_info_cache, sb_info); ecryptfs_set_superblock_private(sb, NULL); @@ -152,10 +151,10 @@ static void ecryptfs_clear_inode(struct */ static void ecryptfs_umount_begin(struct vfsmount *vfsmnt, int flags) { - struct vfsmount *lower_mnt; + struct vfsmount *lower_mnt = + ecryptfs_dentry_to_lower_mnt(vfsmnt->mnt_sb->s_root); struct super_block *lower_sb; - lower_mnt = ecryptfs_superblock_to_private(vfsmnt->mnt_sb)->lower_mnt; lower_sb = lower_mnt->mnt_sb; if (lower_sb->s_op->umount_begin) lower_sb->s_op->umount_begin(lower_mnt, flags); @@ -170,22 +169,18 @@ static void ecryptfs_umount_begin(struct static int ecryptfs_show_options(struct seq_file *m, struct vfsmount *mnt) { struct super_block *sb = mnt->mnt_sb; - struct dentry *lower_root_dentry; - struct ecryptfs_sb_info *sb_info; - struct vfsmount *lower_mount; - int rc = 0; - char *tmp_page = NULL; + 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; } - lower_root_dentry = ecryptfs_dentry_to_lower(sb->s_root); - sb_info = ecryptfs_superblock_to_private(sb); - lower_mount = sb_info->lower_mnt; - path = d_path(lower_root_dentry, lower_mount, tmp_page, PAGE_SIZE); + path = d_path(lower_root_dentry, lower_mnt, tmp_page, PAGE_SIZE); if (IS_ERR(path)) { rc = PTR_ERR(path); goto out; _