162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * The base64 encode/decode code was copied from fscrypt:
462306a36Sopenharmony_ci * Copyright (C) 2015, Google, Inc.
562306a36Sopenharmony_ci * Copyright (C) 2015, Motorola Mobility
662306a36Sopenharmony_ci * Written by Uday Savagaonkar, 2014.
762306a36Sopenharmony_ci * Modified by Jaegeuk Kim, 2015.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci#include <linux/ceph/ceph_debug.h>
1062306a36Sopenharmony_ci#include <linux/xattr.h>
1162306a36Sopenharmony_ci#include <linux/fscrypt.h>
1262306a36Sopenharmony_ci#include <linux/ceph/striper.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "super.h"
1562306a36Sopenharmony_ci#include "mds_client.h"
1662306a36Sopenharmony_ci#include "crypto.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/*
1962306a36Sopenharmony_ci * The base64url encoding used by fscrypt includes the '_' character, which may
2062306a36Sopenharmony_ci * cause problems in snapshot names (which can not start with '_').  Thus, we
2162306a36Sopenharmony_ci * used the base64 encoding defined for IMAP mailbox names (RFC 3501) instead,
2262306a36Sopenharmony_ci * which replaces '-' and '_' by '+' and ','.
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_cistatic const char base64_table[65] =
2562306a36Sopenharmony_ci	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ciint ceph_base64_encode(const u8 *src, int srclen, char *dst)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	u32 ac = 0;
3062306a36Sopenharmony_ci	int bits = 0;
3162306a36Sopenharmony_ci	int i;
3262306a36Sopenharmony_ci	char *cp = dst;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	for (i = 0; i < srclen; i++) {
3562306a36Sopenharmony_ci		ac = (ac << 8) | src[i];
3662306a36Sopenharmony_ci		bits += 8;
3762306a36Sopenharmony_ci		do {
3862306a36Sopenharmony_ci			bits -= 6;
3962306a36Sopenharmony_ci			*cp++ = base64_table[(ac >> bits) & 0x3f];
4062306a36Sopenharmony_ci		} while (bits >= 6);
4162306a36Sopenharmony_ci	}
4262306a36Sopenharmony_ci	if (bits)
4362306a36Sopenharmony_ci		*cp++ = base64_table[(ac << (6 - bits)) & 0x3f];
4462306a36Sopenharmony_ci	return cp - dst;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ciint ceph_base64_decode(const char *src, int srclen, u8 *dst)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	u32 ac = 0;
5062306a36Sopenharmony_ci	int bits = 0;
5162306a36Sopenharmony_ci	int i;
5262306a36Sopenharmony_ci	u8 *bp = dst;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	for (i = 0; i < srclen; i++) {
5562306a36Sopenharmony_ci		const char *p = strchr(base64_table, src[i]);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci		if (p == NULL || src[i] == 0)
5862306a36Sopenharmony_ci			return -1;
5962306a36Sopenharmony_ci		ac = (ac << 6) | (p - base64_table);
6062306a36Sopenharmony_ci		bits += 6;
6162306a36Sopenharmony_ci		if (bits >= 8) {
6262306a36Sopenharmony_ci			bits -= 8;
6362306a36Sopenharmony_ci			*bp++ = (u8)(ac >> bits);
6462306a36Sopenharmony_ci		}
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci	if (ac & ((1 << bits) - 1))
6762306a36Sopenharmony_ci		return -1;
6862306a36Sopenharmony_ci	return bp - dst;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic int ceph_crypt_get_context(struct inode *inode, void *ctx, size_t len)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	struct ceph_inode_info *ci = ceph_inode(inode);
7462306a36Sopenharmony_ci	struct ceph_fscrypt_auth *cfa = (struct ceph_fscrypt_auth *)ci->fscrypt_auth;
7562306a36Sopenharmony_ci	u32 ctxlen;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	/* Non existent or too short? */
7862306a36Sopenharmony_ci	if (!cfa || (ci->fscrypt_auth_len < (offsetof(struct ceph_fscrypt_auth, cfa_blob) + 1)))
7962306a36Sopenharmony_ci		return -ENOBUFS;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	/* Some format we don't recognize? */
8262306a36Sopenharmony_ci	if (le32_to_cpu(cfa->cfa_version) != CEPH_FSCRYPT_AUTH_VERSION)
8362306a36Sopenharmony_ci		return -ENOBUFS;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	ctxlen = le32_to_cpu(cfa->cfa_blob_len);
8662306a36Sopenharmony_ci	if (len < ctxlen)
8762306a36Sopenharmony_ci		return -ERANGE;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	memcpy(ctx, cfa->cfa_blob, ctxlen);
9062306a36Sopenharmony_ci	return ctxlen;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic int ceph_crypt_set_context(struct inode *inode, const void *ctx,
9462306a36Sopenharmony_ci				  size_t len, void *fs_data)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	int ret;
9762306a36Sopenharmony_ci	struct iattr attr = { };
9862306a36Sopenharmony_ci	struct ceph_iattr cia = { };
9962306a36Sopenharmony_ci	struct ceph_fscrypt_auth *cfa;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	WARN_ON_ONCE(fs_data);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	if (len > FSCRYPT_SET_CONTEXT_MAX_SIZE)
10462306a36Sopenharmony_ci		return -EINVAL;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	cfa = kzalloc(sizeof(*cfa), GFP_KERNEL);
10762306a36Sopenharmony_ci	if (!cfa)
10862306a36Sopenharmony_ci		return -ENOMEM;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	cfa->cfa_version = cpu_to_le32(CEPH_FSCRYPT_AUTH_VERSION);
11162306a36Sopenharmony_ci	cfa->cfa_blob_len = cpu_to_le32(len);
11262306a36Sopenharmony_ci	memcpy(cfa->cfa_blob, ctx, len);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	cia.fscrypt_auth = cfa;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	ret = __ceph_setattr(inode, &attr, &cia);
11762306a36Sopenharmony_ci	if (ret == 0)
11862306a36Sopenharmony_ci		inode_set_flags(inode, S_ENCRYPTED, S_ENCRYPTED);
11962306a36Sopenharmony_ci	kfree(cia.fscrypt_auth);
12062306a36Sopenharmony_ci	return ret;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic bool ceph_crypt_empty_dir(struct inode *inode)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	struct ceph_inode_info *ci = ceph_inode(inode);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	return ci->i_rsubdirs + ci->i_rfiles == 1;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic const union fscrypt_policy *ceph_get_dummy_policy(struct super_block *sb)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	return ceph_sb_to_client(sb)->fsc_dummy_enc_policy.policy;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic struct fscrypt_operations ceph_fscrypt_ops = {
13662306a36Sopenharmony_ci	.get_context		= ceph_crypt_get_context,
13762306a36Sopenharmony_ci	.set_context		= ceph_crypt_set_context,
13862306a36Sopenharmony_ci	.get_dummy_policy	= ceph_get_dummy_policy,
13962306a36Sopenharmony_ci	.empty_dir		= ceph_crypt_empty_dir,
14062306a36Sopenharmony_ci};
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_civoid ceph_fscrypt_set_ops(struct super_block *sb)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	fscrypt_set_ops(sb, &ceph_fscrypt_ops);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_civoid ceph_fscrypt_free_dummy_policy(struct ceph_fs_client *fsc)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	fscrypt_free_dummy_policy(&fsc->fsc_dummy_enc_policy);
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ciint ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode,
15362306a36Sopenharmony_ci				 struct ceph_acl_sec_ctx *as)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	int ret, ctxsize;
15662306a36Sopenharmony_ci	bool encrypted = false;
15762306a36Sopenharmony_ci	struct ceph_inode_info *ci = ceph_inode(inode);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	ret = fscrypt_prepare_new_inode(dir, inode, &encrypted);
16062306a36Sopenharmony_ci	if (ret)
16162306a36Sopenharmony_ci		return ret;
16262306a36Sopenharmony_ci	if (!encrypted)
16362306a36Sopenharmony_ci		return 0;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	as->fscrypt_auth = kzalloc(sizeof(*as->fscrypt_auth), GFP_KERNEL);
16662306a36Sopenharmony_ci	if (!as->fscrypt_auth)
16762306a36Sopenharmony_ci		return -ENOMEM;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	ctxsize = fscrypt_context_for_new_inode(as->fscrypt_auth->cfa_blob,
17062306a36Sopenharmony_ci						inode);
17162306a36Sopenharmony_ci	if (ctxsize < 0)
17262306a36Sopenharmony_ci		return ctxsize;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	as->fscrypt_auth->cfa_version = cpu_to_le32(CEPH_FSCRYPT_AUTH_VERSION);
17562306a36Sopenharmony_ci	as->fscrypt_auth->cfa_blob_len = cpu_to_le32(ctxsize);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	WARN_ON_ONCE(ci->fscrypt_auth);
17862306a36Sopenharmony_ci	kfree(ci->fscrypt_auth);
17962306a36Sopenharmony_ci	ci->fscrypt_auth_len = ceph_fscrypt_auth_len(as->fscrypt_auth);
18062306a36Sopenharmony_ci	ci->fscrypt_auth = kmemdup(as->fscrypt_auth, ci->fscrypt_auth_len,
18162306a36Sopenharmony_ci				   GFP_KERNEL);
18262306a36Sopenharmony_ci	if (!ci->fscrypt_auth)
18362306a36Sopenharmony_ci		return -ENOMEM;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	inode->i_flags |= S_ENCRYPTED;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	return 0;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_civoid ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req,
19162306a36Sopenharmony_ci				struct ceph_acl_sec_ctx *as)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	swap(req->r_fscrypt_auth, as->fscrypt_auth);
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci/*
19762306a36Sopenharmony_ci * User-created snapshots can't start with '_'.  Snapshots that start with this
19862306a36Sopenharmony_ci * character are special (hint: there aren't real snapshots) and use the
19962306a36Sopenharmony_ci * following format:
20062306a36Sopenharmony_ci *
20162306a36Sopenharmony_ci *   _<SNAPSHOT-NAME>_<INODE-NUMBER>
20262306a36Sopenharmony_ci *
20362306a36Sopenharmony_ci * where:
20462306a36Sopenharmony_ci *  - <SNAPSHOT-NAME> - the real snapshot name that may need to be decrypted,
20562306a36Sopenharmony_ci *  - <INODE-NUMBER> - the inode number (in decimal) for the actual snapshot
20662306a36Sopenharmony_ci *
20762306a36Sopenharmony_ci * This function parses these snapshot names and returns the inode
20862306a36Sopenharmony_ci * <INODE-NUMBER>.  'name_len' will also bet set with the <SNAPSHOT-NAME>
20962306a36Sopenharmony_ci * length.
21062306a36Sopenharmony_ci */
21162306a36Sopenharmony_cistatic struct inode *parse_longname(const struct inode *parent,
21262306a36Sopenharmony_ci				    const char *name, int *name_len)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	struct inode *dir = NULL;
21562306a36Sopenharmony_ci	struct ceph_vino vino = { .snap = CEPH_NOSNAP };
21662306a36Sopenharmony_ci	char *inode_number;
21762306a36Sopenharmony_ci	char *name_end;
21862306a36Sopenharmony_ci	int orig_len = *name_len;
21962306a36Sopenharmony_ci	int ret = -EIO;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	/* Skip initial '_' */
22262306a36Sopenharmony_ci	name++;
22362306a36Sopenharmony_ci	name_end = strrchr(name, '_');
22462306a36Sopenharmony_ci	if (!name_end) {
22562306a36Sopenharmony_ci		dout("Failed to parse long snapshot name: %s\n", name);
22662306a36Sopenharmony_ci		return ERR_PTR(-EIO);
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci	*name_len = (name_end - name);
22962306a36Sopenharmony_ci	if (*name_len <= 0) {
23062306a36Sopenharmony_ci		pr_err("Failed to parse long snapshot name\n");
23162306a36Sopenharmony_ci		return ERR_PTR(-EIO);
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	/* Get the inode number */
23562306a36Sopenharmony_ci	inode_number = kmemdup_nul(name_end + 1,
23662306a36Sopenharmony_ci				   orig_len - *name_len - 2,
23762306a36Sopenharmony_ci				   GFP_KERNEL);
23862306a36Sopenharmony_ci	if (!inode_number)
23962306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
24062306a36Sopenharmony_ci	ret = kstrtou64(inode_number, 10, &vino.ino);
24162306a36Sopenharmony_ci	if (ret) {
24262306a36Sopenharmony_ci		dout("Failed to parse inode number: %s\n", name);
24362306a36Sopenharmony_ci		dir = ERR_PTR(ret);
24462306a36Sopenharmony_ci		goto out;
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	/* And finally the inode */
24862306a36Sopenharmony_ci	dir = ceph_find_inode(parent->i_sb, vino);
24962306a36Sopenharmony_ci	if (!dir) {
25062306a36Sopenharmony_ci		/* This can happen if we're not mounting cephfs on the root */
25162306a36Sopenharmony_ci		dir = ceph_get_inode(parent->i_sb, vino, NULL);
25262306a36Sopenharmony_ci		if (IS_ERR(dir))
25362306a36Sopenharmony_ci			dout("Can't find inode %s (%s)\n", inode_number, name);
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ciout:
25762306a36Sopenharmony_ci	kfree(inode_number);
25862306a36Sopenharmony_ci	return dir;
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ciint ceph_encode_encrypted_dname(struct inode *parent, struct qstr *d_name,
26262306a36Sopenharmony_ci				char *buf)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	struct inode *dir = parent;
26562306a36Sopenharmony_ci	struct qstr iname;
26662306a36Sopenharmony_ci	u32 len;
26762306a36Sopenharmony_ci	int name_len;
26862306a36Sopenharmony_ci	int elen;
26962306a36Sopenharmony_ci	int ret;
27062306a36Sopenharmony_ci	u8 *cryptbuf = NULL;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	iname.name = d_name->name;
27362306a36Sopenharmony_ci	name_len = d_name->len;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	/* Handle the special case of snapshot names that start with '_' */
27662306a36Sopenharmony_ci	if ((ceph_snap(dir) == CEPH_SNAPDIR) && (name_len > 0) &&
27762306a36Sopenharmony_ci	    (iname.name[0] == '_')) {
27862306a36Sopenharmony_ci		dir = parse_longname(parent, iname.name, &name_len);
27962306a36Sopenharmony_ci		if (IS_ERR(dir))
28062306a36Sopenharmony_ci			return PTR_ERR(dir);
28162306a36Sopenharmony_ci		iname.name++; /* skip initial '_' */
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci	iname.len = name_len;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	if (!fscrypt_has_encryption_key(dir)) {
28662306a36Sopenharmony_ci		memcpy(buf, d_name->name, d_name->len);
28762306a36Sopenharmony_ci		elen = d_name->len;
28862306a36Sopenharmony_ci		goto out;
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	/*
29262306a36Sopenharmony_ci	 * Convert cleartext d_name to ciphertext. If result is longer than
29362306a36Sopenharmony_ci	 * CEPH_NOHASH_NAME_MAX, sha256 the remaining bytes
29462306a36Sopenharmony_ci	 *
29562306a36Sopenharmony_ci	 * See: fscrypt_setup_filename
29662306a36Sopenharmony_ci	 */
29762306a36Sopenharmony_ci	if (!fscrypt_fname_encrypted_size(dir, iname.len, NAME_MAX, &len)) {
29862306a36Sopenharmony_ci		elen = -ENAMETOOLONG;
29962306a36Sopenharmony_ci		goto out;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	/* Allocate a buffer appropriate to hold the result */
30362306a36Sopenharmony_ci	cryptbuf = kmalloc(len > CEPH_NOHASH_NAME_MAX ? NAME_MAX : len,
30462306a36Sopenharmony_ci			   GFP_KERNEL);
30562306a36Sopenharmony_ci	if (!cryptbuf) {
30662306a36Sopenharmony_ci		elen = -ENOMEM;
30762306a36Sopenharmony_ci		goto out;
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	ret = fscrypt_fname_encrypt(dir, &iname, cryptbuf, len);
31162306a36Sopenharmony_ci	if (ret) {
31262306a36Sopenharmony_ci		elen = ret;
31362306a36Sopenharmony_ci		goto out;
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	/* hash the end if the name is long enough */
31762306a36Sopenharmony_ci	if (len > CEPH_NOHASH_NAME_MAX) {
31862306a36Sopenharmony_ci		u8 hash[SHA256_DIGEST_SIZE];
31962306a36Sopenharmony_ci		u8 *extra = cryptbuf + CEPH_NOHASH_NAME_MAX;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci		/*
32262306a36Sopenharmony_ci		 * hash the extra bytes and overwrite crypttext beyond that
32362306a36Sopenharmony_ci		 * point with it
32462306a36Sopenharmony_ci		 */
32562306a36Sopenharmony_ci		sha256(extra, len - CEPH_NOHASH_NAME_MAX, hash);
32662306a36Sopenharmony_ci		memcpy(extra, hash, SHA256_DIGEST_SIZE);
32762306a36Sopenharmony_ci		len = CEPH_NOHASH_NAME_MAX + SHA256_DIGEST_SIZE;
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	/* base64 encode the encrypted name */
33162306a36Sopenharmony_ci	elen = ceph_base64_encode(cryptbuf, len, buf);
33262306a36Sopenharmony_ci	dout("base64-encoded ciphertext name = %.*s\n", elen, buf);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	/* To understand the 240 limit, see CEPH_NOHASH_NAME_MAX comments */
33562306a36Sopenharmony_ci	WARN_ON(elen > 240);
33662306a36Sopenharmony_ci	if ((elen > 0) && (dir != parent)) {
33762306a36Sopenharmony_ci		char tmp_buf[NAME_MAX];
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci		elen = snprintf(tmp_buf, sizeof(tmp_buf), "_%.*s_%ld",
34062306a36Sopenharmony_ci				elen, buf, dir->i_ino);
34162306a36Sopenharmony_ci		memcpy(buf, tmp_buf, elen);
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ciout:
34562306a36Sopenharmony_ci	kfree(cryptbuf);
34662306a36Sopenharmony_ci	if (dir != parent) {
34762306a36Sopenharmony_ci		if ((dir->i_state & I_NEW))
34862306a36Sopenharmony_ci			discard_new_inode(dir);
34962306a36Sopenharmony_ci		else
35062306a36Sopenharmony_ci			iput(dir);
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci	return elen;
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ciint ceph_encode_encrypted_fname(struct inode *parent, struct dentry *dentry,
35662306a36Sopenharmony_ci				char *buf)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	WARN_ON_ONCE(!fscrypt_has_encryption_key(parent));
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	return ceph_encode_encrypted_dname(parent, &dentry->d_name, buf);
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci/**
36462306a36Sopenharmony_ci * ceph_fname_to_usr - convert a filename for userland presentation
36562306a36Sopenharmony_ci * @fname: ceph_fname to be converted
36662306a36Sopenharmony_ci * @tname: temporary name buffer to use for conversion (may be NULL)
36762306a36Sopenharmony_ci * @oname: where converted name should be placed
36862306a36Sopenharmony_ci * @is_nokey: set to true if key wasn't available during conversion (may be NULL)
36962306a36Sopenharmony_ci *
37062306a36Sopenharmony_ci * Given a filename (usually from the MDS), format it for presentation to
37162306a36Sopenharmony_ci * userland. If @parent is not encrypted, just pass it back as-is.
37262306a36Sopenharmony_ci *
37362306a36Sopenharmony_ci * Otherwise, base64 decode the string, and then ask fscrypt to format it
37462306a36Sopenharmony_ci * for userland presentation.
37562306a36Sopenharmony_ci *
37662306a36Sopenharmony_ci * Returns 0 on success or negative error code on error.
37762306a36Sopenharmony_ci */
37862306a36Sopenharmony_ciint ceph_fname_to_usr(const struct ceph_fname *fname, struct fscrypt_str *tname,
37962306a36Sopenharmony_ci		      struct fscrypt_str *oname, bool *is_nokey)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	struct inode *dir = fname->dir;
38262306a36Sopenharmony_ci	struct fscrypt_str _tname = FSTR_INIT(NULL, 0);
38362306a36Sopenharmony_ci	struct fscrypt_str iname;
38462306a36Sopenharmony_ci	char *name = fname->name;
38562306a36Sopenharmony_ci	int name_len = fname->name_len;
38662306a36Sopenharmony_ci	int ret;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	/* Sanity check that the resulting name will fit in the buffer */
38962306a36Sopenharmony_ci	if (fname->name_len > NAME_MAX || fname->ctext_len > NAME_MAX)
39062306a36Sopenharmony_ci		return -EIO;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	/* Handle the special case of snapshot names that start with '_' */
39362306a36Sopenharmony_ci	if ((ceph_snap(dir) == CEPH_SNAPDIR) && (name_len > 0) &&
39462306a36Sopenharmony_ci	    (name[0] == '_')) {
39562306a36Sopenharmony_ci		dir = parse_longname(dir, name, &name_len);
39662306a36Sopenharmony_ci		if (IS_ERR(dir))
39762306a36Sopenharmony_ci			return PTR_ERR(dir);
39862306a36Sopenharmony_ci		name++; /* skip initial '_' */
39962306a36Sopenharmony_ci	}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	if (!IS_ENCRYPTED(dir)) {
40262306a36Sopenharmony_ci		oname->name = fname->name;
40362306a36Sopenharmony_ci		oname->len = fname->name_len;
40462306a36Sopenharmony_ci		ret = 0;
40562306a36Sopenharmony_ci		goto out_inode;
40662306a36Sopenharmony_ci	}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	ret = ceph_fscrypt_prepare_readdir(dir);
40962306a36Sopenharmony_ci	if (ret)
41062306a36Sopenharmony_ci		goto out_inode;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	/*
41362306a36Sopenharmony_ci	 * Use the raw dentry name as sent by the MDS instead of
41462306a36Sopenharmony_ci	 * generating a nokey name via fscrypt.
41562306a36Sopenharmony_ci	 */
41662306a36Sopenharmony_ci	if (!fscrypt_has_encryption_key(dir)) {
41762306a36Sopenharmony_ci		if (fname->no_copy)
41862306a36Sopenharmony_ci			oname->name = fname->name;
41962306a36Sopenharmony_ci		else
42062306a36Sopenharmony_ci			memcpy(oname->name, fname->name, fname->name_len);
42162306a36Sopenharmony_ci		oname->len = fname->name_len;
42262306a36Sopenharmony_ci		if (is_nokey)
42362306a36Sopenharmony_ci			*is_nokey = true;
42462306a36Sopenharmony_ci		ret = 0;
42562306a36Sopenharmony_ci		goto out_inode;
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	if (fname->ctext_len == 0) {
42962306a36Sopenharmony_ci		int declen;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci		if (!tname) {
43262306a36Sopenharmony_ci			ret = fscrypt_fname_alloc_buffer(NAME_MAX, &_tname);
43362306a36Sopenharmony_ci			if (ret)
43462306a36Sopenharmony_ci				goto out_inode;
43562306a36Sopenharmony_ci			tname = &_tname;
43662306a36Sopenharmony_ci		}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci		declen = ceph_base64_decode(name, name_len, tname->name);
43962306a36Sopenharmony_ci		if (declen <= 0) {
44062306a36Sopenharmony_ci			ret = -EIO;
44162306a36Sopenharmony_ci			goto out;
44262306a36Sopenharmony_ci		}
44362306a36Sopenharmony_ci		iname.name = tname->name;
44462306a36Sopenharmony_ci		iname.len = declen;
44562306a36Sopenharmony_ci	} else {
44662306a36Sopenharmony_ci		iname.name = fname->ctext;
44762306a36Sopenharmony_ci		iname.len = fname->ctext_len;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	ret = fscrypt_fname_disk_to_usr(dir, 0, 0, &iname, oname);
45162306a36Sopenharmony_ci	if (!ret && (dir != fname->dir)) {
45262306a36Sopenharmony_ci		char tmp_buf[CEPH_BASE64_CHARS(NAME_MAX)];
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci		name_len = snprintf(tmp_buf, sizeof(tmp_buf), "_%.*s_%ld",
45562306a36Sopenharmony_ci				    oname->len, oname->name, dir->i_ino);
45662306a36Sopenharmony_ci		memcpy(oname->name, tmp_buf, name_len);
45762306a36Sopenharmony_ci		oname->len = name_len;
45862306a36Sopenharmony_ci	}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ciout:
46162306a36Sopenharmony_ci	fscrypt_fname_free_buffer(&_tname);
46262306a36Sopenharmony_ciout_inode:
46362306a36Sopenharmony_ci	if (dir != fname->dir) {
46462306a36Sopenharmony_ci		if ((dir->i_state & I_NEW))
46562306a36Sopenharmony_ci			discard_new_inode(dir);
46662306a36Sopenharmony_ci		else
46762306a36Sopenharmony_ci			iput(dir);
46862306a36Sopenharmony_ci	}
46962306a36Sopenharmony_ci	return ret;
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci/**
47362306a36Sopenharmony_ci * ceph_fscrypt_prepare_readdir - simple __fscrypt_prepare_readdir() wrapper
47462306a36Sopenharmony_ci * @dir: directory inode for readdir prep
47562306a36Sopenharmony_ci *
47662306a36Sopenharmony_ci * Simple wrapper around __fscrypt_prepare_readdir() that will mark directory as
47762306a36Sopenharmony_ci * non-complete if this call results in having the directory unlocked.
47862306a36Sopenharmony_ci *
47962306a36Sopenharmony_ci * Returns:
48062306a36Sopenharmony_ci *     1 - if directory was locked and key is now loaded (i.e. dir is unlocked)
48162306a36Sopenharmony_ci *     0 - if directory is still locked
48262306a36Sopenharmony_ci *   < 0 - if __fscrypt_prepare_readdir() fails
48362306a36Sopenharmony_ci */
48462306a36Sopenharmony_ciint ceph_fscrypt_prepare_readdir(struct inode *dir)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	bool had_key = fscrypt_has_encryption_key(dir);
48762306a36Sopenharmony_ci	int err;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	if (!IS_ENCRYPTED(dir))
49062306a36Sopenharmony_ci		return 0;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	err = __fscrypt_prepare_readdir(dir);
49362306a36Sopenharmony_ci	if (err)
49462306a36Sopenharmony_ci		return err;
49562306a36Sopenharmony_ci	if (!had_key && fscrypt_has_encryption_key(dir)) {
49662306a36Sopenharmony_ci		/* directory just got unlocked, mark it as not complete */
49762306a36Sopenharmony_ci		ceph_dir_clear_complete(dir);
49862306a36Sopenharmony_ci		return 1;
49962306a36Sopenharmony_ci	}
50062306a36Sopenharmony_ci	return 0;
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ciint ceph_fscrypt_decrypt_block_inplace(const struct inode *inode,
50462306a36Sopenharmony_ci				  struct page *page, unsigned int len,
50562306a36Sopenharmony_ci				  unsigned int offs, u64 lblk_num)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	dout("%s: len %u offs %u blk %llu\n", __func__, len, offs, lblk_num);
50862306a36Sopenharmony_ci	return fscrypt_decrypt_block_inplace(inode, page, len, offs, lblk_num);
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ciint ceph_fscrypt_encrypt_block_inplace(const struct inode *inode,
51262306a36Sopenharmony_ci				  struct page *page, unsigned int len,
51362306a36Sopenharmony_ci				  unsigned int offs, u64 lblk_num,
51462306a36Sopenharmony_ci				  gfp_t gfp_flags)
51562306a36Sopenharmony_ci{
51662306a36Sopenharmony_ci	dout("%s: len %u offs %u blk %llu\n", __func__, len, offs, lblk_num);
51762306a36Sopenharmony_ci	return fscrypt_encrypt_block_inplace(inode, page, len, offs, lblk_num,
51862306a36Sopenharmony_ci					     gfp_flags);
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci/**
52262306a36Sopenharmony_ci * ceph_fscrypt_decrypt_pages - decrypt an array of pages
52362306a36Sopenharmony_ci * @inode: pointer to inode associated with these pages
52462306a36Sopenharmony_ci * @page: pointer to page array
52562306a36Sopenharmony_ci * @off: offset into the file that the read data starts
52662306a36Sopenharmony_ci * @len: max length to decrypt
52762306a36Sopenharmony_ci *
52862306a36Sopenharmony_ci * Decrypt an array of fscrypt'ed pages and return the amount of
52962306a36Sopenharmony_ci * data decrypted. Any data in the page prior to the start of the
53062306a36Sopenharmony_ci * first complete block in the read is ignored. Any incomplete
53162306a36Sopenharmony_ci * crypto blocks at the end of the array are ignored (and should
53262306a36Sopenharmony_ci * probably be zeroed by the caller).
53362306a36Sopenharmony_ci *
53462306a36Sopenharmony_ci * Returns the length of the decrypted data or a negative errno.
53562306a36Sopenharmony_ci */
53662306a36Sopenharmony_ciint ceph_fscrypt_decrypt_pages(struct inode *inode, struct page **page,
53762306a36Sopenharmony_ci			       u64 off, int len)
53862306a36Sopenharmony_ci{
53962306a36Sopenharmony_ci	int i, num_blocks;
54062306a36Sopenharmony_ci	u64 baseblk = off >> CEPH_FSCRYPT_BLOCK_SHIFT;
54162306a36Sopenharmony_ci	int ret = 0;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	/*
54462306a36Sopenharmony_ci	 * We can't deal with partial blocks on an encrypted file, so mask off
54562306a36Sopenharmony_ci	 * the last bit.
54662306a36Sopenharmony_ci	 */
54762306a36Sopenharmony_ci	num_blocks = ceph_fscrypt_blocks(off, len & CEPH_FSCRYPT_BLOCK_MASK);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	/* Decrypt each block */
55062306a36Sopenharmony_ci	for (i = 0; i < num_blocks; ++i) {
55162306a36Sopenharmony_ci		int blkoff = i << CEPH_FSCRYPT_BLOCK_SHIFT;
55262306a36Sopenharmony_ci		int pgidx = blkoff >> PAGE_SHIFT;
55362306a36Sopenharmony_ci		unsigned int pgoffs = offset_in_page(blkoff);
55462306a36Sopenharmony_ci		int fret;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci		fret = ceph_fscrypt_decrypt_block_inplace(inode, page[pgidx],
55762306a36Sopenharmony_ci				CEPH_FSCRYPT_BLOCK_SIZE, pgoffs,
55862306a36Sopenharmony_ci				baseblk + i);
55962306a36Sopenharmony_ci		if (fret < 0) {
56062306a36Sopenharmony_ci			if (ret == 0)
56162306a36Sopenharmony_ci				ret = fret;
56262306a36Sopenharmony_ci			break;
56362306a36Sopenharmony_ci		}
56462306a36Sopenharmony_ci		ret += CEPH_FSCRYPT_BLOCK_SIZE;
56562306a36Sopenharmony_ci	}
56662306a36Sopenharmony_ci	return ret;
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci/**
57062306a36Sopenharmony_ci * ceph_fscrypt_decrypt_extents: decrypt received extents in given buffer
57162306a36Sopenharmony_ci * @inode: inode associated with pages being decrypted
57262306a36Sopenharmony_ci * @page: pointer to page array
57362306a36Sopenharmony_ci * @off: offset into the file that the data in page[0] starts
57462306a36Sopenharmony_ci * @map: pointer to extent array
57562306a36Sopenharmony_ci * @ext_cnt: length of extent array
57662306a36Sopenharmony_ci *
57762306a36Sopenharmony_ci * Given an extent map and a page array, decrypt the received data in-place,
57862306a36Sopenharmony_ci * skipping holes. Returns the offset into buffer of end of last decrypted
57962306a36Sopenharmony_ci * block.
58062306a36Sopenharmony_ci */
58162306a36Sopenharmony_ciint ceph_fscrypt_decrypt_extents(struct inode *inode, struct page **page,
58262306a36Sopenharmony_ci				 u64 off, struct ceph_sparse_extent *map,
58362306a36Sopenharmony_ci				 u32 ext_cnt)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	int i, ret = 0;
58662306a36Sopenharmony_ci	struct ceph_inode_info *ci = ceph_inode(inode);
58762306a36Sopenharmony_ci	u64 objno, objoff;
58862306a36Sopenharmony_ci	u32 xlen;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	/* Nothing to do for empty array */
59162306a36Sopenharmony_ci	if (ext_cnt == 0) {
59262306a36Sopenharmony_ci		dout("%s: empty array, ret 0\n", __func__);
59362306a36Sopenharmony_ci		return 0;
59462306a36Sopenharmony_ci	}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	ceph_calc_file_object_mapping(&ci->i_layout, off, map[0].len,
59762306a36Sopenharmony_ci				      &objno, &objoff, &xlen);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	for (i = 0; i < ext_cnt; ++i) {
60062306a36Sopenharmony_ci		struct ceph_sparse_extent *ext = &map[i];
60162306a36Sopenharmony_ci		int pgsoff = ext->off - objoff;
60262306a36Sopenharmony_ci		int pgidx = pgsoff >> PAGE_SHIFT;
60362306a36Sopenharmony_ci		int fret;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci		if ((ext->off | ext->len) & ~CEPH_FSCRYPT_BLOCK_MASK) {
60662306a36Sopenharmony_ci			pr_warn("%s: bad encrypted sparse extent idx %d off %llx len %llx\n",
60762306a36Sopenharmony_ci				__func__, i, ext->off, ext->len);
60862306a36Sopenharmony_ci			return -EIO;
60962306a36Sopenharmony_ci		}
61062306a36Sopenharmony_ci		fret = ceph_fscrypt_decrypt_pages(inode, &page[pgidx],
61162306a36Sopenharmony_ci						 off + pgsoff, ext->len);
61262306a36Sopenharmony_ci		dout("%s: [%d] 0x%llx~0x%llx fret %d\n", __func__, i,
61362306a36Sopenharmony_ci				ext->off, ext->len, fret);
61462306a36Sopenharmony_ci		if (fret < 0) {
61562306a36Sopenharmony_ci			if (ret == 0)
61662306a36Sopenharmony_ci				ret = fret;
61762306a36Sopenharmony_ci			break;
61862306a36Sopenharmony_ci		}
61962306a36Sopenharmony_ci		ret = pgsoff + fret;
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci	dout("%s: ret %d\n", __func__, ret);
62262306a36Sopenharmony_ci	return ret;
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci/**
62662306a36Sopenharmony_ci * ceph_fscrypt_encrypt_pages - encrypt an array of pages
62762306a36Sopenharmony_ci * @inode: pointer to inode associated with these pages
62862306a36Sopenharmony_ci * @page: pointer to page array
62962306a36Sopenharmony_ci * @off: offset into the file that the data starts
63062306a36Sopenharmony_ci * @len: max length to encrypt
63162306a36Sopenharmony_ci * @gfp: gfp flags to use for allocation
63262306a36Sopenharmony_ci *
63362306a36Sopenharmony_ci * Decrypt an array of cleartext pages and return the amount of
63462306a36Sopenharmony_ci * data encrypted. Any data in the page prior to the start of the
63562306a36Sopenharmony_ci * first complete block in the read is ignored. Any incomplete
63662306a36Sopenharmony_ci * crypto blocks at the end of the array are ignored.
63762306a36Sopenharmony_ci *
63862306a36Sopenharmony_ci * Returns the length of the encrypted data or a negative errno.
63962306a36Sopenharmony_ci */
64062306a36Sopenharmony_ciint ceph_fscrypt_encrypt_pages(struct inode *inode, struct page **page, u64 off,
64162306a36Sopenharmony_ci				int len, gfp_t gfp)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	int i, num_blocks;
64462306a36Sopenharmony_ci	u64 baseblk = off >> CEPH_FSCRYPT_BLOCK_SHIFT;
64562306a36Sopenharmony_ci	int ret = 0;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	/*
64862306a36Sopenharmony_ci	 * We can't deal with partial blocks on an encrypted file, so mask off
64962306a36Sopenharmony_ci	 * the last bit.
65062306a36Sopenharmony_ci	 */
65162306a36Sopenharmony_ci	num_blocks = ceph_fscrypt_blocks(off, len & CEPH_FSCRYPT_BLOCK_MASK);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	/* Encrypt each block */
65462306a36Sopenharmony_ci	for (i = 0; i < num_blocks; ++i) {
65562306a36Sopenharmony_ci		int blkoff = i << CEPH_FSCRYPT_BLOCK_SHIFT;
65662306a36Sopenharmony_ci		int pgidx = blkoff >> PAGE_SHIFT;
65762306a36Sopenharmony_ci		unsigned int pgoffs = offset_in_page(blkoff);
65862306a36Sopenharmony_ci		int fret;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci		fret = ceph_fscrypt_encrypt_block_inplace(inode, page[pgidx],
66162306a36Sopenharmony_ci				CEPH_FSCRYPT_BLOCK_SIZE, pgoffs,
66262306a36Sopenharmony_ci				baseblk + i, gfp);
66362306a36Sopenharmony_ci		if (fret < 0) {
66462306a36Sopenharmony_ci			if (ret == 0)
66562306a36Sopenharmony_ci				ret = fret;
66662306a36Sopenharmony_ci			break;
66762306a36Sopenharmony_ci		}
66862306a36Sopenharmony_ci		ret += CEPH_FSCRYPT_BLOCK_SIZE;
66962306a36Sopenharmony_ci	}
67062306a36Sopenharmony_ci	return ret;
67162306a36Sopenharmony_ci}
672