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 | |
| 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>
| -rw-r--r-- | fs/crypto/hooks.c | 40 | ||||
| -rw-r--r-- | fs/crypto/keyinfo.c | 21 | ||||
| -rw-r--r-- | fs/ext4/super.c | 3 | ||||
| -rw-r--r-- | fs/f2fs/super.c | 3 | ||||
| -rw-r--r-- | fs/ubifs/super.c | 3 | ||||
| -rw-r--r-- | include/linux/fscrypt.h | 5 |
6 files changed, 68 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: |
diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index bf291c10c682..82989098b2fc 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c | |||
| @@ -584,9 +584,30 @@ out: | |||
| 584 | } | 584 | } |
| 585 | EXPORT_SYMBOL(fscrypt_get_encryption_info); | 585 | EXPORT_SYMBOL(fscrypt_get_encryption_info); |
| 586 | 586 | ||
| 587 | /** | ||
| 588 | * fscrypt_put_encryption_info - free most of an inode's fscrypt data | ||
| 589 | * | ||
| 590 | * Free the inode's fscrypt_info. Filesystems must call this when the inode is | ||
| 591 | * being evicted. An RCU grace period need not have elapsed yet. | ||
| 592 | */ | ||
| 587 | void fscrypt_put_encryption_info(struct inode *inode) | 593 | void fscrypt_put_encryption_info(struct inode *inode) |
| 588 | { | 594 | { |
| 589 | put_crypt_info(inode->i_crypt_info); | 595 | put_crypt_info(inode->i_crypt_info); |
| 590 | inode->i_crypt_info = NULL; | 596 | inode->i_crypt_info = NULL; |
| 591 | } | 597 | } |
| 592 | EXPORT_SYMBOL(fscrypt_put_encryption_info); | 598 | EXPORT_SYMBOL(fscrypt_put_encryption_info); |
| 599 | |||
| 600 | /** | ||
| 601 | * fscrypt_free_inode - free an inode's fscrypt data requiring RCU delay | ||
| 602 | * | ||
| 603 | * Free the inode's cached decrypted symlink target, if any. Filesystems must | ||
| 604 | * call this after an RCU grace period, just before they free the inode. | ||
| 605 | */ | ||
| 606 | void fscrypt_free_inode(struct inode *inode) | ||
| 607 | { | ||
| 608 | if (IS_ENCRYPTED(inode) && S_ISLNK(inode->i_mode)) { | ||
| 609 | kfree(inode->i_link); | ||
| 610 | inode->i_link = NULL; | ||
| 611 | } | ||
| 612 | } | ||
| 613 | EXPORT_SYMBOL(fscrypt_free_inode); | ||
diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 6ed4eb81e674..5b92054bf8ea 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c | |||
| @@ -1110,6 +1110,9 @@ static int ext4_drop_inode(struct inode *inode) | |||
| 1110 | static void ext4_i_callback(struct rcu_head *head) | 1110 | static void ext4_i_callback(struct rcu_head *head) |
| 1111 | { | 1111 | { |
| 1112 | struct inode *inode = container_of(head, struct inode, i_rcu); | 1112 | struct inode *inode = container_of(head, struct inode, i_rcu); |
| 1113 | |||
| 1114 | fscrypt_free_inode(inode); | ||
| 1115 | |||
| 1113 | kmem_cache_free(ext4_inode_cachep, EXT4_I(inode)); | 1116 | kmem_cache_free(ext4_inode_cachep, EXT4_I(inode)); |
| 1114 | } | 1117 | } |
| 1115 | 1118 | ||
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index f2aaa2cc6b3e..11b3a039a188 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c | |||
| @@ -1003,6 +1003,9 @@ static void f2fs_dirty_inode(struct inode *inode, int flags) | |||
| 1003 | static void f2fs_i_callback(struct rcu_head *head) | 1003 | static void f2fs_i_callback(struct rcu_head *head) |
| 1004 | { | 1004 | { |
| 1005 | struct inode *inode = container_of(head, struct inode, i_rcu); | 1005 | struct inode *inode = container_of(head, struct inode, i_rcu); |
| 1006 | |||
| 1007 | fscrypt_free_inode(inode); | ||
| 1008 | |||
| 1006 | kmem_cache_free(f2fs_inode_cachep, F2FS_I(inode)); | 1009 | kmem_cache_free(f2fs_inode_cachep, F2FS_I(inode)); |
| 1007 | } | 1010 | } |
| 1008 | 1011 | ||
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 12628184772c..19fd21098745 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c | |||
| @@ -276,7 +276,10 @@ static void ubifs_i_callback(struct rcu_head *head) | |||
| 276 | { | 276 | { |
| 277 | struct inode *inode = container_of(head, struct inode, i_rcu); | 277 | struct inode *inode = container_of(head, struct inode, i_rcu); |
| 278 | struct ubifs_inode *ui = ubifs_inode(inode); | 278 | struct ubifs_inode *ui = ubifs_inode(inode); |
| 279 | |||
| 279 | kfree(ui->data); | 280 | kfree(ui->data); |
| 281 | fscrypt_free_inode(inode); | ||
| 282 | |||
| 280 | kmem_cache_free(ubifs_inode_slab, ui); | 283 | kmem_cache_free(ubifs_inode_slab, ui); |
| 281 | } | 284 | } |
| 282 | 285 | ||
diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index abe7081b6b22..28c74e0a7231 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h | |||
| @@ -128,6 +128,7 @@ extern int fscrypt_inherit_context(struct inode *, struct inode *, | |||
| 128 | /* keyinfo.c */ | 128 | /* keyinfo.c */ |
| 129 | extern int fscrypt_get_encryption_info(struct inode *); | 129 | extern int fscrypt_get_encryption_info(struct inode *); |
| 130 | extern void fscrypt_put_encryption_info(struct inode *); | 130 | extern void fscrypt_put_encryption_info(struct inode *); |
| 131 | extern void fscrypt_free_inode(struct inode *); | ||
| 131 | 132 | ||
| 132 | /* fname.c */ | 133 | /* fname.c */ |
| 133 | extern int fscrypt_setup_filename(struct inode *, const struct qstr *, | 134 | extern int fscrypt_setup_filename(struct inode *, const struct qstr *, |
| @@ -341,6 +342,10 @@ static inline void fscrypt_put_encryption_info(struct inode *inode) | |||
| 341 | return; | 342 | return; |
| 342 | } | 343 | } |
| 343 | 344 | ||
| 345 | static inline void fscrypt_free_inode(struct inode *inode) | ||
| 346 | { | ||
| 347 | } | ||
| 348 | |||
| 344 | /* fname.c */ | 349 | /* fname.c */ |
| 345 | static inline int fscrypt_setup_filename(struct inode *dir, | 350 | static inline int fscrypt_setup_filename(struct inode *dir, |
| 346 | const struct qstr *iname, | 351 | const struct qstr *iname, |
