diff options
author | Eric Biggers <ebiggers@google.com> | 2019-04-10 16:21:15 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2019-04-17 12:43:29 -0400 |
commit | 2c58d548f5706d085c4b009f6abb945220460632 (patch) | |
tree | b263f432d3ad778b6970ebbb842bd0a577e88cdc /fs/crypto/hooks.c | |
parent | 4c4f7c19b3c721aed418bc97907b411608c5c6a0 (diff) |
fscrypt: cache decrypted symlink target in ->i_link
Path lookups that traverse encrypted symlink(s) are very slow because
each encrypted symlink needs to be decrypted each time it's followed.
This also involves dropping out of rcu-walk mode.
Make encrypted symlinks faster by caching the decrypted symlink target
in ->i_link. The first call to fscrypt_get_symlink() sets it. Then,
the existing VFS path lookup code uses the non-NULL ->i_link to take the
fast path where ->get_link() isn't called, and lookups in rcu-walk mode
remain in rcu-walk mode.
Also set ->i_link immediately when a new encrypted symlink is created.
To safely free the symlink target after an RCU grace period has elapsed,
introduce a new function fscrypt_free_inode(), and make the relevant
filesystems call it just before actually freeing the inode.
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Diffstat (limited to 'fs/crypto/hooks.c')
-rw-r--r-- | fs/crypto/hooks.c | 40 |
1 files changed, 33 insertions, 7 deletions
diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index 042d5b44f4ed..2dc22549d724 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c | |||
@@ -189,11 +189,9 @@ int __fscrypt_encrypt_symlink(struct inode *inode, const char *target, | |||
189 | sd->len = cpu_to_le16(ciphertext_len); | 189 | sd->len = cpu_to_le16(ciphertext_len); |
190 | 190 | ||
191 | err = fname_encrypt(inode, &iname, sd->encrypted_path, ciphertext_len); | 191 | err = fname_encrypt(inode, &iname, sd->encrypted_path, ciphertext_len); |
192 | if (err) { | 192 | if (err) |
193 | if (!disk_link->name) | 193 | goto err_free_sd; |
194 | kfree(sd); | 194 | |
195 | return err; | ||
196 | } | ||
197 | /* | 195 | /* |
198 | * Null-terminating the ciphertext doesn't make sense, but we still | 196 | * Null-terminating the ciphertext doesn't make sense, but we still |
199 | * count the null terminator in the length, so we might as well | 197 | * count the null terminator in the length, so we might as well |
@@ -201,9 +199,20 @@ int __fscrypt_encrypt_symlink(struct inode *inode, const char *target, | |||
201 | */ | 199 | */ |
202 | sd->encrypted_path[ciphertext_len] = '\0'; | 200 | sd->encrypted_path[ciphertext_len] = '\0'; |
203 | 201 | ||
202 | /* Cache the plaintext symlink target for later use by get_link() */ | ||
203 | err = -ENOMEM; | ||
204 | inode->i_link = kmemdup(target, len + 1, GFP_NOFS); | ||
205 | if (!inode->i_link) | ||
206 | goto err_free_sd; | ||
207 | |||
204 | if (!disk_link->name) | 208 | if (!disk_link->name) |
205 | disk_link->name = (unsigned char *)sd; | 209 | disk_link->name = (unsigned char *)sd; |
206 | return 0; | 210 | return 0; |
211 | |||
212 | err_free_sd: | ||
213 | if (!disk_link->name) | ||
214 | kfree(sd); | ||
215 | return err; | ||
207 | } | 216 | } |
208 | EXPORT_SYMBOL_GPL(__fscrypt_encrypt_symlink); | 217 | EXPORT_SYMBOL_GPL(__fscrypt_encrypt_symlink); |
209 | 218 | ||
@@ -212,7 +221,7 @@ EXPORT_SYMBOL_GPL(__fscrypt_encrypt_symlink); | |||
212 | * @inode: the symlink inode | 221 | * @inode: the symlink inode |
213 | * @caddr: the on-disk contents of the symlink | 222 | * @caddr: the on-disk contents of the symlink |
214 | * @max_size: size of @caddr buffer | 223 | * @max_size: size of @caddr buffer |
215 | * @done: if successful, will be set up to free the returned target | 224 | * @done: if successful, will be set up to free the returned target if needed |
216 | * | 225 | * |
217 | * If the symlink's encryption key is available, we decrypt its target. | 226 | * If the symlink's encryption key is available, we decrypt its target. |
218 | * Otherwise, we encode its target for presentation. | 227 | * Otherwise, we encode its target for presentation. |
@@ -227,12 +236,18 @@ const char *fscrypt_get_symlink(struct inode *inode, const void *caddr, | |||
227 | { | 236 | { |
228 | const struct fscrypt_symlink_data *sd; | 237 | const struct fscrypt_symlink_data *sd; |
229 | struct fscrypt_str cstr, pstr; | 238 | struct fscrypt_str cstr, pstr; |
239 | bool has_key; | ||
230 | int err; | 240 | int err; |
231 | 241 | ||
232 | /* This is for encrypted symlinks only */ | 242 | /* This is for encrypted symlinks only */ |
233 | if (WARN_ON(!IS_ENCRYPTED(inode))) | 243 | if (WARN_ON(!IS_ENCRYPTED(inode))) |
234 | return ERR_PTR(-EINVAL); | 244 | return ERR_PTR(-EINVAL); |
235 | 245 | ||
246 | /* If the decrypted target is already cached, just return it. */ | ||
247 | pstr.name = READ_ONCE(inode->i_link); | ||
248 | if (pstr.name) | ||
249 | return pstr.name; | ||
250 | |||
236 | /* | 251 | /* |
237 | * Try to set up the symlink's encryption key, but we can continue | 252 | * Try to set up the symlink's encryption key, but we can continue |
238 | * regardless of whether the key is available or not. | 253 | * regardless of whether the key is available or not. |
@@ -240,6 +255,7 @@ const char *fscrypt_get_symlink(struct inode *inode, const void *caddr, | |||
240 | err = fscrypt_get_encryption_info(inode); | 255 | err = fscrypt_get_encryption_info(inode); |
241 | if (err) | 256 | if (err) |
242 | return ERR_PTR(err); | 257 | return ERR_PTR(err); |
258 | has_key = fscrypt_has_encryption_key(inode); | ||
243 | 259 | ||
244 | /* | 260 | /* |
245 | * For historical reasons, encrypted symlink targets are prefixed with | 261 | * For historical reasons, encrypted symlink targets are prefixed with |
@@ -271,7 +287,17 @@ const char *fscrypt_get_symlink(struct inode *inode, const void *caddr, | |||
271 | goto err_kfree; | 287 | goto err_kfree; |
272 | 288 | ||
273 | pstr.name[pstr.len] = '\0'; | 289 | pstr.name[pstr.len] = '\0'; |
274 | set_delayed_call(done, kfree_link, pstr.name); | 290 | |
291 | /* | ||
292 | * Cache decrypted symlink targets in i_link for later use. Don't cache | ||
293 | * symlink targets encoded without the key, since those become outdated | ||
294 | * once the key is added. This pairs with the READ_ONCE() above and in | ||
295 | * the VFS path lookup code. | ||
296 | */ | ||
297 | if (!has_key || | ||
298 | cmpxchg_release(&inode->i_link, NULL, pstr.name) != NULL) | ||
299 | set_delayed_call(done, kfree_link, pstr.name); | ||
300 | |||
275 | return pstr.name; | 301 | return pstr.name; |
276 | 302 | ||
277 | err_kfree: | 303 | err_kfree: |