Whamcloud - gitweb
LU-13717 sec: filename encryption - symlink support
[fs/lustre-release.git] / lustre / llite / symlink.c
index 20a61be..1319c09 100644 (file)
 #include "llite_internal.h"
 
 /* Must be called with lli_size_mutex locked */
+/* HAVE_IOP_GET_LINK is defined from kernel 4.5, whereas
+ * IS_ENCRYPTED is brought by kernel 4.14.
+ * So there is no need to handle encryption case otherwise.
+ */
+#ifdef HAVE_IOP_GET_LINK
+static int ll_readlink_internal(struct inode *inode,
+                               struct ptlrpc_request **request,
+                               char **symname, struct delayed_call *done)
+#else
 static int ll_readlink_internal(struct inode *inode,
                                struct ptlrpc_request **request, char **symname)
+#endif
 {
        struct ll_inode_info *lli = ll_i2info(inode);
        struct ll_sb_info *sbi = ll_i2sbi(inode);
@@ -98,7 +108,9 @@ static int ll_readlink_internal(struct inode *inode,
        }
 
        *symname = req_capsule_server_get(&(*request)->rq_pill, &RMF_MDT_MD);
-       if (!*symname || strnlen(*symname, symlen) != symlen - 1) {
+       if (!*symname ||
+           (!IS_ENCRYPTED(inode) &&
+            strnlen(*symname, symlen) != symlen - 1)) {
                /* not full/NULL terminated */
                CERROR("%s: inode "DFID": symlink not NULL terminated string of length %d\n",
                       sbi->ll_fsname,
@@ -106,6 +118,23 @@ static int ll_readlink_internal(struct inode *inode,
                GOTO(failed, rc = -EPROTO);
        }
 
+#ifdef HAVE_IOP_GET_LINK
+       if (IS_ENCRYPTED(inode)) {
+               const char *target = llcrypt_get_symlink(inode, *symname,
+                                                        symlen, done);
+               if (IS_ERR(target))
+                       RETURN(PTR_ERR(target));
+               symlen = strlen(target) + 1;
+               *symname = (char *)target;
+
+               /* Do not cache symlink targets encoded without the key,
+                * since those become outdated once the key is added.
+                */
+               if (!llcrypt_has_encryption_key(inode))
+                       RETURN(0);
+       }
+#endif
+
        OBD_ALLOC(lli->lli_symlink_name, symlen);
        /* do not return an error if we cannot cache the symlink locally */
        if (lli->lli_symlink_name) {
@@ -181,11 +210,12 @@ static const char *ll_get_link(struct dentry *dentry,
        int rc;
 
        ENTRY;
-       CDEBUG(D_VFSTRACE, "VFS Op\n");
+       CDEBUG(D_VFSTRACE, "VFS Op:name=%pd, inode="DFID"(%p)\n",
+              dentry, PFID(ll_inode2fid(inode)), inode);
        if (!dentry)
                RETURN(ERR_PTR(-ECHILD));
        ll_inode_size_lock(inode);
-       rc = ll_readlink_internal(inode, &request, &symname);
+       rc = ll_readlink_internal(inode, &request, &symname, done);
        ll_inode_size_unlock(inode);
        if (rc < 0) {
                ptlrpc_req_finished(request);
@@ -228,6 +258,61 @@ static const char *ll_follow_link(struct dentry *dentry, void **cookie)
 # endif /* HAVE_IOP_GET_LINK */
 #endif /* HAVE_SYMLINK_OPS_USE_NAMEIDATA */
 
+#ifdef HAVE_INODEOPS_ENHANCED_GETATTR
+/**
+ * ll_getattr_link() - link-specific getattr to set the correct st_size
+ *                    for encrypted symlinks
+ *
+ * Override st_size of encrypted symlinks to be the length of the decrypted
+ * symlink target (or the no-key encoded symlink target, if the key is
+ * unavailable) rather than the length of the encrypted symlink target. This is
+ * necessary for st_size to match the symlink target that userspace actually
+ * sees.  POSIX requires this, and some userspace programs depend on it.
+ *
+ * For non encrypted symlinks, this is a just calling ll_getattr().
+ * For encrypted symlinks, this additionally requires reading the symlink target
+ * from disk if needed, setting up the inode's encryption key if possible, and
+ * then decrypting or encoding the symlink target.  This makes lstat() more
+ * heavyweight than is normally the case.  However, decrypted symlink targets
+ * will be cached in ->i_link, so usually the symlink won't have to be read and
+ * decrypted again later if/when it is actually followed, readlink() is called,
+ * or lstat() is called again.
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int ll_getattr_link(const struct path *path, struct kstat *stat,
+                          u32 request_mask, unsigned int flags)
+{
+       struct dentry *dentry = path->dentry;
+       struct inode *inode = d_inode(dentry);
+       DEFINE_DELAYED_CALL(done);
+       const char *link;
+       int rc;
+
+       rc = ll_getattr(path, stat, request_mask, flags);
+       if (rc || !IS_ENCRYPTED(inode))
+               return rc;
+
+       /*
+        * To get the symlink target that userspace will see (whether it's the
+        * decrypted target or the no-key encoded target), we can just get it
+        * in the same way the VFS does during path resolution and readlink().
+        */
+       link = READ_ONCE(inode->i_link);
+       if (!link) {
+               link = inode->i_op->get_link(dentry, inode, &done);
+               if (IS_ERR(link))
+                       return PTR_ERR(link);
+       }
+       stat->size = strlen(link);
+       do_delayed_call(&done);
+       return 0;
+}
+#else /* HAVE_INODEOPS_ENHANCED_GETATTR */
+#define ll_getattr_link ll_getattr
+#endif
+
+
 const struct inode_operations ll_fast_symlink_inode_operations = {
 #ifdef HAVE_IOP_GENERIC_READLINK
        .readlink       = generic_readlink,
@@ -239,7 +324,7 @@ const struct inode_operations ll_fast_symlink_inode_operations = {
        .follow_link    = ll_follow_link,
        .put_link       = ll_put_link,
 #endif
-       .getattr        = ll_getattr,
+       .getattr        = ll_getattr_link,
        .permission     = ll_inode_permission,
 #ifdef HAVE_IOP_XATTR
        .setxattr       = ll_setxattr,