#include <linux/fscrypt.h>
 
 #include "super.h"
+#include "mds_client.h"
 #include "crypto.h"
 
 static int ceph_crypt_get_context(struct inode *inode, void *ctx, size_t len)
        return ci->i_rsubdirs + ci->i_rfiles == 1;
 }
 
+static const union fscrypt_policy *ceph_get_dummy_policy(struct super_block *sb)
+{
+       return ceph_sb_to_client(sb)->fsc_dummy_enc_policy.policy;
+}
+
 static struct fscrypt_operations ceph_fscrypt_ops = {
        .get_context            = ceph_crypt_get_context,
        .set_context            = ceph_crypt_set_context,
+       .get_dummy_policy       = ceph_get_dummy_policy,
        .empty_dir              = ceph_crypt_empty_dir,
 };
 
 {
        fscrypt_set_ops(sb, &ceph_fscrypt_ops);
 }
+
+void ceph_fscrypt_free_dummy_policy(struct ceph_fs_client *fsc)
+{
+       fscrypt_free_dummy_policy(&fsc->fsc_dummy_enc_policy);
+}
+
+int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode,
+                                struct ceph_acl_sec_ctx *as)
+{
+       int ret, ctxsize;
+       bool encrypted = false;
+       struct ceph_inode_info *ci = ceph_inode(inode);
+
+       ret = fscrypt_prepare_new_inode(dir, inode, &encrypted);
+       if (ret)
+               return ret;
+       if (!encrypted)
+               return 0;
+
+       as->fscrypt_auth = kzalloc(sizeof(*as->fscrypt_auth), GFP_KERNEL);
+       if (!as->fscrypt_auth)
+               return -ENOMEM;
+
+       ctxsize = fscrypt_context_for_new_inode(as->fscrypt_auth->cfa_blob,
+                                               inode);
+       if (ctxsize < 0)
+               return ctxsize;
+
+       as->fscrypt_auth->cfa_version = cpu_to_le32(CEPH_FSCRYPT_AUTH_VERSION);
+       as->fscrypt_auth->cfa_blob_len = cpu_to_le32(ctxsize);
+
+       WARN_ON_ONCE(ci->fscrypt_auth);
+       kfree(ci->fscrypt_auth);
+       ci->fscrypt_auth_len = ceph_fscrypt_auth_len(as->fscrypt_auth);
+       ci->fscrypt_auth = kmemdup(as->fscrypt_auth, ci->fscrypt_auth_len,
+                                  GFP_KERNEL);
+       if (!ci->fscrypt_auth)
+               return -ENOMEM;
+
+       inode->i_flags |= S_ENCRYPTED;
+
+       return 0;
+}
+
+void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req,
+                               struct ceph_acl_sec_ctx *as)
+{
+       swap(req->r_fscrypt_auth, as->fscrypt_auth);
+}
 
 
 #include <linux/fscrypt.h>
 
+struct ceph_fs_client;
+struct ceph_acl_sec_ctx;
+struct ceph_mds_request;
+
 struct ceph_fscrypt_auth {
        __le32  cfa_version;
        __le32  cfa_blob_len;
 #ifdef CONFIG_FS_ENCRYPTION
 void ceph_fscrypt_set_ops(struct super_block *sb);
 
+void ceph_fscrypt_free_dummy_policy(struct ceph_fs_client *fsc);
+
+int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode,
+                                struct ceph_acl_sec_ctx *as);
+void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req,
+                               struct ceph_acl_sec_ctx *as);
+
 #else /* CONFIG_FS_ENCRYPTION */
 
 static inline void ceph_fscrypt_set_ops(struct super_block *sb)
 {
 }
 
+static inline void ceph_fscrypt_free_dummy_policy(struct ceph_fs_client *fsc)
+{
+}
+
+static inline int ceph_fscrypt_prepare_context(struct inode *dir,
+                                              struct inode *inode,
+                                              struct ceph_acl_sec_ctx *as)
+{
+       if (IS_ENCRYPTED(dir))
+               return -EOPNOTSUPP;
+       return 0;
+}
+
+static inline void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req,
+                                               struct ceph_acl_sec_ctx *as_ctx)
+{
+}
 #endif /* CONFIG_FS_ENCRYPTION */
 
 #endif
 
        struct ceph_fs_client *fsc = ceph_sb_to_client(s);
 
        dout("put_super\n");
+       ceph_fscrypt_free_dummy_policy(fsc);
        ceph_mdsc_close_sessions(fsc->mdsc);
 }
 
        Opt_recover_session,
        Opt_source,
        Opt_mon_addr,
+       Opt_test_dummy_encryption,
        /* string args above */
        Opt_dirstat,
        Opt_rbytes,
        fsparam_string  ("fsc",                         Opt_fscache), // fsc=...
        fsparam_flag_no ("ino32",                       Opt_ino32),
        fsparam_string  ("mds_namespace",               Opt_mds_namespace),
+       fsparam_string  ("mon_addr",                    Opt_mon_addr),
        fsparam_flag_no ("poolperm",                    Opt_poolperm),
        fsparam_flag_no ("quotadf",                     Opt_quotadf),
        fsparam_u32     ("rasize",                      Opt_rasize),
        fsparam_u32     ("rsize",                       Opt_rsize),
        fsparam_string  ("snapdirname",                 Opt_snapdirname),
        fsparam_string  ("source",                      Opt_source),
-       fsparam_string  ("mon_addr",                    Opt_mon_addr),
+       fsparam_flag    ("test_dummy_encryption",       Opt_test_dummy_encryption),
+       fsparam_string  ("test_dummy_encryption",       Opt_test_dummy_encryption),
        fsparam_u32     ("wsize",                       Opt_wsize),
        fsparam_flag_no ("wsync",                       Opt_wsync),
        fsparam_flag_no ("pagecache",                   Opt_pagecache),
                else
                        fsopt->flags |= CEPH_MOUNT_OPT_SPARSEREAD;
                break;
+       case Opt_test_dummy_encryption:
+#ifdef CONFIG_FS_ENCRYPTION
+               fscrypt_free_dummy_policy(&fsopt->dummy_enc_policy);
+               ret = fscrypt_parse_test_dummy_encryption(param,
+                                               &fsopt->dummy_enc_policy);
+               if (ret == -EINVAL) {
+                       warnfc(fc, "Value of option \"%s\" is unrecognized",
+                              param->key);
+               } else if (ret == -EEXIST) {
+                       warnfc(fc, "Conflicting test_dummy_encryption options");
+                       ret = -EINVAL;
+               }
+#else
+               warnfc(fc,
+                      "FS encryption not supported: test_dummy_encryption mount option ignored");
+#endif
+               break;
        default:
                BUG();
        }
        kfree(args->server_path);
        kfree(args->fscache_uniq);
        kfree(args->mon_addr);
+       fscrypt_free_dummy_policy(&args->dummy_enc_policy);
        kfree(args);
 }
 
        if (fsopt->flags & CEPH_MOUNT_OPT_SPARSEREAD)
                seq_puts(m, ",sparseread");
 
+       fscrypt_show_test_dummy_encryption(m, ',', root->d_sb);
+
        if (fsopt->wsize != CEPH_MAX_WRITE_SIZE)
                seq_printf(m, ",wsize=%u", fsopt->wsize);
        if (fsopt->rsize != CEPH_MAX_READ_SIZE)
        return root;
 }
 
+#ifdef CONFIG_FS_ENCRYPTION
+static int ceph_apply_test_dummy_encryption(struct super_block *sb,
+                                           struct fs_context *fc,
+                                           struct ceph_mount_options *fsopt)
+{
+       struct ceph_fs_client *fsc = sb->s_fs_info;
+
+       if (!fscrypt_is_dummy_policy_set(&fsopt->dummy_enc_policy))
+               return 0;
+
+       /* No changing encryption context on remount. */
+       if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE &&
+           !fscrypt_is_dummy_policy_set(&fsc->fsc_dummy_enc_policy)) {
+               if (fscrypt_dummy_policies_equal(&fsopt->dummy_enc_policy,
+                                                &fsc->fsc_dummy_enc_policy))
+                       return 0;
+               errorfc(fc, "Can't set test_dummy_encryption on remount");
+               return -EINVAL;
+       }
+
+       /* Also make sure fsopt doesn't contain a conflicting value. */
+       if (fscrypt_is_dummy_policy_set(&fsc->fsc_dummy_enc_policy)) {
+               if (fscrypt_dummy_policies_equal(&fsopt->dummy_enc_policy,
+                                                &fsc->fsc_dummy_enc_policy))
+                       return 0;
+               errorfc(fc, "Conflicting test_dummy_encryption options");
+               return -EINVAL;
+       }
+
+       fsc->fsc_dummy_enc_policy = fsopt->dummy_enc_policy;
+       memset(&fsopt->dummy_enc_policy, 0, sizeof(fsopt->dummy_enc_policy));
+
+       warnfc(fc, "test_dummy_encryption mode enabled");
+       return 0;
+}
+#else
+static int ceph_apply_test_dummy_encryption(struct super_block *sb,
+                                           struct fs_context *fc,
+                                           struct ceph_mount_options *fsopt)
+{
+       return 0;
+}
+#endif
+
 /*
  * mount: join the ceph cluster, and open root directory.
  */
                                goto out;
                }
 
+               err = ceph_apply_test_dummy_encryption(fsc->sb, fc,
+                                                      fsc->mount_options);
+               if (err)
+                       goto out;
+
                dout("mount opening path '%s'\n", path);
 
                ceph_fs_debugfs_init(fsc);
 
 out:
        mutex_unlock(&fsc->client->mount_mutex);
+       ceph_fscrypt_free_dummy_policy(fsc);
        return ERR_PTR(err);
 }
 
 
 static int ceph_reconfigure_fc(struct fs_context *fc)
 {
+       int err;
        struct ceph_parse_opts_ctx *pctx = fc->fs_private;
        struct ceph_mount_options *fsopt = pctx->opts;
-       struct ceph_fs_client *fsc = ceph_sb_to_client(fc->root->d_sb);
+       struct super_block *sb = fc->root->d_sb;
+       struct ceph_fs_client *fsc = ceph_sb_to_client(sb);
+
+       err = ceph_apply_test_dummy_encryption(sb, fc, fsopt);
+       if (err)
+               return err;
 
        if (fsopt->flags & CEPH_MOUNT_OPT_ASYNC_DIROPS)
                ceph_set_mount_opt(fsc, ASYNC_DIROPS);
                pr_notice("ceph: monitor addresses recorded, but not used for reconnection");
        }
 
-       sync_filesystem(fc->root->d_sb);
+       sync_filesystem(sb);
        return 0;
 }