From 35ffa948b2f7bdf79e488cd496232935d095087a Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Tue, 12 Apr 2011 11:21:36 -0500 Subject: eCryptfs: Remove extra d_delete in ecryptfs_rmdir vfs_rmdir() already calls d_delete() on the lower dentry. That was being duplicated in ecryptfs_rmdir() and caused a NULL pointer dereference when NFSv3 was the lower filesystem. Signed-off-by: Tyler Hicks --- fs/ecryptfs/inode.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'fs/ecryptfs/inode.c') diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index f99051b7adab..9c3c2f5bc6a6 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -538,8 +538,6 @@ static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry) dget(lower_dentry); rc = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry); 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); -- cgit v1.2.3 From dd55c89852481a0708c3fd4b48f3081f4280d9d3 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Tue, 12 Apr 2011 11:23:09 -0500 Subject: eCryptfs: dput dentries returned from dget_parent Call dput on the dentries previously returned by dget_parent() in ecryptfs_rename(). This is needed for supported eCryptfs mounts on top of the NFSv3 client. Signed-off-by: Tyler Hicks --- fs/ecryptfs/inode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs/ecryptfs/inode.c') diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 9c3c2f5bc6a6..72d357649599 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -608,8 +608,8 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry, fsstack_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode); 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_dir_dentry); + dput(lower_old_dir_dentry); dput(lower_new_dentry); dput(lower_old_dentry); return rc; -- cgit v1.2.3 From 332ab16f830f59e7621ae8eb2c353dc135a316f6 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Thu, 14 Apr 2011 15:35:11 -0500 Subject: eCryptfs: Add reference counting to lower files For any given lower inode, eCryptfs keeps only one lower file open and multiplexes all eCryptfs file operations through that lower file. The lower file was considered "persistent" and stayed open from the first lookup through the lifetime of the inode. This patch keeps the notion of a single, per-inode lower file, but adds reference counting around the lower file so that it is closed when not currently in use. If the reference count is at 0 when an operation (such as open, create, etc.) needs to use the lower file, a new lower file is opened. Since the file is no longer persistent, all references to the term persistent file are changed to lower file. Locking is added around the sections of code that opens the lower file and assign the pointer in the inode info, as well as the code the fputs the lower file when all eCryptfs users are done with it. This patch is needed to fix issues, when mounted on top of the NFSv3 client, where the lower file is left silly renamed until the eCryptfs inode is destroyed. Signed-off-by: Tyler Hicks --- fs/ecryptfs/inode.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) (limited to 'fs/ecryptfs/inode.c') diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 72d357649599..f6b388638c3d 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -168,19 +168,18 @@ static int ecryptfs_initialize_file(struct dentry *ecryptfs_dentry) "context; rc = [%d]\n", rc); goto out; } - rc = ecryptfs_init_persistent_file(ecryptfs_dentry); + rc = ecryptfs_get_lower_file(ecryptfs_dentry); if (rc) { printk(KERN_ERR "%s: Error attempting to initialize " - "the persistent file for the dentry with name " + "the lower file for the dentry with name " "[%s]; rc = [%d]\n", __func__, ecryptfs_dentry->d_name.name, rc); goto out; } rc = ecryptfs_write_metadata(ecryptfs_dentry); - if (rc) { + if (rc) printk(KERN_ERR "Error writing headers; rc = [%d]\n", rc); - goto out; - } + ecryptfs_put_lower_file(ecryptfs_dentry->d_inode); out: return rc; } @@ -230,7 +229,7 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry, struct ecryptfs_crypt_stat *crypt_stat; char *page_virt = NULL; u64 file_size; - int rc = 0; + int put_lower = 0, rc = 0; lower_dir_dentry = lower_dentry->d_parent; lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt( @@ -277,14 +276,15 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry, rc = -ENOMEM; goto out; } - rc = ecryptfs_init_persistent_file(ecryptfs_dentry); + rc = ecryptfs_get_lower_file(ecryptfs_dentry); if (rc) { printk(KERN_ERR "%s: Error attempting to initialize " - "the persistent file for the dentry with name " + "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 */ @@ -322,6 +322,8 @@ out_put: mntput(lower_mnt); d_drop(ecryptfs_dentry); out: + if (put_lower) + ecryptfs_put_lower_file(ecryptfs_dentry->d_inode); return rc; } @@ -757,8 +759,11 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia, if (unlikely((ia->ia_size == i_size))) { lower_ia->ia_valid &= ~ATTR_SIZE; - goto out; + return 0; } + rc = ecryptfs_get_lower_file(dentry); + if (rc) + return rc; crypt_stat = &ecryptfs_inode_to_private(dentry->d_inode)->crypt_stat; /* Switch on growing or shrinking file */ if (ia->ia_size > i_size) { @@ -836,6 +841,7 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia, lower_ia->ia_valid &= ~ATTR_SIZE; } out: + ecryptfs_put_lower_file(inode); return rc; } @@ -911,7 +917,13 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia) mount_crypt_stat = &ecryptfs_superblock_to_private( dentry->d_sb)->mount_crypt_stat; + rc = ecryptfs_get_lower_file(dentry); + if (rc) { + mutex_unlock(&crypt_stat->cs_mutex); + goto out; + } rc = ecryptfs_read_metadata(dentry); + ecryptfs_put_lower_file(inode); if (rc) { if (!(mount_crypt_stat->flags & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)) { -- cgit v1.2.3 From 3aeb86ea4cd15f728147a3bd5469a205ada8c767 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Tue, 15 Mar 2011 14:54:00 -0500 Subject: eCryptfs: Handle failed metadata read in lookup When failing to read the lower file's crypto metadata during a lookup, eCryptfs must continue on without throwing an error. For example, there may be a plaintext file in the lower mount point that the user wants to delete through the eCryptfs mount. If an error is encountered while reading the metadata in lookup(), the eCryptfs inode's size could be incorrect. We must be sure to reread the plaintext inode size from the metadata when performing an open() or setattr(). The metadata is already being read in those paths, so this adds minimal performance overhead. This patch introduces a flag which will track whether or not the plaintext inode size has been read so that an incorrect i_size can be fixed in the open() or setattr() paths. https://bugs.launchpad.net/bugs/509180 Cc: Signed-off-by: Tyler Hicks --- fs/ecryptfs/inode.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) (limited to 'fs/ecryptfs/inode.c') diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index f6b388638c3d..2c19d362d2d4 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -225,10 +225,8 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry, 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 put_lower = 0, rc = 0; lower_dir_dentry = lower_dentry->d_parent; @@ -302,18 +300,7 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry, } 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->metadata_size - + 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); + ecryptfs_i_size_init(page_virt, ecryptfs_dentry->d_inode); out_free_kmem: kmem_cache_free(ecryptfs_header_cache_2, page_virt); goto out; @@ -937,7 +924,8 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia) goto out; } rc = 0; - crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED); + crypt_stat->flags &= ~(ECRYPTFS_I_SIZE_INITIALIZED + | ECRYPTFS_ENCRYPTED); } } mutex_unlock(&crypt_stat->cs_mutex); -- cgit v1.2.3 From 5be79de2e1ffa19d871a494697cf76cddee93384 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Fri, 22 Apr 2011 13:08:00 -0500 Subject: eCryptfs: Flush dirty pages in setattr After 57db4e8d73ef2b5e94a3f412108dff2576670a8a changed eCryptfs to write-back caching, eCryptfs page writeback updates the lower inode times due to the use of vfs_write() on the lower file. To preserve inode metadata changes, such as 'cp -p' does with utimensat(), we need to flush all dirty pages early in ecryptfs_setattr() so that the user-updated lower inode metadata isn't clobbered later in writeback. https://bugzilla.kernel.org/show_bug.cgi?id=33372 Reported-by: Rocko Signed-off-by: Tyler Hicks --- fs/ecryptfs/inode.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'fs/ecryptfs/inode.c') diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 2c19d362d2d4..4d4cc6a90cd5 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -929,6 +929,12 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia) } } mutex_unlock(&crypt_stat->cs_mutex); + if (S_ISREG(inode->i_mode)) { + rc = filemap_write_and_wait(inode->i_mapping); + if (rc) + goto out; + fsstack_copy_attr_all(inode, lower_inode); + } memcpy(&lower_ia, ia, sizeof(lower_ia)); if (ia->ia_valid & ATTR_FILE) lower_ia.ia_file = ecryptfs_file_to_lower(ia->ia_file); -- cgit v1.2.3 From 79bf7c732b5ff75b96022ed9d29181afd3d2509c Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 24 May 2011 13:06:06 -0700 Subject: vfs: push dentry_unhash on rmdir into file systems Only a few file systems need this. Start by pushing it down into each fs rmdir method (except gfs2 and xfs) so it can be dealt with on a per-fs basis. This does not change behavior for any in-tree file systems. Acked-by: Christoph Hellwig Signed-off-by: Sage Weil Signed-off-by: Al Viro --- fs/ecryptfs/inode.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs/ecryptfs/inode.c') diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 4d4cc6a90cd5..c88612f0c1eb 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -521,6 +521,8 @@ static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry) struct dentry *lower_dir_dentry; int rc; + dentry_unhash(dentry); + lower_dentry = ecryptfs_dentry_to_lower(dentry); dget(dentry); lower_dir_dentry = lock_parent(lower_dentry); -- cgit v1.2.3 From e4eaac06bcccb2a70bca6a2de9871882dce2aa14 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 24 May 2011 13:06:07 -0700 Subject: vfs: push dentry_unhash on rename_dir into file systems Only a few file systems need this. Start by pushing it down into each rename method (except gfs2 and xfs) so that it can be dealt with on a per-fs basis. Acked-by: Christoph Hellwig Signed-off-by: Sage Weil Signed-off-by: Al Viro --- fs/ecryptfs/inode.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'fs/ecryptfs/inode.c') diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index c88612f0c1eb..227b409b8406 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -573,6 +573,9 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct dentry *lower_new_dir_dentry; struct dentry *trap = NULL; + if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode)) + dentry_unhash(new_dentry); + lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry); lower_new_dentry = ecryptfs_dentry_to_lower(new_dentry); dget(lower_old_dentry); -- cgit v1.2.3 From 07850552b92b3637fa56767b5e460b4238014447 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Fri, 29 Apr 2011 16:26:27 -0500 Subject: eCryptfs: Clear i_nlink in rmdir eCryptfs wasn't clearing the eCryptfs inode's i_nlink after a successful vfs_rmdir() on the lower directory. This resulted in the inode evict and destroy paths to be missed. https://bugs.launchpad.net/ecryptfs/+bug/723518 Signed-off-by: Tyler Hicks Cc: --- fs/ecryptfs/inode.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs/ecryptfs/inode.c') diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 4d4cc6a90cd5..94ab3c06317a 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -527,6 +527,8 @@ static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry) dget(lower_dentry); rc = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry); dput(lower_dentry); + if (!rc && dentry->d_inode) + clear_nlink(dentry->d_inode); 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); -- cgit v1.2.3 From 55e5b7e022eaaa805a44e3b6ecd5c8638d862050 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 27 May 2011 13:42:08 -0700 Subject: ecryptfs: remove unnecessary dentry_unhash on rmdir, dir rename ecryptfs does not have problems with references to unlinked directories. CC: Tyler Hicks CC: Dustin Kirkland CC: ecryptfs-devel@lists.launchpad.net Signed-off-by: Sage Weil Signed-off-by: Al Viro --- fs/ecryptfs/inode.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'fs/ecryptfs/inode.c') diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 227b409b8406..4d4cc6a90cd5 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -521,8 +521,6 @@ static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry) struct dentry *lower_dir_dentry; int rc; - dentry_unhash(dentry); - lower_dentry = ecryptfs_dentry_to_lower(dentry); dget(dentry); lower_dir_dentry = lock_parent(lower_dentry); @@ -573,9 +571,6 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct dentry *lower_new_dir_dentry; struct dentry *trap = NULL; - if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode)) - dentry_unhash(new_dentry); - lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry); lower_new_dentry = ecryptfs_dentry_to_lower(new_dentry); dget(lower_old_dentry); -- cgit v1.2.3 From c4f790736ca8d7d86883c5aee2ba1caa15cd8da3 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Mon, 23 May 2011 21:18:20 -0500 Subject: eCryptfs: Consolidate inode functions into inode.c These functions should live in inode.c since their focus is on inodes and they're primarily used by functions in inode.c. Also does a simple cleanup of ecryptfs_inode_test() and rolls ecryptfs_init_inode() into ecryptfs_inode_set(). Signed-off-by: Tyler Hicks Tested-by: David --- fs/ecryptfs/inode.c | 104 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 89 insertions(+), 15 deletions(-) (limited to 'fs/ecryptfs/inode.c') diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 94ab3c06317a..704a8c8fe19a 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -51,6 +51,95 @@ static void unlock_dir(struct dentry *dir) dput(dir); } +static int ecryptfs_inode_test(struct inode *inode, void *lower_inode) +{ + if (ecryptfs_inode_to_lower(inode) == (struct inode *)lower_inode) + return 1; + return 0; +} + +static int ecryptfs_inode_set(struct inode *inode, void *lower_inode) +{ + ecryptfs_set_inode_lower(inode, (struct inode *)lower_inode); + inode->i_ino = ((struct inode *)lower_inode)->i_ino; + inode->i_version++; + inode->i_op = &ecryptfs_main_iops; + inode->i_fop = &ecryptfs_main_fops; + inode->i_mapping->a_ops = &ecryptfs_aops; + return 0; +} + +struct inode *ecryptfs_get_inode(struct inode *lower_inode, + struct super_block *sb) +{ + struct inode *inode; + int rc = 0; + + if (lower_inode->i_sb != ecryptfs_superblock_to_lower(sb)) { + rc = -EXDEV; + goto out; + } + if (!igrab(lower_inode)) { + rc = -ESTALE; + goto out; + } + inode = iget5_locked(sb, (unsigned long)lower_inode, + ecryptfs_inode_test, ecryptfs_inode_set, + lower_inode); + if (!inode) { + rc = -EACCES; + iput(lower_inode); + goto out; + } + if (inode->i_state & I_NEW) + unlock_new_inode(inode); + else + iput(lower_inode); + if (S_ISLNK(lower_inode->i_mode)) + inode->i_op = &ecryptfs_symlink_iops; + else if (S_ISDIR(lower_inode->i_mode)) + inode->i_op = &ecryptfs_dir_iops; + if (S_ISDIR(lower_inode->i_mode)) + inode->i_fop = &ecryptfs_dir_fops; + if (special_file(lower_inode->i_mode)) + init_special_inode(inode, lower_inode->i_mode, + lower_inode->i_rdev); + fsstack_copy_attr_all(inode, lower_inode); + /* This size will be overwritten for real files w/ headers and + * other metadata */ + fsstack_copy_inode_size(inode, lower_inode); + return inode; +out: + return ERR_PTR(rc); +} + +#define ECRYPTFS_INTERPOSE_FLAG_D_ADD 0x00000001 +/** + * ecryptfs_interpose + * @lower_dentry: Existing dentry in the lower filesystem + * @dentry: ecryptfs' dentry + * @sb: ecryptfs's super_block + * @flags: flags to govern behavior of interpose procedure + * + * 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, + u32 flags) +{ + struct inode *lower_inode = lower_dentry->d_inode; + struct inode *inode = ecryptfs_get_inode(lower_inode, sb); + if (IS_ERR(inode)) + return PTR_ERR(inode); + if (flags & ECRYPTFS_INTERPOSE_FLAG_D_ADD) + d_add(dentry, inode); + else + d_instantiate(dentry, inode); + return 0; +} + /** * ecryptfs_create_underlying_file * @lower_dir_inode: inode of the parent in the lower fs of the new file @@ -1079,21 +1168,6 @@ out: return rc; } -int ecryptfs_inode_test(struct inode *inode, void *candidate_lower_inode) -{ - if ((ecryptfs_inode_to_lower(inode) - == (struct inode *)candidate_lower_inode)) - return 1; - else - return 0; -} - -int ecryptfs_inode_set(struct inode *inode, void *lower_inode) -{ - ecryptfs_init_inode(inode, (struct inode *)lower_inode); - return 0; -} - const struct inode_operations ecryptfs_symlink_iops = { .readlink = ecryptfs_readlink, .follow_link = ecryptfs_follow_link, -- cgit v1.2.3 From 5ccf92037c7c6e6f28175fd245284923f939259f Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Tue, 24 May 2011 02:16:51 -0500 Subject: eCryptfs: Cleanup inode initialization code The eCryptfs inode get, initialization, and dentry interposition code has two separate paths. One is for when dentry interposition is needed after doing things like a mkdir in the lower filesystem and the other is needed after a lookup. Unlocking new inodes and doing a d_add() needs to happen at different times, depending on which type of dentry interposing is being done. This patch cleans up the inode get and initialization code paths and splits them up so that the locking and d_add() differences mentioned above can be handled appropriately in a later patch. Signed-off-by: Tyler Hicks Tested-by: David --- fs/ecryptfs/inode.c | 134 +++++++++++++++++++++++++++------------------------- 1 file changed, 69 insertions(+), 65 deletions(-) (limited to 'fs/ecryptfs/inode.c') diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 704a8c8fe19a..fc7d2b748503 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -58,85 +58,87 @@ static int ecryptfs_inode_test(struct inode *inode, void *lower_inode) return 0; } -static int ecryptfs_inode_set(struct inode *inode, void *lower_inode) +static int ecryptfs_inode_set(struct inode *inode, void *opaque) { - ecryptfs_set_inode_lower(inode, (struct inode *)lower_inode); - inode->i_ino = ((struct inode *)lower_inode)->i_ino; + 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_version++; - inode->i_op = &ecryptfs_main_iops; - inode->i_fop = &ecryptfs_main_fops; 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; } -struct inode *ecryptfs_get_inode(struct inode *lower_inode, - struct super_block *sb) +static struct inode *__ecryptfs_get_inode(struct inode *lower_inode, + struct super_block *sb) { struct inode *inode; - int rc = 0; - if (lower_inode->i_sb != ecryptfs_superblock_to_lower(sb)) { - rc = -EXDEV; - goto out; - } - if (!igrab(lower_inode)) { - rc = -ESTALE; - goto out; - } + 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) { - rc = -EACCES; iput(lower_inode); - goto out; + return ERR_PTR(-EACCES); } - if (inode->i_state & I_NEW) - unlock_new_inode(inode); - else + if (!(inode->i_state & I_NEW)) iput(lower_inode); - if (S_ISLNK(lower_inode->i_mode)) - inode->i_op = &ecryptfs_symlink_iops; - else if (S_ISDIR(lower_inode->i_mode)) - inode->i_op = &ecryptfs_dir_iops; - if (S_ISDIR(lower_inode->i_mode)) - inode->i_fop = &ecryptfs_dir_fops; - if (special_file(lower_inode->i_mode)) - init_special_inode(inode, lower_inode->i_mode, - lower_inode->i_rdev); - fsstack_copy_attr_all(inode, lower_inode); - /* This size will be overwritten for real files w/ headers and - * other metadata */ - fsstack_copy_inode_size(inode, 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; -out: - return ERR_PTR(rc); } -#define ECRYPTFS_INTERPOSE_FLAG_D_ADD 0x00000001 /** * ecryptfs_interpose * @lower_dentry: Existing dentry in the lower filesystem * @dentry: ecryptfs' dentry * @sb: ecryptfs's super_block - * @flags: flags to govern behavior of interpose procedure * * 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, - u32 flags) + struct dentry *dentry, struct super_block *sb) { - struct inode *lower_inode = lower_dentry->d_inode; - struct inode *inode = ecryptfs_get_inode(lower_inode, sb); + struct inode *inode = ecryptfs_get_inode(lower_dentry->d_inode, sb); + if (IS_ERR(inode)) return PTR_ERR(inode); - if (flags & ECRYPTFS_INTERPOSE_FLAG_D_ADD) - d_add(dentry, inode); - else - d_instantiate(dentry, inode); + d_instantiate(dentry, inode); + return 0; } @@ -218,7 +220,7 @@ ecryptfs_do_create(struct inode *directory_inode, goto out_lock; } rc = ecryptfs_interpose(lower_dentry, ecryptfs_dentry, - directory_inode->i_sb, 0); + directory_inode->i_sb); if (rc) { ecryptfs_printk(KERN_ERR, "Failure in ecryptfs_interpose\n"); goto out_lock; @@ -305,15 +307,15 @@ out: } /** - * ecryptfs_lookup_and_interpose_lower - Perform a lookup + * ecryptfs_lookup_interpose - Dentry interposition for a lookup */ -int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry, - struct dentry *lower_dentry, - struct inode *ecryptfs_dir_inode) +static int ecryptfs_lookup_interpose(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 inode *inode, *lower_inode; struct ecryptfs_crypt_stat *crypt_stat; char *page_virt = NULL; int put_lower = 0, rc = 0; @@ -341,14 +343,16 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry, 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) { + inode = __ecryptfs_get_inode(lower_inode, ecryptfs_dir_inode->i_sb); + if (IS_ERR(inode)) { + rc = PTR_ERR(inode); printk(KERN_ERR "%s: Error interposing; rc = [%d]\n", __func__, rc); goto out; } + if (inode->i_state & I_NEW) + unlock_new_inode(inode); + d_add(ecryptfs_dentry, inode); if (S_ISDIR(lower_inode->i_mode)) goto out; if (S_ISLNK(lower_inode->i_mode)) @@ -442,12 +446,12 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode, goto out_d_drop; } if (lower_dentry->d_inode) - goto lookup_and_interpose; + goto interpose; mount_crypt_stat = &ecryptfs_superblock_to_private( ecryptfs_dentry->d_sb)->mount_crypt_stat; if (!(mount_crypt_stat && (mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES))) - goto lookup_and_interpose; + goto interpose; dput(lower_dentry); rc = ecryptfs_encrypt_and_encode_filename( &encrypted_and_encoded_name, &encrypted_and_encoded_name_size, @@ -470,9 +474,9 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode, encrypted_and_encoded_name); goto out_d_drop; } -lookup_and_interpose: - rc = ecryptfs_lookup_and_interpose_lower(ecryptfs_dentry, lower_dentry, - ecryptfs_dir_inode); +interpose: + rc = ecryptfs_lookup_interpose(ecryptfs_dentry, lower_dentry, + ecryptfs_dir_inode); goto out; out_d_drop: d_drop(ecryptfs_dentry); @@ -500,7 +504,7 @@ static int ecryptfs_link(struct dentry *old_dentry, struct inode *dir, lower_new_dentry); if (rc || !lower_new_dentry->d_inode) goto out_lock; - rc = ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb, 0); + rc = ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb); if (rc) goto out_lock; fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); @@ -567,7 +571,7 @@ static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry, kfree(encoded_symname); if (rc || !lower_dentry->d_inode) goto out_lock; - rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0); + rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb); if (rc) goto out_lock; fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); @@ -591,7 +595,7 @@ static int ecryptfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) rc = vfs_mkdir(lower_dir_dentry->d_inode, lower_dentry, mode); if (rc || !lower_dentry->d_inode) goto out; - rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0); + rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb); if (rc) goto out; fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); @@ -639,7 +643,7 @@ ecryptfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) rc = vfs_mknod(lower_dir_dentry->d_inode, lower_dentry, mode, dev); if (rc || !lower_dentry->d_inode) goto out; - rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0); + rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb); if (rc) goto out; fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); -- cgit v1.2.3 From 3b06b3ebf44170c90c893c6c80916db6e922b9f2 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Tue, 24 May 2011 03:49:02 -0500 Subject: eCryptfs: Fix new inode race condition Only unlock and d_add() new inodes after the plaintext inode size has been read from the lower filesystem. This fixes a race condition that was sometimes seen during a multi-job kernel build in an eCryptfs mount. https://bugzilla.kernel.org/show_bug.cgi?id=36002 Signed-off-by: Tyler Hicks Reported-by: David Tested-by: David --- fs/ecryptfs/inode.c | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) (limited to 'fs/ecryptfs/inode.c') diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index fc7d2b748503..f0ad965d7d51 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -259,7 +259,8 @@ static int ecryptfs_initialize_file(struct dentry *ecryptfs_dentry) "context; rc = [%d]\n", rc); goto out; } - rc = ecryptfs_get_lower_file(ecryptfs_dentry); + rc = ecryptfs_get_lower_file(ecryptfs_dentry, + ecryptfs_dentry->d_inode); if (rc) { printk(KERN_ERR "%s: Error attempting to initialize " "the lower file for the dentry with name " @@ -350,50 +351,51 @@ static int ecryptfs_lookup_interpose(struct dentry *ecryptfs_dentry, __func__, rc); goto out; } - if (inode->i_state & I_NEW) - unlock_new_inode(inode); - d_add(ecryptfs_dentry, inode); - 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)) + if (!S_ISREG(inode->i_mode)) { + if (inode->i_state & I_NEW) + unlock_new_inode(inode); + d_add(ecryptfs_dentry, inode); 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; + make_bad_inode(inode); goto out; } - rc = ecryptfs_get_lower_file(ecryptfs_dentry); + rc = ecryptfs_get_lower_file(ecryptfs_dentry, inode); 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); + make_bad_inode(inode); goto out_free_kmem; } put_lower = 1; - crypt_stat = &ecryptfs_inode_to_private( - ecryptfs_dentry->d_inode)->crypt_stat; + crypt_stat = &ecryptfs_inode_to_private(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); + rc = ecryptfs_read_and_validate_header_region(page_virt, inode); if (rc) { memset(page_virt, 0, PAGE_CACHE_SIZE); rc = ecryptfs_read_and_validate_xattr_region(page_virt, - ecryptfs_dentry); + inode); if (rc) { rc = 0; - goto out_free_kmem; + goto unlock_inode; } crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR; } - ecryptfs_i_size_init(page_virt, ecryptfs_dentry->d_inode); + ecryptfs_i_size_init(page_virt, inode); +unlock_inode: + if (inode->i_state & I_NEW) + unlock_new_inode(inode); + d_add(ecryptfs_dentry, inode); out_free_kmem: kmem_cache_free(ecryptfs_header_cache_2, page_virt); goto out; @@ -403,7 +405,7 @@ out_put: d_drop(ecryptfs_dentry); out: if (put_lower) - ecryptfs_put_lower_file(ecryptfs_dentry->d_inode); + ecryptfs_put_lower_file(inode); return rc; } @@ -843,7 +845,7 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia, lower_ia->ia_valid &= ~ATTR_SIZE; return 0; } - rc = ecryptfs_get_lower_file(dentry); + rc = ecryptfs_get_lower_file(dentry, inode); if (rc) return rc; crypt_stat = &ecryptfs_inode_to_private(dentry->d_inode)->crypt_stat; @@ -999,7 +1001,7 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia) mount_crypt_stat = &ecryptfs_superblock_to_private( dentry->d_sb)->mount_crypt_stat; - rc = ecryptfs_get_lower_file(dentry); + rc = ecryptfs_get_lower_file(dentry, inode); if (rc) { mutex_unlock(&crypt_stat->cs_mutex); goto out; -- cgit v1.2.3 From 778aeb42a708d2a57e491d2cbb5a1e74f61270b9 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Tue, 24 May 2011 04:56:23 -0500 Subject: eCryptfs: Cleanup and optimize ecryptfs_lookup_interpose() ecryptfs_lookup_interpose() has turned into spaghetti code over the years. This is an effort to clean it up. - Shorten overly descriptive variable names such as ecryptfs_dentry - Simplify gotos and error paths - Create helper function for reading plaintext i_size from metadata It also includes an optimization when reading i_size from the metadata. A complete page-sized kmem_cache_alloc() was being done to read in 16 bytes of metadata. The buffer for that is now statically declared. Signed-off-by: Tyler Hicks --- fs/ecryptfs/inode.c | 141 +++++++++++++++++++++++----------------------------- 1 file changed, 63 insertions(+), 78 deletions(-) (limited to 'fs/ecryptfs/inode.c') diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index f0ad965d7d51..7349ade17de6 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -307,105 +307,90 @@ out: return rc; } +static int ecryptfs_i_size_read(struct dentry *dentry, struct inode *inode) +{ + struct ecryptfs_crypt_stat *crypt_stat; + int rc; + + rc = ecryptfs_get_lower_file(dentry, inode); + if (rc) { + printk(KERN_ERR "%s: Error attempting to initialize " + "the lower file for the dentry with name " + "[%s]; rc = [%d]\n", __func__, + dentry->d_name.name, rc); + return rc; + } + + crypt_stat = &ecryptfs_inode_to_private(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(inode); + ecryptfs_put_lower_file(inode); + if (rc) { + rc = ecryptfs_read_and_validate_xattr_region(dentry, inode); + if (!rc) + crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR; + } + + /* Must return 0 to allow non-eCryptfs files to be looked up, too */ + return 0; +} + /** * ecryptfs_lookup_interpose - Dentry interposition for a lookup */ -static int ecryptfs_lookup_interpose(struct dentry *ecryptfs_dentry, +static int ecryptfs_lookup_interpose(struct dentry *dentry, struct dentry *lower_dentry, - struct inode *ecryptfs_dir_inode) + struct inode *dir_inode) { - struct dentry *lower_dir_dentry; + struct inode *inode, *lower_inode = lower_dentry->d_inode; + struct ecryptfs_dentry_info *dentry_info; struct vfsmount *lower_mnt; - struct inode *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); + 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); - ecryptfs_set_dentry_private(ecryptfs_dentry, - kmem_cache_alloc(ecryptfs_dentry_info_cache, - GFP_KERNEL)); - if (!ecryptfs_dentry_to_private(ecryptfs_dentry)) { - rc = -ENOMEM; + + 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__); - goto out_put; + dput(lower_dentry); + mntput(lower_mnt); + d_drop(dentry); + return -ENOMEM; } - ecryptfs_set_dentry_lower(ecryptfs_dentry, lower_dentry); - ecryptfs_set_dentry_lower_mnt(ecryptfs_dentry, lower_mnt); + 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(ecryptfs_dentry, NULL); - goto out; + d_add(dentry, NULL); + return 0; } - inode = __ecryptfs_get_inode(lower_inode, ecryptfs_dir_inode->i_sb); + inode = __ecryptfs_get_inode(lower_inode, dir_inode->i_sb); if (IS_ERR(inode)) { - rc = PTR_ERR(inode); - printk(KERN_ERR "%s: Error interposing; rc = [%d]\n", - __func__, rc); - goto out; - } - if (!S_ISREG(inode->i_mode)) { - if (inode->i_state & I_NEW) - unlock_new_inode(inode); - d_add(ecryptfs_dentry, inode); - 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; - make_bad_inode(inode); - goto out; - } - rc = ecryptfs_get_lower_file(ecryptfs_dentry, inode); - 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); - make_bad_inode(inode); - goto out_free_kmem; + printk(KERN_ERR "%s: Error interposing; rc = [%ld]\n", + __func__, PTR_ERR(inode)); + return PTR_ERR(inode); } - put_lower = 1; - crypt_stat = &ecryptfs_inode_to_private(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, inode); - if (rc) { - memset(page_virt, 0, PAGE_CACHE_SIZE); - rc = ecryptfs_read_and_validate_xattr_region(page_virt, - inode); + if (S_ISREG(inode->i_mode)) { + rc = ecryptfs_i_size_read(dentry, inode); if (rc) { - rc = 0; - goto unlock_inode; + make_bad_inode(inode); + return rc; } - crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR; } - ecryptfs_i_size_init(page_virt, inode); -unlock_inode: + if (inode->i_state & I_NEW) unlock_new_inode(inode); - d_add(ecryptfs_dentry, 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(inode); + d_add(dentry, inode); + return rc; } -- cgit v1.2.3