fscrypt: new helper function - fscrypt_get_symlink()
authorEric Biggers <ebiggers@google.com>
Fri, 5 Jan 2018 18:45:02 +0000 (10:45 -0800)
committerJaegeuk Kim <jaegeuk@kernel.org>
Sun, 8 Apr 2018 09:40:15 +0000 (02:40 -0700)
Filesystems also have duplicate code to support ->get_link() on
encrypted symlinks.  Factor it out into a new function
fscrypt_get_symlink().  It takes in the contents of the encrypted
symlink on-disk and provides the target (decrypted or encoded) that
should be returned from ->get_link().

Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
fs/crypto/hooks.c
include/linux/fscrypt_notsupp.h
include/linux/fscrypt_supp.h

index 4b83e4af2e4174c4ebd549d4d3612dc2896a36a1..8900e348ba6e3b7cedde88d0efdb8719fa1b03ed 100644 (file)
@@ -200,3 +200,76 @@ int __fscrypt_encrypt_symlink(struct inode *inode, const char *target,
        return 0;
 }
 EXPORT_SYMBOL_GPL(__fscrypt_encrypt_symlink);
+
+/**
+ * fscrypt_get_symlink - get the target of an encrypted symlink
+ * @inode: the symlink inode
+ * @caddr: the on-disk contents of the symlink
+ * @max_size: size of @caddr buffer
+ * @done: if successful, will be set up to free the returned target
+ *
+ * If the symlink's encryption key is available, we decrypt its target.
+ * Otherwise, we encode its target for presentation.
+ *
+ * This may sleep, so the filesystem must have dropped out of RCU mode already.
+ *
+ * Return: the presentable symlink target or an ERR_PTR()
+ */
+const char *fscrypt_get_symlink(struct inode *inode, const void *caddr,
+                               unsigned int max_size,
+                               struct delayed_call *done)
+{
+       const struct fscrypt_symlink_data *sd;
+       struct fscrypt_str cstr, pstr;
+       int err;
+
+       /* This is for encrypted symlinks only */
+       if (WARN_ON(!IS_ENCRYPTED(inode)))
+               return ERR_PTR(-EINVAL);
+
+       /*
+        * Try to set up the symlink's encryption key, but we can continue
+        * regardless of whether the key is available or not.
+        */
+       err = fscrypt_get_encryption_info(inode);
+       if (err)
+               return ERR_PTR(err);
+
+       /*
+        * For historical reasons, encrypted symlink targets are prefixed with
+        * the ciphertext length, even though this is redundant with i_size.
+        */
+
+       if (max_size < sizeof(*sd))
+               return ERR_PTR(-EUCLEAN);
+       sd = caddr;
+       cstr.name = (unsigned char *)sd->encrypted_path;
+       cstr.len = le16_to_cpu(sd->len);
+
+       if (cstr.len == 0)
+               return ERR_PTR(-EUCLEAN);
+
+       if (cstr.len + sizeof(*sd) - 1 > max_size)
+               return ERR_PTR(-EUCLEAN);
+
+       err = fscrypt_fname_alloc_buffer(inode, cstr.len, &pstr);
+       if (err)
+               return ERR_PTR(err);
+
+       err = fscrypt_fname_disk_to_usr(inode, 0, 0, &cstr, &pstr);
+       if (err)
+               goto err_kfree;
+
+       err = -EUCLEAN;
+       if (pstr.name[0] == '\0')
+               goto err_kfree;
+
+       pstr.name[pstr.len] = '\0';
+       set_delayed_call(done, kfree_link, pstr.name);
+       return pstr.name;
+
+err_kfree:
+       kfree(pstr.name);
+       return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(fscrypt_get_symlink);
index 02ec0aa894d8935a1a345f7649c85fab1860f029..dd106640c6eabe43419c95387bf1079939e749ff 100644 (file)
@@ -239,4 +239,12 @@ static inline int __fscrypt_encrypt_symlink(struct inode *inode,
        return -EOPNOTSUPP;
 }
 
+static inline const char *fscrypt_get_symlink(struct inode *inode,
+                                             const void *caddr,
+                                             unsigned int max_size,
+                                             struct delayed_call *done)
+{
+       return ERR_PTR(-EOPNOTSUPP);
+}
+
 #endif /* _LINUX_FSCRYPT_NOTSUPP_H */
index 7e0b67ccd816db3f7969de0bc0b34e86d2a4146b..dc2babf3f7d322a8c8e08efae000b53e63be5a32 100644 (file)
@@ -211,5 +211,8 @@ extern int __fscrypt_prepare_symlink(struct inode *dir, unsigned int len,
 extern int __fscrypt_encrypt_symlink(struct inode *inode, const char *target,
                                     unsigned int len,
                                     struct fscrypt_str *disk_link);
+extern const char *fscrypt_get_symlink(struct inode *inode, const void *caddr,
+                                      unsigned int max_size,
+                                      struct delayed_call *done);
 
 #endif /* _LINUX_FSCRYPT_SUPP_H */