aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ecryptfs
diff options
context:
space:
mode:
authorTyler Hicks <tyhicks@linux.vnet.ibm.com>2011-04-14 16:35:11 -0400
committerTyler Hicks <tyhicks@linux.vnet.ibm.com>2011-04-25 19:32:37 -0400
commit332ab16f830f59e7621ae8eb2c353dc135a316f6 (patch)
treec5b249f34dcacf2aa5ee24de8d332d8171398203 /fs/ecryptfs
parentdd55c89852481a0708c3fd4b48f3081f4280d9d3 (diff)
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 <tyhicks@linux.vnet.ibm.com>
Diffstat (limited to 'fs/ecryptfs')
-rw-r--r--fs/ecryptfs/ecryptfs_kernel.h5
-rw-r--r--fs/ecryptfs/file.c22
-rw-r--r--fs/ecryptfs/inode.c30
-rw-r--r--fs/ecryptfs/kthread.c6
-rw-r--r--fs/ecryptfs/main.c72
-rw-r--r--fs/ecryptfs/super.c16
6 files changed, 92 insertions, 59 deletions
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
index bd3cafd0949d..380bee1094c3 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -295,6 +295,8 @@ struct ecryptfs_crypt_stat {
295struct ecryptfs_inode_info { 295struct ecryptfs_inode_info {
296 struct inode vfs_inode; 296 struct inode vfs_inode;
297 struct inode *wii_inode; 297 struct inode *wii_inode;
298 struct mutex lower_file_mutex;
299 atomic_t lower_file_count;
298 struct file *lower_file; 300 struct file *lower_file;
299 struct ecryptfs_crypt_stat crypt_stat; 301 struct ecryptfs_crypt_stat crypt_stat;
300}; 302};
@@ -757,7 +759,8 @@ int ecryptfs_privileged_open(struct file **lower_file,
757 struct dentry *lower_dentry, 759 struct dentry *lower_dentry,
758 struct vfsmount *lower_mnt, 760 struct vfsmount *lower_mnt,
759 const struct cred *cred); 761 const struct cred *cred);
760int ecryptfs_init_persistent_file(struct dentry *ecryptfs_dentry); 762int ecryptfs_get_lower_file(struct dentry *ecryptfs_dentry);
763void ecryptfs_put_lower_file(struct inode *inode);
761int 764int
762ecryptfs_write_tag_70_packet(char *dest, size_t *remaining_bytes, 765ecryptfs_write_tag_70_packet(char *dest, size_t *remaining_bytes,
763 size_t *packet_size, 766 size_t *packet_size,
diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c
index cedc913d11ba..146c4edff70c 100644
--- a/fs/ecryptfs/file.c
+++ b/fs/ecryptfs/file.c
@@ -191,10 +191,10 @@ static int ecryptfs_open(struct inode *inode, struct file *file)
191 | ECRYPTFS_ENCRYPTED); 191 | ECRYPTFS_ENCRYPTED);
192 } 192 }
193 mutex_unlock(&crypt_stat->cs_mutex); 193 mutex_unlock(&crypt_stat->cs_mutex);
194 rc = ecryptfs_init_persistent_file(ecryptfs_dentry); 194 rc = ecryptfs_get_lower_file(ecryptfs_dentry);
195 if (rc) { 195 if (rc) {
196 printk(KERN_ERR "%s: Error attempting to initialize " 196 printk(KERN_ERR "%s: Error attempting to initialize "
197 "the persistent file for the dentry with name " 197 "the lower file for the dentry with name "
198 "[%s]; rc = [%d]\n", __func__, 198 "[%s]; rc = [%d]\n", __func__,
199 ecryptfs_dentry->d_name.name, rc); 199 ecryptfs_dentry->d_name.name, rc);
200 goto out_free; 200 goto out_free;
@@ -202,9 +202,9 @@ static int ecryptfs_open(struct inode *inode, struct file *file)
202 if ((ecryptfs_inode_to_private(inode)->lower_file->f_flags & O_ACCMODE) 202 if ((ecryptfs_inode_to_private(inode)->lower_file->f_flags & O_ACCMODE)
203 == O_RDONLY && (file->f_flags & O_ACCMODE) != O_RDONLY) { 203 == O_RDONLY && (file->f_flags & O_ACCMODE) != O_RDONLY) {
204 rc = -EPERM; 204 rc = -EPERM;
205 printk(KERN_WARNING "%s: Lower persistent file is RO; eCryptfs " 205 printk(KERN_WARNING "%s: Lower file is RO; eCryptfs "
206 "file must hence be opened RO\n", __func__); 206 "file must hence be opened RO\n", __func__);
207 goto out_free; 207 goto out_put;
208 } 208 }
209 ecryptfs_set_file_lower( 209 ecryptfs_set_file_lower(
210 file, ecryptfs_inode_to_private(inode)->lower_file); 210 file, ecryptfs_inode_to_private(inode)->lower_file);
@@ -232,7 +232,7 @@ static int ecryptfs_open(struct inode *inode, struct file *file)
232 "Plaintext passthrough mode is not " 232 "Plaintext passthrough mode is not "
233 "enabled; returning -EIO\n"); 233 "enabled; returning -EIO\n");
234 mutex_unlock(&crypt_stat->cs_mutex); 234 mutex_unlock(&crypt_stat->cs_mutex);
235 goto out_free; 235 goto out_put;
236 } 236 }
237 rc = 0; 237 rc = 0;
238 crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED); 238 crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED);
@@ -245,6 +245,8 @@ static int ecryptfs_open(struct inode *inode, struct file *file)
245 "[0x%.16lx] size: [0x%.16llx]\n", inode, inode->i_ino, 245 "[0x%.16lx] size: [0x%.16llx]\n", inode, inode->i_ino,
246 (unsigned long long)i_size_read(inode)); 246 (unsigned long long)i_size_read(inode));
247 goto out; 247 goto out;
248out_put:
249 ecryptfs_put_lower_file(inode);
248out_free: 250out_free:
249 kmem_cache_free(ecryptfs_file_info_cache, 251 kmem_cache_free(ecryptfs_file_info_cache,
250 ecryptfs_file_to_private(file)); 252 ecryptfs_file_to_private(file));
@@ -254,17 +256,13 @@ out:
254 256
255static int ecryptfs_flush(struct file *file, fl_owner_t td) 257static int ecryptfs_flush(struct file *file, fl_owner_t td)
256{ 258{
257 int rc = 0; 259 return file->f_mode & FMODE_WRITE
258 struct file *lower_file = NULL; 260 ? filemap_write_and_wait(file->f_mapping) : 0;
259
260 lower_file = ecryptfs_file_to_lower(file);
261 if (lower_file->f_op && lower_file->f_op->flush)
262 rc = lower_file->f_op->flush(lower_file, td);
263 return rc;
264} 261}
265 262
266static int ecryptfs_release(struct inode *inode, struct file *file) 263static int ecryptfs_release(struct inode *inode, struct file *file)
267{ 264{
265 ecryptfs_put_lower_file(inode);
268 kmem_cache_free(ecryptfs_file_info_cache, 266 kmem_cache_free(ecryptfs_file_info_cache,
269 ecryptfs_file_to_private(file)); 267 ecryptfs_file_to_private(file));
270 return 0; 268 return 0;
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)
168 "context; rc = [%d]\n", rc); 168 "context; rc = [%d]\n", rc);
169 goto out; 169 goto out;
170 } 170 }
171 rc = ecryptfs_init_persistent_file(ecryptfs_dentry); 171 rc = ecryptfs_get_lower_file(ecryptfs_dentry);
172 if (rc) { 172 if (rc) {
173 printk(KERN_ERR "%s: Error attempting to initialize " 173 printk(KERN_ERR "%s: Error attempting to initialize "
174 "the persistent file for the dentry with name " 174 "the lower file for the dentry with name "
175 "[%s]; rc = [%d]\n", __func__, 175 "[%s]; rc = [%d]\n", __func__,
176 ecryptfs_dentry->d_name.name, rc); 176 ecryptfs_dentry->d_name.name, rc);
177 goto out; 177 goto out;
178 } 178 }
179 rc = ecryptfs_write_metadata(ecryptfs_dentry); 179 rc = ecryptfs_write_metadata(ecryptfs_dentry);
180 if (rc) { 180 if (rc)
181 printk(KERN_ERR "Error writing headers; rc = [%d]\n", rc); 181 printk(KERN_ERR "Error writing headers; rc = [%d]\n", rc);
182 goto out; 182 ecryptfs_put_lower_file(ecryptfs_dentry->d_inode);
183 }
184out: 183out:
185 return rc; 184 return rc;
186} 185}
@@ -230,7 +229,7 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
230 struct ecryptfs_crypt_stat *crypt_stat; 229 struct ecryptfs_crypt_stat *crypt_stat;
231 char *page_virt = NULL; 230 char *page_virt = NULL;
232 u64 file_size; 231 u64 file_size;
233 int rc = 0; 232 int put_lower = 0, rc = 0;
234 233
235 lower_dir_dentry = lower_dentry->d_parent; 234 lower_dir_dentry = lower_dentry->d_parent;
236 lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt( 235 lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt(
@@ -277,14 +276,15 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
277 rc = -ENOMEM; 276 rc = -ENOMEM;
278 goto out; 277 goto out;
279 } 278 }
280 rc = ecryptfs_init_persistent_file(ecryptfs_dentry); 279 rc = ecryptfs_get_lower_file(ecryptfs_dentry);
281 if (rc) { 280 if (rc) {
282 printk(KERN_ERR "%s: Error attempting to initialize " 281 printk(KERN_ERR "%s: Error attempting to initialize "
283 "the persistent file for the dentry with name " 282 "the lower file for the dentry with name "
284 "[%s]; rc = [%d]\n", __func__, 283 "[%s]; rc = [%d]\n", __func__,
285 ecryptfs_dentry->d_name.name, rc); 284 ecryptfs_dentry->d_name.name, rc);
286 goto out_free_kmem; 285 goto out_free_kmem;
287 } 286 }
287 put_lower = 1;
288 crypt_stat = &ecryptfs_inode_to_private( 288 crypt_stat = &ecryptfs_inode_to_private(
289 ecryptfs_dentry->d_inode)->crypt_stat; 289 ecryptfs_dentry->d_inode)->crypt_stat;
290 /* TODO: lock for crypt_stat comparison */ 290 /* TODO: lock for crypt_stat comparison */
@@ -322,6 +322,8 @@ out_put:
322 mntput(lower_mnt); 322 mntput(lower_mnt);
323 d_drop(ecryptfs_dentry); 323 d_drop(ecryptfs_dentry);
324out: 324out:
325 if (put_lower)
326 ecryptfs_put_lower_file(ecryptfs_dentry->d_inode);
325 return rc; 327 return rc;
326} 328}
327 329
@@ -757,8 +759,11 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia,
757 759
758 if (unlikely((ia->ia_size == i_size))) { 760 if (unlikely((ia->ia_size == i_size))) {
759 lower_ia->ia_valid &= ~ATTR_SIZE; 761 lower_ia->ia_valid &= ~ATTR_SIZE;
760 goto out; 762 return 0;
761 } 763 }
764 rc = ecryptfs_get_lower_file(dentry);
765 if (rc)
766 return rc;
762 crypt_stat = &ecryptfs_inode_to_private(dentry->d_inode)->crypt_stat; 767 crypt_stat = &ecryptfs_inode_to_private(dentry->d_inode)->crypt_stat;
763 /* Switch on growing or shrinking file */ 768 /* Switch on growing or shrinking file */
764 if (ia->ia_size > i_size) { 769 if (ia->ia_size > i_size) {
@@ -836,6 +841,7 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia,
836 lower_ia->ia_valid &= ~ATTR_SIZE; 841 lower_ia->ia_valid &= ~ATTR_SIZE;
837 } 842 }
838out: 843out:
844 ecryptfs_put_lower_file(inode);
839 return rc; 845 return rc;
840} 846}
841 847
@@ -911,7 +917,13 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)
911 917
912 mount_crypt_stat = &ecryptfs_superblock_to_private( 918 mount_crypt_stat = &ecryptfs_superblock_to_private(
913 dentry->d_sb)->mount_crypt_stat; 919 dentry->d_sb)->mount_crypt_stat;
920 rc = ecryptfs_get_lower_file(dentry);
921 if (rc) {
922 mutex_unlock(&crypt_stat->cs_mutex);
923 goto out;
924 }
914 rc = ecryptfs_read_metadata(dentry); 925 rc = ecryptfs_read_metadata(dentry);
926 ecryptfs_put_lower_file(inode);
915 if (rc) { 927 if (rc) {
916 if (!(mount_crypt_stat->flags 928 if (!(mount_crypt_stat->flags
917 & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)) { 929 & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)) {
diff --git a/fs/ecryptfs/kthread.c b/fs/ecryptfs/kthread.c
index 0851ab6980f5..69f994a7d524 100644
--- a/fs/ecryptfs/kthread.c
+++ b/fs/ecryptfs/kthread.c
@@ -44,7 +44,7 @@ static struct task_struct *ecryptfs_kthread;
44 * @ignored: ignored 44 * @ignored: ignored
45 * 45 *
46 * The eCryptfs kernel thread that has the responsibility of getting 46 * The eCryptfs kernel thread that has the responsibility of getting
47 * the lower persistent file with RW permissions. 47 * the lower file with RW permissions.
48 * 48 *
49 * Returns zero on success; non-zero otherwise 49 * Returns zero on success; non-zero otherwise
50 */ 50 */
@@ -141,8 +141,8 @@ int ecryptfs_privileged_open(struct file **lower_file,
141 int rc = 0; 141 int rc = 0;
142 142
143 /* Corresponding dput() and mntput() are done when the 143 /* Corresponding dput() and mntput() are done when the
144 * persistent file is fput() when the eCryptfs inode is 144 * lower file is fput() when all eCryptfs files for the inode are
145 * destroyed. */ 145 * released. */
146 dget(lower_dentry); 146 dget(lower_dentry);
147 mntget(lower_mnt); 147 mntget(lower_mnt);
148 flags |= IS_RDONLY(lower_dentry->d_inode) ? O_RDONLY : O_RDWR; 148 flags |= IS_RDONLY(lower_dentry->d_inode) ? O_RDONLY : O_RDWR;
diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c
index fdb2eb0ad09e..89b93389af8e 100644
--- a/fs/ecryptfs/main.c
+++ b/fs/ecryptfs/main.c
@@ -96,7 +96,7 @@ void __ecryptfs_printk(const char *fmt, ...)
96} 96}
97 97
98/** 98/**
99 * ecryptfs_init_persistent_file 99 * ecryptfs_init_lower_file
100 * @ecryptfs_dentry: Fully initialized eCryptfs dentry object, with 100 * @ecryptfs_dentry: Fully initialized eCryptfs dentry object, with
101 * the lower dentry and the lower mount set 101 * the lower dentry and the lower mount set
102 * 102 *
@@ -104,42 +104,70 @@ void __ecryptfs_printk(const char *fmt, ...)
104 * inode. All I/O operations to the lower inode occur through that 104 * inode. All I/O operations to the lower inode occur through that
105 * file. When the first eCryptfs dentry that interposes with the first 105 * file. When the first eCryptfs dentry that interposes with the first
106 * lower dentry for that inode is created, this function creates the 106 * lower dentry for that inode is created, this function creates the
107 * persistent file struct and associates it with the eCryptfs 107 * lower file struct and associates it with the eCryptfs
108 * inode. When the eCryptfs inode is destroyed, the file is closed. 108 * inode. When all eCryptfs files associated with the inode are released, the
109 * file is closed.
109 * 110 *
110 * The persistent file will be opened with read/write permissions, if 111 * The lower file will be opened with read/write permissions, if
111 * possible. Otherwise, it is opened read-only. 112 * possible. Otherwise, it is opened read-only.
112 * 113 *
113 * This function does nothing if a lower persistent file is already 114 * This function does nothing if a lower file is already
114 * associated with the eCryptfs inode. 115 * associated with the eCryptfs inode.
115 * 116 *
116 * Returns zero on success; non-zero otherwise 117 * Returns zero on success; non-zero otherwise
117 */ 118 */
118int ecryptfs_init_persistent_file(struct dentry *ecryptfs_dentry) 119static int ecryptfs_init_lower_file(struct dentry *dentry,
120 struct file **lower_file)
119{ 121{
120 const struct cred *cred = current_cred(); 122 const struct cred *cred = current_cred();
121 struct ecryptfs_inode_info *inode_info = 123 struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
122 ecryptfs_inode_to_private(ecryptfs_dentry->d_inode); 124 struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry);
123 int rc = 0; 125 int rc;
124 126
125 if (!inode_info->lower_file) { 127 rc = ecryptfs_privileged_open(lower_file, lower_dentry, lower_mnt,
126 struct dentry *lower_dentry; 128 cred);
127 struct vfsmount *lower_mnt = 129 if (rc) {
128 ecryptfs_dentry_to_lower_mnt(ecryptfs_dentry); 130 printk(KERN_ERR "Error opening lower file "
131 "for lower_dentry [0x%p] and lower_mnt [0x%p]; "
132 "rc = [%d]\n", lower_dentry, lower_mnt, rc);
133 (*lower_file) = NULL;
134 }
135 return rc;
136}
129 137
130 lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry); 138int ecryptfs_get_lower_file(struct dentry *dentry)
131 rc = ecryptfs_privileged_open(&inode_info->lower_file, 139{
132 lower_dentry, lower_mnt, cred); 140 struct ecryptfs_inode_info *inode_info =
133 if (rc) { 141 ecryptfs_inode_to_private(dentry->d_inode);
134 printk(KERN_ERR "Error opening lower persistent file " 142 int count, rc = 0;
135 "for lower_dentry [0x%p] and lower_mnt [0x%p]; " 143
136 "rc = [%d]\n", lower_dentry, lower_mnt, rc); 144 mutex_lock(&inode_info->lower_file_mutex);
137 inode_info->lower_file = NULL; 145 count = atomic_inc_return(&inode_info->lower_file_count);
138 } 146 if (WARN_ON_ONCE(count < 1))
147 rc = -EINVAL;
148 else if (count == 1) {
149 rc = ecryptfs_init_lower_file(dentry,
150 &inode_info->lower_file);
151 if (rc)
152 atomic_set(&inode_info->lower_file_count, 0);
139 } 153 }
154 mutex_unlock(&inode_info->lower_file_mutex);
140 return rc; 155 return rc;
141} 156}
142 157
158void ecryptfs_put_lower_file(struct inode *inode)
159{
160 struct ecryptfs_inode_info *inode_info;
161
162 inode_info = ecryptfs_inode_to_private(inode);
163 if (atomic_dec_and_mutex_lock(&inode_info->lower_file_count,
164 &inode_info->lower_file_mutex)) {
165 fput(inode_info->lower_file);
166 inode_info->lower_file = NULL;
167 mutex_unlock(&inode_info->lower_file_mutex);
168 }
169}
170
143static struct inode *ecryptfs_get_inode(struct inode *lower_inode, 171static struct inode *ecryptfs_get_inode(struct inode *lower_inode,
144 struct super_block *sb) 172 struct super_block *sb)
145{ 173{
diff --git a/fs/ecryptfs/super.c b/fs/ecryptfs/super.c
index bacc882e1ae4..245b517bf1b6 100644
--- a/fs/ecryptfs/super.c
+++ b/fs/ecryptfs/super.c
@@ -55,6 +55,8 @@ static struct inode *ecryptfs_alloc_inode(struct super_block *sb)
55 if (unlikely(!inode_info)) 55 if (unlikely(!inode_info))
56 goto out; 56 goto out;
57 ecryptfs_init_crypt_stat(&inode_info->crypt_stat); 57 ecryptfs_init_crypt_stat(&inode_info->crypt_stat);
58 mutex_init(&inode_info->lower_file_mutex);
59 atomic_set(&inode_info->lower_file_count, 0);
58 inode_info->lower_file = NULL; 60 inode_info->lower_file = NULL;
59 inode = &inode_info->vfs_inode; 61 inode = &inode_info->vfs_inode;
60out: 62out:
@@ -77,8 +79,7 @@ static void ecryptfs_i_callback(struct rcu_head *head)
77 * 79 *
78 * This is used during the final destruction of the inode. All 80 * This is used during the final destruction of the inode. All
79 * allocation of memory related to the inode, including allocated 81 * allocation of memory related to the inode, including allocated
80 * memory in the crypt_stat struct, will be released here. This 82 * memory in the crypt_stat struct, will be released here.
81 * function also fput()'s the persistent file for the lower inode.
82 * There should be no chance that this deallocation will be missed. 83 * There should be no chance that this deallocation will be missed.
83 */ 84 */
84static void ecryptfs_destroy_inode(struct inode *inode) 85static void ecryptfs_destroy_inode(struct inode *inode)
@@ -86,16 +87,7 @@ static void ecryptfs_destroy_inode(struct inode *inode)
86 struct ecryptfs_inode_info *inode_info; 87 struct ecryptfs_inode_info *inode_info;
87 88
88 inode_info = ecryptfs_inode_to_private(inode); 89 inode_info = ecryptfs_inode_to_private(inode);
89 if (inode_info->lower_file) { 90 BUG_ON(inode_info->lower_file);
90 struct dentry *lower_dentry =
91 inode_info->lower_file->f_dentry;
92
93 BUG_ON(!lower_dentry);
94 if (lower_dentry->d_inode) {
95 fput(inode_info->lower_file);
96 inode_info->lower_file = NULL;
97 }
98 }
99 ecryptfs_destroy_crypt_stat(&inode_info->crypt_stat); 91 ecryptfs_destroy_crypt_stat(&inode_info->crypt_stat);
100 call_rcu(&inode->i_rcu, ecryptfs_i_callback); 92 call_rcu(&inode->i_rcu, ecryptfs_i_callback);
101} 93}