diff options
Diffstat (limited to 'fs/crypto/hooks.c')
-rw-r--r-- | fs/crypto/hooks.c | 54 |
1 files changed, 49 insertions, 5 deletions
diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index a3582f2ca376..7173233c26ca 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c | |||
@@ -59,8 +59,8 @@ int __fscrypt_prepare_link(struct inode *inode, struct inode *dir, | |||
59 | if (err) | 59 | if (err) |
60 | return err; | 60 | return err; |
61 | 61 | ||
62 | /* ... in case we looked up ciphertext name before key was added */ | 62 | /* ... in case we looked up no-key name before key was added */ |
63 | if (dentry->d_flags & DCACHE_ENCRYPTED_NAME) | 63 | if (fscrypt_is_nokey_name(dentry)) |
64 | return -ENOKEY; | 64 | return -ENOKEY; |
65 | 65 | ||
66 | if (!fscrypt_has_permitted_context(dir, inode)) | 66 | if (!fscrypt_has_permitted_context(dir, inode)) |
@@ -84,9 +84,9 @@ int __fscrypt_prepare_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
84 | if (err) | 84 | if (err) |
85 | return err; | 85 | return err; |
86 | 86 | ||
87 | /* ... in case we looked up ciphertext name(s) before key was added */ | 87 | /* ... in case we looked up no-key name(s) before key was added */ |
88 | if ((old_dentry->d_flags | new_dentry->d_flags) & | 88 | if (fscrypt_is_nokey_name(old_dentry) || |
89 | DCACHE_ENCRYPTED_NAME) | 89 | fscrypt_is_nokey_name(new_dentry)) |
90 | return -ENOKEY; | 90 | return -ENOKEY; |
91 | 91 | ||
92 | if (old_dir != new_dir) { | 92 | if (old_dir != new_dir) { |
@@ -348,3 +348,47 @@ err_kfree: | |||
348 | return ERR_PTR(err); | 348 | return ERR_PTR(err); |
349 | } | 349 | } |
350 | EXPORT_SYMBOL_GPL(fscrypt_get_symlink); | 350 | EXPORT_SYMBOL_GPL(fscrypt_get_symlink); |
351 | |||
352 | /** | ||
353 | * fscrypt_symlink_getattr() - set the correct st_size for encrypted symlinks | ||
354 | * @path: the path for the encrypted symlink being queried | ||
355 | * @stat: the struct being filled with the symlink's attributes | ||
356 | * | ||
357 | * Override st_size of encrypted symlinks to be the length of the decrypted | ||
358 | * symlink target (or the no-key encoded symlink target, if the key is | ||
359 | * unavailable) rather than the length of the encrypted symlink target. This is | ||
360 | * necessary for st_size to match the symlink target that userspace actually | ||
361 | * sees. POSIX requires this, and some userspace programs depend on it. | ||
362 | * | ||
363 | * This requires reading the symlink target from disk if needed, setting up the | ||
364 | * inode's encryption key if possible, and then decrypting or encoding the | ||
365 | * symlink target. This makes lstat() more heavyweight than is normally the | ||
366 | * case. However, decrypted symlink targets will be cached in ->i_link, so | ||
367 | * usually the symlink won't have to be read and decrypted again later if/when | ||
368 | * it is actually followed, readlink() is called, or lstat() is called again. | ||
369 | * | ||
370 | * Return: 0 on success, -errno on failure | ||
371 | */ | ||
372 | int fscrypt_symlink_getattr(const struct path *path, struct kstat *stat) | ||
373 | { | ||
374 | struct dentry *dentry = path->dentry; | ||
375 | struct inode *inode = d_inode(dentry); | ||
376 | const char *link; | ||
377 | DEFINE_DELAYED_CALL(done); | ||
378 | |||
379 | /* | ||
380 | * To get the symlink target that userspace will see (whether it's the | ||
381 | * decrypted target or the no-key encoded target), we can just get it in | ||
382 | * the same way the VFS does during path resolution and readlink(). | ||
383 | */ | ||
384 | link = READ_ONCE(inode->i_link); | ||
385 | if (!link) { | ||
386 | link = inode->i_op->get_link(dentry, inode, &done); | ||
387 | if (IS_ERR(link)) | ||
388 | return PTR_ERR(link); | ||
389 | } | ||
390 | stat->size = strlen(link); | ||
391 | do_delayed_call(&done); | ||
392 | return 0; | ||
393 | } | ||
394 | EXPORT_SYMBOL_GPL(fscrypt_symlink_getattr); | ||