nvmem: Do not expect fixed layouts to grab a layout driver
[platform/kernel/linux-starfive.git] / fs / ceph / dir.c
index bdcffb0..854cbdd 100644 (file)
@@ -9,6 +9,7 @@
 
 #include "super.h"
 #include "mds_client.h"
+#include "crypto.h"
 
 /*
  * Directory operations: readdir, lookup, create, link, unlink,
@@ -241,7 +242,9 @@ static int __dcache_readdir(struct file *file,  struct dir_context *ctx,
                di = ceph_dentry(dentry);
                if (d_unhashed(dentry) ||
                    d_really_is_negative(dentry) ||
-                   di->lease_shared_gen != shared_gen) {
+                   di->lease_shared_gen != shared_gen ||
+                   ((dentry->d_flags & DCACHE_NOKEY_NAME) &&
+                    fscrypt_has_encryption_key(dir))) {
                        spin_unlock(&dentry->d_lock);
                        dput(dentry);
                        err = -EAGAIN;
@@ -340,6 +343,10 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
                ctx->pos = 2;
        }
 
+       err = ceph_fscrypt_prepare_readdir(inode);
+       if (err < 0)
+               return err;
+
        spin_lock(&ci->i_ceph_lock);
        /* request Fx cap. if have Fx, we don't need to release Fs cap
         * for later create/unlink. */
@@ -389,6 +396,7 @@ more:
                req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS);
                if (IS_ERR(req))
                        return PTR_ERR(req);
+
                err = ceph_alloc_readdir_reply_buffer(req, inode);
                if (err) {
                        ceph_mdsc_put_request(req);
@@ -402,11 +410,21 @@ more:
                        req->r_inode_drop = CEPH_CAP_FILE_EXCL;
                }
                if (dfi->last_name) {
-                       req->r_path2 = kstrdup(dfi->last_name, GFP_KERNEL);
+                       struct qstr d_name = { .name = dfi->last_name,
+                                              .len = strlen(dfi->last_name) };
+
+                       req->r_path2 = kzalloc(NAME_MAX + 1, GFP_KERNEL);
                        if (!req->r_path2) {
                                ceph_mdsc_put_request(req);
                                return -ENOMEM;
                        }
+
+                       err = ceph_encode_encrypted_dname(inode, &d_name,
+                                                         req->r_path2);
+                       if (err < 0) {
+                               ceph_mdsc_put_request(req);
+                               return err;
+                       }
                } else if (is_hash_order(ctx->pos)) {
                        req->r_args.readdir.offset_hash =
                                cpu_to_le32(fpos_hash(ctx->pos));
@@ -511,15 +529,20 @@ more:
        for (; i < rinfo->dir_nr; i++) {
                struct ceph_mds_reply_dir_entry *rde = rinfo->dir_entries + i;
 
-               BUG_ON(rde->offset < ctx->pos);
+               if (rde->offset < ctx->pos) {
+                       pr_warn("%s: rde->offset 0x%llx ctx->pos 0x%llx\n",
+                               __func__, rde->offset, ctx->pos);
+                       return -EIO;
+               }
+
+               if (WARN_ON_ONCE(!rde->inode.in))
+                       return -EIO;
 
                ctx->pos = rde->offset;
                dout("readdir (%d/%d) -> %llx '%.*s' %p\n",
                     i, rinfo->dir_nr, ctx->pos,
                     rde->name_len, rde->name, &rde->inode.in);
 
-               BUG_ON(!rde->inode.in);
-
                if (!dir_emit(ctx, rde->name, rde->name_len,
                              ceph_present_ino(inode->i_sb, le64_to_cpu(rde->inode.in->ino)),
                              le32_to_cpu(rde->inode.in->mode) >> 12)) {
@@ -532,6 +555,8 @@ more:
                        dout("filldir stopping us...\n");
                        return 0;
                }
+
+               /* Reset the lengths to their original allocated vals */
                ctx->pos++;
        }
 
@@ -586,7 +611,6 @@ more:
                                        dfi->dir_ordered_count);
                spin_unlock(&ci->i_ceph_lock);
        }
-
        dout("readdir %p file %p done.\n", inode, file);
        return 0;
 }
@@ -760,6 +784,18 @@ static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry,
        if (dentry->d_name.len > NAME_MAX)
                return ERR_PTR(-ENAMETOOLONG);
 
+       if (IS_ENCRYPTED(dir)) {
+               bool had_key = fscrypt_has_encryption_key(dir);
+
+               err = fscrypt_prepare_lookup_partial(dir, dentry);
+               if (err < 0)
+                       return ERR_PTR(err);
+
+               /* mark directory as incomplete if it has been unlocked */
+               if (!had_key && fscrypt_has_encryption_key(dir))
+                       ceph_dir_clear_complete(dir);
+       }
+
        /* can we conclude ENOENT locally? */
        if (d_really_is_negative(dentry)) {
                struct ceph_inode_info *ci = ceph_inode(dir);
@@ -865,13 +901,6 @@ static int ceph_mknod(struct mnt_idmap *idmap, struct inode *dir,
                goto out;
        }
 
-       err = ceph_pre_init_acls(dir, &mode, &as_ctx);
-       if (err < 0)
-               goto out;
-       err = ceph_security_init_secctx(dentry, mode, &as_ctx);
-       if (err < 0)
-               goto out;
-
        dout("mknod in dir %p dentry %p mode 0%ho rdev %d\n",
             dir, dentry, mode, rdev);
        req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_MKNOD, USE_AUTH_MDS);
@@ -879,6 +908,17 @@ static int ceph_mknod(struct mnt_idmap *idmap, struct inode *dir,
                err = PTR_ERR(req);
                goto out;
        }
+
+       req->r_new_inode = ceph_new_inode(dir, dentry, &mode, &as_ctx);
+       if (IS_ERR(req->r_new_inode)) {
+               err = PTR_ERR(req->r_new_inode);
+               req->r_new_inode = NULL;
+               goto out_req;
+       }
+
+       if (S_ISREG(mode) && IS_ENCRYPTED(dir))
+               set_bit(CEPH_MDS_R_FSCRYPT_FILE, &req->r_req_flags);
+
        req->r_dentry = dget(dentry);
        req->r_num_caps = 2;
        req->r_parent = dir;
@@ -889,13 +929,13 @@ static int ceph_mknod(struct mnt_idmap *idmap, struct inode *dir,
        req->r_dentry_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_AUTH_EXCL |
                             CEPH_CAP_XATTR_EXCL;
        req->r_dentry_unless = CEPH_CAP_FILE_EXCL;
-       if (as_ctx.pagelist) {
-               req->r_pagelist = as_ctx.pagelist;
-               as_ctx.pagelist = NULL;
-       }
+
+       ceph_as_ctx_to_req(req, &as_ctx);
+
        err = ceph_mdsc_do_request(mdsc, dir, req);
        if (!err && !req->r_reply_info.head->is_dentry)
                err = ceph_handle_notrace_create(dir, dentry);
+out_req:
        ceph_mdsc_put_request(req);
 out:
        if (!err)
@@ -912,12 +952,50 @@ static int ceph_create(struct mnt_idmap *idmap, struct inode *dir,
        return ceph_mknod(idmap, dir, dentry, mode, 0);
 }
 
+#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
+static int prep_encrypted_symlink_target(struct ceph_mds_request *req,
+                                        const char *dest)
+{
+       int err;
+       int len = strlen(dest);
+       struct fscrypt_str osd_link = FSTR_INIT(NULL, 0);
+
+       err = fscrypt_prepare_symlink(req->r_parent, dest, len, PATH_MAX,
+                                     &osd_link);
+       if (err)
+               goto out;
+
+       err = fscrypt_encrypt_symlink(req->r_new_inode, dest, len, &osd_link);
+       if (err)
+               goto out;
+
+       req->r_path2 = kmalloc(CEPH_BASE64_CHARS(osd_link.len) + 1, GFP_KERNEL);
+       if (!req->r_path2) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       len = ceph_base64_encode(osd_link.name, osd_link.len, req->r_path2);
+       req->r_path2[len] = '\0';
+out:
+       fscrypt_fname_free_buffer(&osd_link);
+       return err;
+}
+#else
+static int prep_encrypted_symlink_target(struct ceph_mds_request *req,
+                                        const char *dest)
+{
+       return -EOPNOTSUPP;
+}
+#endif
+
 static int ceph_symlink(struct mnt_idmap *idmap, struct inode *dir,
                        struct dentry *dentry, const char *dest)
 {
        struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dir->i_sb);
        struct ceph_mds_request *req;
        struct ceph_acl_sec_ctx as_ctx = {};
+       umode_t mode = S_IFLNK | 0777;
        int err;
 
        if (ceph_snap(dir) != CEPH_NOSNAP)
@@ -932,38 +1010,48 @@ static int ceph_symlink(struct mnt_idmap *idmap, struct inode *dir,
                goto out;
        }
 
-       err = ceph_security_init_secctx(dentry, S_IFLNK | 0777, &as_ctx);
-       if (err < 0)
-               goto out;
-
        dout("symlink in dir %p dentry %p to '%s'\n", dir, dentry, dest);
        req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SYMLINK, USE_AUTH_MDS);
        if (IS_ERR(req)) {
                err = PTR_ERR(req);
                goto out;
        }
-       req->r_path2 = kstrdup(dest, GFP_KERNEL);
-       if (!req->r_path2) {
-               err = -ENOMEM;
-               ceph_mdsc_put_request(req);
-               goto out;
+
+       req->r_new_inode = ceph_new_inode(dir, dentry, &mode, &as_ctx);
+       if (IS_ERR(req->r_new_inode)) {
+               err = PTR_ERR(req->r_new_inode);
+               req->r_new_inode = NULL;
+               goto out_req;
        }
+
        req->r_parent = dir;
        ihold(dir);
 
+       if (IS_ENCRYPTED(req->r_new_inode)) {
+               err = prep_encrypted_symlink_target(req, dest);
+               if (err)
+                       goto out_req;
+       } else {
+               req->r_path2 = kstrdup(dest, GFP_KERNEL);
+               if (!req->r_path2) {
+                       err = -ENOMEM;
+                       goto out_req;
+               }
+       }
+
        set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags);
        req->r_dentry = dget(dentry);
        req->r_num_caps = 2;
        req->r_dentry_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_AUTH_EXCL |
                             CEPH_CAP_XATTR_EXCL;
        req->r_dentry_unless = CEPH_CAP_FILE_EXCL;
-       if (as_ctx.pagelist) {
-               req->r_pagelist = as_ctx.pagelist;
-               as_ctx.pagelist = NULL;
-       }
+
+       ceph_as_ctx_to_req(req, &as_ctx);
+
        err = ceph_mdsc_do_request(mdsc, dir, req);
        if (!err && !req->r_reply_info.head->is_dentry)
                err = ceph_handle_notrace_create(dir, dentry);
+out_req:
        ceph_mdsc_put_request(req);
 out:
        if (err)
@@ -1003,14 +1091,12 @@ static int ceph_mkdir(struct mnt_idmap *idmap, struct inode *dir,
                err = -EDQUOT;
                goto out;
        }
-
-       mode |= S_IFDIR;
-       err = ceph_pre_init_acls(dir, &mode, &as_ctx);
-       if (err < 0)
-               goto out;
-       err = ceph_security_init_secctx(dentry, mode, &as_ctx);
-       if (err < 0)
+       if ((op == CEPH_MDS_OP_MKSNAP) && IS_ENCRYPTED(dir) &&
+           !fscrypt_has_encryption_key(dir)) {
+               err = -ENOKEY;
                goto out;
+       }
+
 
        req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS);
        if (IS_ERR(req)) {
@@ -1018,6 +1104,14 @@ static int ceph_mkdir(struct mnt_idmap *idmap, struct inode *dir,
                goto out;
        }
 
+       mode |= S_IFDIR;
+       req->r_new_inode = ceph_new_inode(dir, dentry, &mode, &as_ctx);
+       if (IS_ERR(req->r_new_inode)) {
+               err = PTR_ERR(req->r_new_inode);
+               req->r_new_inode = NULL;
+               goto out_req;
+       }
+
        req->r_dentry = dget(dentry);
        req->r_num_caps = 2;
        req->r_parent = dir;
@@ -1027,15 +1121,15 @@ static int ceph_mkdir(struct mnt_idmap *idmap, struct inode *dir,
        req->r_dentry_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_AUTH_EXCL |
                             CEPH_CAP_XATTR_EXCL;
        req->r_dentry_unless = CEPH_CAP_FILE_EXCL;
-       if (as_ctx.pagelist) {
-               req->r_pagelist = as_ctx.pagelist;
-               as_ctx.pagelist = NULL;
-       }
+
+       ceph_as_ctx_to_req(req, &as_ctx);
+
        err = ceph_mdsc_do_request(mdsc, dir, req);
        if (!err &&
            !req->r_reply_info.head->is_target &&
            !req->r_reply_info.head->is_dentry)
                err = ceph_handle_notrace_create(dir, dentry);
+out_req:
        ceph_mdsc_put_request(req);
 out:
        if (!err)
@@ -1063,6 +1157,10 @@ static int ceph_link(struct dentry *old_dentry, struct inode *dir,
        if (ceph_snap(dir) != CEPH_NOSNAP)
                return -EROFS;
 
+       err = fscrypt_prepare_link(old_dentry, dir, dentry);
+       if (err)
+               return err;
+
        dout("link in dir %p %llx.%llx old_dentry %p:'%pd' dentry %p:'%pd'\n",
             dir, ceph_vinop(dir), old_dentry, old_dentry, dentry, dentry);
        req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LINK, USE_AUTH_MDS);
@@ -1310,6 +1408,11 @@ static int ceph_rename(struct mnt_idmap *idmap, struct inode *old_dir,
        if (err)
                return err;
 
+       err = fscrypt_prepare_rename(old_dir, old_dentry, new_dir, new_dentry,
+                                    flags);
+       if (err)
+               return err;
+
        dout("rename dir %p dentry %p to dir %p dentry %p\n",
             old_dir, old_dentry, new_dir, new_dentry);
        req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS);
@@ -1765,6 +1868,10 @@ static int ceph_d_revalidate(struct dentry *dentry, unsigned int flags)
        struct inode *dir, *inode;
        struct ceph_mds_client *mdsc;
 
+       valid = fscrypt_d_revalidate(dentry, flags);
+       if (valid <= 0)
+               return valid;
+
        if (flags & LOOKUP_RCU) {
                parent = READ_ONCE(dentry->d_parent);
                dir = d_inode_rcu(parent);
@@ -1777,8 +1884,9 @@ static int ceph_d_revalidate(struct dentry *dentry, unsigned int flags)
                inode = d_inode(dentry);
        }
 
-       dout("d_revalidate %p '%pd' inode %p offset 0x%llx\n", dentry,
-            dentry, inode, ceph_dentry(dentry)->offset);
+       dout("d_revalidate %p '%pd' inode %p offset 0x%llx nokey %d\n", dentry,
+            dentry, inode, ceph_dentry(dentry)->offset,
+            !!(dentry->d_flags & DCACHE_NOKEY_NAME));
 
        mdsc = ceph_sb_to_client(dir->i_sb)->mdsc;