Commit 2c58d548 authored by Eric Biggers's avatar Eric Biggers Committed by Theodore Ts'o

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: default avatarEric Biggers <ebiggers@google.com>
Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
parent 4c4f7c19
...@@ -189,11 +189,9 @@ int __fscrypt_encrypt_symlink(struct inode *inode, const char *target, ...@@ -189,11 +189,9 @@ int __fscrypt_encrypt_symlink(struct inode *inode, const char *target,
sd->len = cpu_to_le16(ciphertext_len); sd->len = cpu_to_le16(ciphertext_len);
err = fname_encrypt(inode, &iname, sd->encrypted_path, ciphertext_len); err = fname_encrypt(inode, &iname, sd->encrypted_path, ciphertext_len);
if (err) { if (err)
if (!disk_link->name) goto err_free_sd;
kfree(sd);
return err;
}
/* /*
* Null-terminating the ciphertext doesn't make sense, but we still * Null-terminating the ciphertext doesn't make sense, but we still
* count the null terminator in the length, so we might as well * 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,9 +199,20 @@ int __fscrypt_encrypt_symlink(struct inode *inode, const char *target,
*/ */
sd->encrypted_path[ciphertext_len] = '\0'; sd->encrypted_path[ciphertext_len] = '\0';
/* Cache the plaintext symlink target for later use by get_link() */
err = -ENOMEM;
inode->i_link = kmemdup(target, len + 1, GFP_NOFS);
if (!inode->i_link)
goto err_free_sd;
if (!disk_link->name) if (!disk_link->name)
disk_link->name = (unsigned char *)sd; disk_link->name = (unsigned char *)sd;
return 0; return 0;
err_free_sd:
if (!disk_link->name)
kfree(sd);
return err;
} }
EXPORT_SYMBOL_GPL(__fscrypt_encrypt_symlink); EXPORT_SYMBOL_GPL(__fscrypt_encrypt_symlink);
...@@ -212,7 +221,7 @@ EXPORT_SYMBOL_GPL(__fscrypt_encrypt_symlink); ...@@ -212,7 +221,7 @@ EXPORT_SYMBOL_GPL(__fscrypt_encrypt_symlink);
* @inode: the symlink inode * @inode: the symlink inode
* @caddr: the on-disk contents of the symlink * @caddr: the on-disk contents of the symlink
* @max_size: size of @caddr buffer * @max_size: size of @caddr buffer
* @done: if successful, will be set up to free the returned target * @done: if successful, will be set up to free the returned target if needed
* *
* If the symlink's encryption key is available, we decrypt its target. * If the symlink's encryption key is available, we decrypt its target.
* Otherwise, we encode its target for presentation. * Otherwise, we encode its target for presentation.
...@@ -227,12 +236,18 @@ const char *fscrypt_get_symlink(struct inode *inode, const void *caddr, ...@@ -227,12 +236,18 @@ const char *fscrypt_get_symlink(struct inode *inode, const void *caddr,
{ {
const struct fscrypt_symlink_data *sd; const struct fscrypt_symlink_data *sd;
struct fscrypt_str cstr, pstr; struct fscrypt_str cstr, pstr;
bool has_key;
int err; int err;
/* This is for encrypted symlinks only */ /* This is for encrypted symlinks only */
if (WARN_ON(!IS_ENCRYPTED(inode))) if (WARN_ON(!IS_ENCRYPTED(inode)))
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
/* If the decrypted target is already cached, just return it. */
pstr.name = READ_ONCE(inode->i_link);
if (pstr.name)
return pstr.name;
/* /*
* Try to set up the symlink's encryption key, but we can continue * Try to set up the symlink's encryption key, but we can continue
* regardless of whether the key is available or not. * 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,6 +255,7 @@ const char *fscrypt_get_symlink(struct inode *inode, const void *caddr,
err = fscrypt_get_encryption_info(inode); err = fscrypt_get_encryption_info(inode);
if (err) if (err)
return ERR_PTR(err); return ERR_PTR(err);
has_key = fscrypt_has_encryption_key(inode);
/* /*
* For historical reasons, encrypted symlink targets are prefixed with * 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,7 +287,17 @@ const char *fscrypt_get_symlink(struct inode *inode, const void *caddr,
goto err_kfree; goto err_kfree;
pstr.name[pstr.len] = '\0'; pstr.name[pstr.len] = '\0';
set_delayed_call(done, kfree_link, pstr.name);
/*
* Cache decrypted symlink targets in i_link for later use. Don't cache
* symlink targets encoded without the key, since those become outdated
* once the key is added. This pairs with the READ_ONCE() above and in
* the VFS path lookup code.
*/
if (!has_key ||
cmpxchg_release(&inode->i_link, NULL, pstr.name) != NULL)
set_delayed_call(done, kfree_link, pstr.name);
return pstr.name; return pstr.name;
err_kfree: err_kfree:
......
...@@ -584,9 +584,30 @@ int fscrypt_get_encryption_info(struct inode *inode) ...@@ -584,9 +584,30 @@ int fscrypt_get_encryption_info(struct inode *inode)
} }
EXPORT_SYMBOL(fscrypt_get_encryption_info); EXPORT_SYMBOL(fscrypt_get_encryption_info);
/**
* fscrypt_put_encryption_info - free most of an inode's fscrypt data
*
* Free the inode's fscrypt_info. Filesystems must call this when the inode is
* being evicted. An RCU grace period need not have elapsed yet.
*/
void fscrypt_put_encryption_info(struct inode *inode) void fscrypt_put_encryption_info(struct inode *inode)
{ {
put_crypt_info(inode->i_crypt_info); put_crypt_info(inode->i_crypt_info);
inode->i_crypt_info = NULL; inode->i_crypt_info = NULL;
} }
EXPORT_SYMBOL(fscrypt_put_encryption_info); EXPORT_SYMBOL(fscrypt_put_encryption_info);
/**
* fscrypt_free_inode - free an inode's fscrypt data requiring RCU delay
*
* Free the inode's cached decrypted symlink target, if any. Filesystems must
* call this after an RCU grace period, just before they free the inode.
*/
void fscrypt_free_inode(struct inode *inode)
{
if (IS_ENCRYPTED(inode) && S_ISLNK(inode->i_mode)) {
kfree(inode->i_link);
inode->i_link = NULL;
}
}
EXPORT_SYMBOL(fscrypt_free_inode);
...@@ -1110,6 +1110,9 @@ static int ext4_drop_inode(struct inode *inode) ...@@ -1110,6 +1110,9 @@ static int ext4_drop_inode(struct inode *inode)
static void ext4_i_callback(struct rcu_head *head) static void ext4_i_callback(struct rcu_head *head)
{ {
struct inode *inode = container_of(head, struct inode, i_rcu); struct inode *inode = container_of(head, struct inode, i_rcu);
fscrypt_free_inode(inode);
kmem_cache_free(ext4_inode_cachep, EXT4_I(inode)); kmem_cache_free(ext4_inode_cachep, EXT4_I(inode));
} }
......
...@@ -1003,6 +1003,9 @@ static void f2fs_dirty_inode(struct inode *inode, int flags) ...@@ -1003,6 +1003,9 @@ static void f2fs_dirty_inode(struct inode *inode, int flags)
static void f2fs_i_callback(struct rcu_head *head) static void f2fs_i_callback(struct rcu_head *head)
{ {
struct inode *inode = container_of(head, struct inode, i_rcu); struct inode *inode = container_of(head, struct inode, i_rcu);
fscrypt_free_inode(inode);
kmem_cache_free(f2fs_inode_cachep, F2FS_I(inode)); kmem_cache_free(f2fs_inode_cachep, F2FS_I(inode));
} }
......
...@@ -276,7 +276,10 @@ static void ubifs_i_callback(struct rcu_head *head) ...@@ -276,7 +276,10 @@ static void ubifs_i_callback(struct rcu_head *head)
{ {
struct inode *inode = container_of(head, struct inode, i_rcu); struct inode *inode = container_of(head, struct inode, i_rcu);
struct ubifs_inode *ui = ubifs_inode(inode); struct ubifs_inode *ui = ubifs_inode(inode);
kfree(ui->data); kfree(ui->data);
fscrypt_free_inode(inode);
kmem_cache_free(ubifs_inode_slab, ui); kmem_cache_free(ubifs_inode_slab, ui);
} }
......
...@@ -128,6 +128,7 @@ extern int fscrypt_inherit_context(struct inode *, struct inode *, ...@@ -128,6 +128,7 @@ extern int fscrypt_inherit_context(struct inode *, struct inode *,
/* keyinfo.c */ /* keyinfo.c */
extern int fscrypt_get_encryption_info(struct inode *); extern int fscrypt_get_encryption_info(struct inode *);
extern void fscrypt_put_encryption_info(struct inode *); extern void fscrypt_put_encryption_info(struct inode *);
extern void fscrypt_free_inode(struct inode *);
/* fname.c */ /* fname.c */
extern int fscrypt_setup_filename(struct inode *, const struct qstr *, 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,6 +342,10 @@ static inline void fscrypt_put_encryption_info(struct inode *inode)
return; return;
} }
static inline void fscrypt_free_inode(struct inode *inode)
{
}
/* fname.c */ /* fname.c */
static inline int fscrypt_setup_filename(struct inode *dir, static inline int fscrypt_setup_filename(struct inode *dir,
const struct qstr *iname, const struct qstr *iname,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment