ceph: mark directory as non-complete after loading key
authorLuís Henriques <lhenriques@suse.de>
Tue, 29 Nov 2022 10:39:49 +0000 (10:39 +0000)
committerIlya Dryomov <idryomov@gmail.com>
Thu, 24 Aug 2023 09:24:35 +0000 (11:24 +0200)
When setting a directory's crypt context, ceph_dir_clear_complete()
needs to be called otherwise if it was complete before, any existing
(old) dentry will still be valid.

This patch adds a wrapper around __fscrypt_prepare_readdir() which will
ensure a directory is marked as non-complete if key status changes.

[ xiubli: revise commit title per Milind ]

Signed-off-by: Luís Henriques <lhenriques@suse.de>
Reviewed-by: Xiubo Li <xiubli@redhat.com>
Reviewed-by: Milind Changire <mchangir@redhat.com>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
fs/ceph/crypto.c
fs/ceph/crypto.h
fs/ceph/dir.c
fs/ceph/mds_client.c

index a08978a..5df2c52 100644 (file)
@@ -287,8 +287,8 @@ int ceph_fname_to_usr(const struct ceph_fname *fname, struct fscrypt_str *tname,
        if (fname->name_len > NAME_MAX || fname->ctext_len > NAME_MAX)
                return -EIO;
 
-       ret = __fscrypt_prepare_readdir(fname->dir);
-       if (ret)
+       ret = ceph_fscrypt_prepare_readdir(fname->dir);
+       if (ret < 0)
                return ret;
 
        /*
@@ -334,3 +334,34 @@ out:
        fscrypt_fname_free_buffer(&_tname);
        return ret;
 }
+
+/**
+ * ceph_fscrypt_prepare_readdir - simple __fscrypt_prepare_readdir() wrapper
+ * @dir: directory inode for readdir prep
+ *
+ * Simple wrapper around __fscrypt_prepare_readdir() that will mark directory as
+ * non-complete if this call results in having the directory unlocked.
+ *
+ * Returns:
+ *     1 - if directory was locked and key is now loaded (i.e. dir is unlocked)
+ *     0 - if directory is still locked
+ *   < 0 - if __fscrypt_prepare_readdir() fails
+ */
+int ceph_fscrypt_prepare_readdir(struct inode *dir)
+{
+       bool had_key = fscrypt_has_encryption_key(dir);
+       int err;
+
+       if (!IS_ENCRYPTED(dir))
+               return 0;
+
+       err = __fscrypt_prepare_readdir(dir);
+       if (err)
+               return err;
+       if (!had_key && fscrypt_has_encryption_key(dir)) {
+               /* directory just got unlocked, mark it as not complete */
+               ceph_dir_clear_complete(dir);
+               return 1;
+       }
+       return 0;
+}
index 21694df..b5413ec 100644 (file)
@@ -103,6 +103,7 @@ static inline void ceph_fname_free_buffer(struct inode *parent,
 
 int ceph_fname_to_usr(const struct ceph_fname *fname, struct fscrypt_str *tname,
                      struct fscrypt_str *oname, bool *is_nokey);
+int ceph_fscrypt_prepare_readdir(struct inode *dir);
 
 #else /* CONFIG_FS_ENCRYPTION */
 
@@ -160,6 +161,11 @@ static inline int ceph_fname_to_usr(const struct ceph_fname *fname,
        oname->len = fname->name_len;
        return 0;
 }
+
+static inline int ceph_fscrypt_prepare_readdir(struct inode *dir)
+{
+       return 0;
+}
 #endif /* CONFIG_FS_ENCRYPTION */
 
 #endif
index 99fdc77..08504af 100644 (file)
@@ -343,8 +343,8 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
                ctx->pos = 2;
        }
 
-       err = fscrypt_prepare_readdir(inode);
-       if (err)
+       err = ceph_fscrypt_prepare_readdir(inode);
+       if (err < 0)
                return err;
 
        spin_lock(&ci->i_ceph_lock);
@@ -785,8 +785,8 @@ static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry,
                return ERR_PTR(-ENAMETOOLONG);
 
        if (IS_ENCRYPTED(dir)) {
-               err = __fscrypt_prepare_readdir(dir);
-               if (err)
+               err = ceph_fscrypt_prepare_readdir(dir);
+               if (err < 0)
                        return ERR_PTR(err);
                if (!fscrypt_has_encryption_key(dir)) {
                        spin_lock(&dentry->d_lock);
index 447acc4..7de2205 100644 (file)
@@ -2545,8 +2545,8 @@ static u8 *get_fscrypt_altname(const struct ceph_mds_request *req, u32 *plen)
        if (!IS_ENCRYPTED(dir))
                goto success;
 
-       ret = __fscrypt_prepare_readdir(dir);
-       if (ret)
+       ret = ceph_fscrypt_prepare_readdir(dir);
+       if (ret < 0)
                return ERR_PTR(ret);
 
        /* No key? Just ignore it. */
@@ -2666,7 +2666,7 @@ retry:
                        spin_unlock(&cur->d_lock);
                        parent = dget_parent(cur);
 
-                       ret = __fscrypt_prepare_readdir(d_inode(parent));
+                       ret = ceph_fscrypt_prepare_readdir(d_inode(parent));
                        if (ret < 0) {
                                dput(parent);
                                dput(cur);