forked from Minki/linux
Some miscellaneous bug fixes and some final on-disk and ABI changes
for ext4 encryption which provide better security and performance. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQEcBAABCAAGBQJVRsVDAAoJEPL5WVaVDYGj/UUIAI6zLGhq3I8uQLZQC22Ew2Ph TPj6eABDuTrB/7QpAu21Dk59N70MQpsBTES6yLWWLf/eHp0gsH7gCNY/C9185vOh tQjzw18hRH2IfPftOBrjDlPGbbBD8Gu9jAmpm5kKKOtBuSVbKQ4GeN6BTECkgwlg U5EJHJJ5Ahl4MalODFreOE5ZrVC7FWGEpc1y/MquQ0qcGSGlNd35leK5FE2bfHWZ M1IJfXH5RRVPUBp26uNvzEg0TtpqkigmCJUT6gOVLfSYBw+lYEbGl4lCflrJmbgt 8EZh3Q0plsDbNhMzqSvOE4RvsOZ28oMjRNbzxkAaoz/FlatWX2hrfAoI2nqRrKg= =Unbp -----END PGP SIGNATURE----- Merge tag 'for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4 Pull ext4 fixes from Ted Ts'o: "Some miscellaneous bug fixes and some final on-disk and ABI changes for ext4 encryption which provide better security and performance" * tag 'for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: ext4: fix growing of tiny filesystems ext4: move check under lock scope to close a race. ext4: fix data corruption caused by unwritten and delayed extents ext4 crypto: remove duplicated encryption mode definitions ext4 crypto: do not select from EXT4_FS_ENCRYPTION ext4 crypto: add padding to filenames before encrypting ext4 crypto: simplify and speed up filename encryption
This commit is contained in:
commit
8663da2c09
@ -64,8 +64,8 @@ config EXT4_FS_SECURITY
|
||||
If you are not using a security module that requires using
|
||||
extended attributes for file security labels, say N.
|
||||
|
||||
config EXT4_FS_ENCRYPTION
|
||||
bool "Ext4 Encryption"
|
||||
config EXT4_ENCRYPTION
|
||||
tristate "Ext4 Encryption"
|
||||
depends on EXT4_FS
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_CBC
|
||||
@ -81,6 +81,11 @@ config EXT4_FS_ENCRYPTION
|
||||
efficient since it avoids caching the encrypted and
|
||||
decrypted pages in the page cache.
|
||||
|
||||
config EXT4_FS_ENCRYPTION
|
||||
bool
|
||||
default y
|
||||
depends on EXT4_ENCRYPTION
|
||||
|
||||
config EXT4_DEBUG
|
||||
bool "EXT4 debugging support"
|
||||
depends on EXT4_FS
|
||||
|
@ -66,6 +66,7 @@ static int ext4_fname_encrypt(struct ext4_fname_crypto_ctx *ctx,
|
||||
int res = 0;
|
||||
char iv[EXT4_CRYPTO_BLOCK_SIZE];
|
||||
struct scatterlist sg[1];
|
||||
int padding = 4 << (ctx->flags & EXT4_POLICY_FLAGS_PAD_MASK);
|
||||
char *workbuf;
|
||||
|
||||
if (iname->len <= 0 || iname->len > ctx->lim)
|
||||
@ -73,6 +74,7 @@ static int ext4_fname_encrypt(struct ext4_fname_crypto_ctx *ctx,
|
||||
|
||||
ciphertext_len = (iname->len < EXT4_CRYPTO_BLOCK_SIZE) ?
|
||||
EXT4_CRYPTO_BLOCK_SIZE : iname->len;
|
||||
ciphertext_len = ext4_fname_crypto_round_up(ciphertext_len, padding);
|
||||
ciphertext_len = (ciphertext_len > ctx->lim)
|
||||
? ctx->lim : ciphertext_len;
|
||||
|
||||
@ -101,7 +103,7 @@ static int ext4_fname_encrypt(struct ext4_fname_crypto_ctx *ctx,
|
||||
/* Create encryption request */
|
||||
sg_init_table(sg, 1);
|
||||
sg_set_page(sg, ctx->workpage, PAGE_SIZE, 0);
|
||||
ablkcipher_request_set_crypt(req, sg, sg, iname->len, iv);
|
||||
ablkcipher_request_set_crypt(req, sg, sg, ciphertext_len, iv);
|
||||
res = crypto_ablkcipher_encrypt(req);
|
||||
if (res == -EINPROGRESS || res == -EBUSY) {
|
||||
BUG_ON(req->base.data != &ecr);
|
||||
@ -198,106 +200,57 @@ static int ext4_fname_decrypt(struct ext4_fname_crypto_ctx *ctx,
|
||||
return oname->len;
|
||||
}
|
||||
|
||||
static const char *lookup_table =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
|
||||
|
||||
/**
|
||||
* ext4_fname_encode_digest() -
|
||||
*
|
||||
* Encodes the input digest using characters from the set [a-zA-Z0-9_+].
|
||||
* The encoded string is roughly 4/3 times the size of the input string.
|
||||
*/
|
||||
int ext4_fname_encode_digest(char *dst, char *src, u32 len)
|
||||
static int digest_encode(const char *src, int len, char *dst)
|
||||
{
|
||||
static const char *lookup_table =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_+";
|
||||
u32 current_chunk, num_chunks, i;
|
||||
char tmp_buf[3];
|
||||
u32 c0, c1, c2, c3;
|
||||
int i = 0, bits = 0, ac = 0;
|
||||
char *cp = dst;
|
||||
|
||||
current_chunk = 0;
|
||||
num_chunks = len/3;
|
||||
for (i = 0; i < num_chunks; i++) {
|
||||
c0 = src[3*i] & 0x3f;
|
||||
c1 = (((src[3*i]>>6)&0x3) | ((src[3*i+1] & 0xf)<<2)) & 0x3f;
|
||||
c2 = (((src[3*i+1]>>4)&0xf) | ((src[3*i+2] & 0x3)<<4)) & 0x3f;
|
||||
c3 = (src[3*i+2]>>2) & 0x3f;
|
||||
dst[4*i] = lookup_table[c0];
|
||||
dst[4*i+1] = lookup_table[c1];
|
||||
dst[4*i+2] = lookup_table[c2];
|
||||
dst[4*i+3] = lookup_table[c3];
|
||||
}
|
||||
if (i*3 < len) {
|
||||
memset(tmp_buf, 0, 3);
|
||||
memcpy(tmp_buf, &src[3*i], len-3*i);
|
||||
c0 = tmp_buf[0] & 0x3f;
|
||||
c1 = (((tmp_buf[0]>>6)&0x3) | ((tmp_buf[1] & 0xf)<<2)) & 0x3f;
|
||||
c2 = (((tmp_buf[1]>>4)&0xf) | ((tmp_buf[2] & 0x3)<<4)) & 0x3f;
|
||||
c3 = (tmp_buf[2]>>2) & 0x3f;
|
||||
dst[4*i] = lookup_table[c0];
|
||||
dst[4*i+1] = lookup_table[c1];
|
||||
dst[4*i+2] = lookup_table[c2];
|
||||
dst[4*i+3] = lookup_table[c3];
|
||||
while (i < len) {
|
||||
ac += (((unsigned char) src[i]) << bits);
|
||||
bits += 8;
|
||||
do {
|
||||
*cp++ = lookup_table[ac & 0x3f];
|
||||
ac >>= 6;
|
||||
bits -= 6;
|
||||
} while (bits >= 6);
|
||||
i++;
|
||||
}
|
||||
return (i * 4);
|
||||
if (bits)
|
||||
*cp++ = lookup_table[ac & 0x3f];
|
||||
return cp - dst;
|
||||
}
|
||||
|
||||
/**
|
||||
* ext4_fname_hash() -
|
||||
*
|
||||
* This function computes the hash of the input filename, and sets the output
|
||||
* buffer to the *encoded* digest. It returns the length of the digest as its
|
||||
* return value. Errors are returned as negative numbers. We trust the caller
|
||||
* to allocate sufficient memory to oname string.
|
||||
*/
|
||||
static int ext4_fname_hash(struct ext4_fname_crypto_ctx *ctx,
|
||||
const struct ext4_str *iname,
|
||||
struct ext4_str *oname)
|
||||
static int digest_decode(const char *src, int len, char *dst)
|
||||
{
|
||||
struct scatterlist sg;
|
||||
struct hash_desc desc = {
|
||||
.tfm = (struct crypto_hash *)ctx->htfm,
|
||||
.flags = CRYPTO_TFM_REQ_MAY_SLEEP
|
||||
};
|
||||
int res = 0;
|
||||
int i = 0, bits = 0, ac = 0;
|
||||
const char *p;
|
||||
char *cp = dst;
|
||||
|
||||
if (iname->len <= EXT4_FNAME_CRYPTO_DIGEST_SIZE) {
|
||||
res = ext4_fname_encode_digest(oname->name, iname->name,
|
||||
iname->len);
|
||||
oname->len = res;
|
||||
return res;
|
||||
while (i < len) {
|
||||
p = strchr(lookup_table, src[i]);
|
||||
if (p == NULL || src[i] == 0)
|
||||
return -2;
|
||||
ac += (p - lookup_table) << bits;
|
||||
bits += 6;
|
||||
if (bits >= 8) {
|
||||
*cp++ = ac & 0xff;
|
||||
ac >>= 8;
|
||||
bits -= 8;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
sg_init_one(&sg, iname->name, iname->len);
|
||||
res = crypto_hash_init(&desc);
|
||||
if (res) {
|
||||
printk(KERN_ERR
|
||||
"%s: Error initializing crypto hash; res = [%d]\n",
|
||||
__func__, res);
|
||||
goto out;
|
||||
}
|
||||
res = crypto_hash_update(&desc, &sg, iname->len);
|
||||
if (res) {
|
||||
printk(KERN_ERR
|
||||
"%s: Error updating crypto hash; res = [%d]\n",
|
||||
__func__, res);
|
||||
goto out;
|
||||
}
|
||||
res = crypto_hash_final(&desc,
|
||||
&oname->name[EXT4_FNAME_CRYPTO_DIGEST_SIZE]);
|
||||
if (res) {
|
||||
printk(KERN_ERR
|
||||
"%s: Error finalizing crypto hash; res = [%d]\n",
|
||||
__func__, res);
|
||||
goto out;
|
||||
}
|
||||
/* Encode the digest as a printable string--this will increase the
|
||||
* size of the digest */
|
||||
oname->name[0] = 'I';
|
||||
res = ext4_fname_encode_digest(oname->name+1,
|
||||
&oname->name[EXT4_FNAME_CRYPTO_DIGEST_SIZE],
|
||||
EXT4_FNAME_CRYPTO_DIGEST_SIZE) + 1;
|
||||
oname->len = res;
|
||||
out:
|
||||
return res;
|
||||
if (ac)
|
||||
return -1;
|
||||
return cp - dst;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -405,6 +358,7 @@ struct ext4_fname_crypto_ctx *ext4_get_fname_crypto_ctx(
|
||||
if (IS_ERR(ctx))
|
||||
return ctx;
|
||||
|
||||
ctx->flags = ei->i_crypt_policy_flags;
|
||||
if (ctx->has_valid_key) {
|
||||
if (ctx->key.mode != EXT4_ENCRYPTION_MODE_AES_256_CTS) {
|
||||
printk_once(KERN_WARNING
|
||||
@ -517,6 +471,7 @@ int ext4_fname_crypto_namelen_on_disk(struct ext4_fname_crypto_ctx *ctx,
|
||||
u32 namelen)
|
||||
{
|
||||
u32 ciphertext_len;
|
||||
int padding = 4 << (ctx->flags & EXT4_POLICY_FLAGS_PAD_MASK);
|
||||
|
||||
if (ctx == NULL)
|
||||
return -EIO;
|
||||
@ -524,6 +479,7 @@ int ext4_fname_crypto_namelen_on_disk(struct ext4_fname_crypto_ctx *ctx,
|
||||
return -EACCES;
|
||||
ciphertext_len = (namelen < EXT4_CRYPTO_BLOCK_SIZE) ?
|
||||
EXT4_CRYPTO_BLOCK_SIZE : namelen;
|
||||
ciphertext_len = ext4_fname_crypto_round_up(ciphertext_len, padding);
|
||||
ciphertext_len = (ciphertext_len > ctx->lim)
|
||||
? ctx->lim : ciphertext_len;
|
||||
return (int) ciphertext_len;
|
||||
@ -539,10 +495,13 @@ int ext4_fname_crypto_alloc_buffer(struct ext4_fname_crypto_ctx *ctx,
|
||||
u32 ilen, struct ext4_str *crypto_str)
|
||||
{
|
||||
unsigned int olen;
|
||||
int padding = 4 << (ctx->flags & EXT4_POLICY_FLAGS_PAD_MASK);
|
||||
|
||||
if (!ctx)
|
||||
return -EIO;
|
||||
olen = ext4_fname_crypto_round_up(ilen, EXT4_CRYPTO_BLOCK_SIZE);
|
||||
if (padding < EXT4_CRYPTO_BLOCK_SIZE)
|
||||
padding = EXT4_CRYPTO_BLOCK_SIZE;
|
||||
olen = ext4_fname_crypto_round_up(ilen, padding);
|
||||
crypto_str->len = olen;
|
||||
if (olen < EXT4_FNAME_CRYPTO_DIGEST_SIZE*2)
|
||||
olen = EXT4_FNAME_CRYPTO_DIGEST_SIZE*2;
|
||||
@ -571,9 +530,13 @@ void ext4_fname_crypto_free_buffer(struct ext4_str *crypto_str)
|
||||
* ext4_fname_disk_to_usr() - converts a filename from disk space to user space
|
||||
*/
|
||||
int _ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx,
|
||||
const struct ext4_str *iname,
|
||||
struct ext4_str *oname)
|
||||
struct dx_hash_info *hinfo,
|
||||
const struct ext4_str *iname,
|
||||
struct ext4_str *oname)
|
||||
{
|
||||
char buf[24];
|
||||
int ret;
|
||||
|
||||
if (ctx == NULL)
|
||||
return -EIO;
|
||||
if (iname->len < 3) {
|
||||
@ -587,18 +550,33 @@ int _ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx,
|
||||
}
|
||||
if (ctx->has_valid_key)
|
||||
return ext4_fname_decrypt(ctx, iname, oname);
|
||||
else
|
||||
return ext4_fname_hash(ctx, iname, oname);
|
||||
|
||||
if (iname->len <= EXT4_FNAME_CRYPTO_DIGEST_SIZE) {
|
||||
ret = digest_encode(iname->name, iname->len, oname->name);
|
||||
oname->len = ret;
|
||||
return ret;
|
||||
}
|
||||
if (hinfo) {
|
||||
memcpy(buf, &hinfo->hash, 4);
|
||||
memcpy(buf+4, &hinfo->minor_hash, 4);
|
||||
} else
|
||||
memset(buf, 0, 8);
|
||||
memcpy(buf + 8, iname->name + iname->len - 16, 16);
|
||||
oname->name[0] = '_';
|
||||
ret = digest_encode(buf, 24, oname->name+1);
|
||||
oname->len = ret + 1;
|
||||
return ret + 1;
|
||||
}
|
||||
|
||||
int ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx,
|
||||
struct dx_hash_info *hinfo,
|
||||
const struct ext4_dir_entry_2 *de,
|
||||
struct ext4_str *oname)
|
||||
{
|
||||
struct ext4_str iname = {.name = (unsigned char *) de->name,
|
||||
.len = de->name_len };
|
||||
|
||||
return _ext4_fname_disk_to_usr(ctx, &iname, oname);
|
||||
return _ext4_fname_disk_to_usr(ctx, hinfo, &iname, oname);
|
||||
}
|
||||
|
||||
|
||||
@ -640,10 +618,11 @@ int ext4_fname_usr_to_hash(struct ext4_fname_crypto_ctx *ctx,
|
||||
const struct qstr *iname,
|
||||
struct dx_hash_info *hinfo)
|
||||
{
|
||||
struct ext4_str tmp, tmp2;
|
||||
struct ext4_str tmp;
|
||||
int ret = 0;
|
||||
char buf[EXT4_FNAME_CRYPTO_DIGEST_SIZE+1];
|
||||
|
||||
if (!ctx || !ctx->has_valid_key ||
|
||||
if (!ctx ||
|
||||
((iname->name[0] == '.') &&
|
||||
((iname->len == 1) ||
|
||||
((iname->name[1] == '.') && (iname->len == 2))))) {
|
||||
@ -651,59 +630,90 @@ int ext4_fname_usr_to_hash(struct ext4_fname_crypto_ctx *ctx,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ctx->has_valid_key && iname->name[0] == '_') {
|
||||
if (iname->len != 33)
|
||||
return -ENOENT;
|
||||
ret = digest_decode(iname->name+1, iname->len, buf);
|
||||
if (ret != 24)
|
||||
return -ENOENT;
|
||||
memcpy(&hinfo->hash, buf, 4);
|
||||
memcpy(&hinfo->minor_hash, buf + 4, 4);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ctx->has_valid_key && iname->name[0] != '_') {
|
||||
if (iname->len > 43)
|
||||
return -ENOENT;
|
||||
ret = digest_decode(iname->name, iname->len, buf);
|
||||
ext4fs_dirhash(buf, ret, hinfo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* First encrypt the plaintext name */
|
||||
ret = ext4_fname_crypto_alloc_buffer(ctx, iname->len, &tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = ext4_fname_encrypt(ctx, iname, &tmp);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
tmp2.len = (4 * ((EXT4_FNAME_CRYPTO_DIGEST_SIZE + 2) / 3)) + 1;
|
||||
tmp2.name = kmalloc(tmp2.len + 1, GFP_KERNEL);
|
||||
if (tmp2.name == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ext4_fname_hash(ctx, &tmp, &tmp2);
|
||||
if (ret > 0)
|
||||
ext4fs_dirhash(tmp2.name, tmp2.len, hinfo);
|
||||
ext4_fname_crypto_free_buffer(&tmp2);
|
||||
out:
|
||||
ext4_fname_crypto_free_buffer(&tmp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ext4_fname_disk_to_htree() - converts a filename from disk space to htree-access string
|
||||
*/
|
||||
int ext4_fname_disk_to_hash(struct ext4_fname_crypto_ctx *ctx,
|
||||
const struct ext4_dir_entry_2 *de,
|
||||
struct dx_hash_info *hinfo)
|
||||
{
|
||||
struct ext4_str iname = {.name = (unsigned char *) de->name,
|
||||
.len = de->name_len};
|
||||
struct ext4_str tmp;
|
||||
int ret;
|
||||
|
||||
if (!ctx ||
|
||||
((iname.name[0] == '.') &&
|
||||
((iname.len == 1) ||
|
||||
((iname.name[1] == '.') && (iname.len == 2))))) {
|
||||
ext4fs_dirhash(iname.name, iname.len, hinfo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
tmp.len = (4 * ((EXT4_FNAME_CRYPTO_DIGEST_SIZE + 2) / 3)) + 1;
|
||||
tmp.name = kmalloc(tmp.len + 1, GFP_KERNEL);
|
||||
if (tmp.name == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = ext4_fname_hash(ctx, &iname, &tmp);
|
||||
if (ret > 0)
|
||||
if (ret >= 0) {
|
||||
ext4fs_dirhash(tmp.name, tmp.len, hinfo);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
ext4_fname_crypto_free_buffer(&tmp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ext4_fname_match(struct ext4_fname_crypto_ctx *ctx, struct ext4_str *cstr,
|
||||
int len, const char * const name,
|
||||
struct ext4_dir_entry_2 *de)
|
||||
{
|
||||
int ret = -ENOENT;
|
||||
int bigname = (*name == '_');
|
||||
|
||||
if (ctx->has_valid_key) {
|
||||
if (cstr->name == NULL) {
|
||||
struct qstr istr;
|
||||
|
||||
ret = ext4_fname_crypto_alloc_buffer(ctx, len, cstr);
|
||||
if (ret < 0)
|
||||
goto errout;
|
||||
istr.name = name;
|
||||
istr.len = len;
|
||||
ret = ext4_fname_encrypt(ctx, &istr, cstr);
|
||||
if (ret < 0)
|
||||
goto errout;
|
||||
}
|
||||
} else {
|
||||
if (cstr->name == NULL) {
|
||||
cstr->name = kmalloc(32, GFP_KERNEL);
|
||||
if (cstr->name == NULL)
|
||||
return -ENOMEM;
|
||||
if ((bigname && (len != 33)) ||
|
||||
(!bigname && (len > 43)))
|
||||
goto errout;
|
||||
ret = digest_decode(name+bigname, len-bigname,
|
||||
cstr->name);
|
||||
if (ret < 0) {
|
||||
ret = -ENOENT;
|
||||
goto errout;
|
||||
}
|
||||
cstr->len = ret;
|
||||
}
|
||||
if (bigname) {
|
||||
if (de->name_len < 16)
|
||||
return 0;
|
||||
ret = memcmp(de->name + de->name_len - 16,
|
||||
cstr->name + 8, 16);
|
||||
return (ret == 0) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
if (de->name_len != cstr->len)
|
||||
return 0;
|
||||
ret = memcmp(de->name, cstr->name, cstr->len);
|
||||
return (ret == 0) ? 1 : 0;
|
||||
errout:
|
||||
kfree(cstr->name);
|
||||
cstr->name = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
@ -110,6 +110,7 @@ int ext4_generate_encryption_key(struct inode *inode)
|
||||
}
|
||||
res = 0;
|
||||
|
||||
ei->i_crypt_policy_flags = ctx.flags;
|
||||
if (S_ISREG(inode->i_mode))
|
||||
crypt_key->mode = ctx.contents_encryption_mode;
|
||||
else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
|
||||
|
@ -37,6 +37,8 @@ static int ext4_is_encryption_context_consistent_with_policy(
|
||||
return 0;
|
||||
return (memcmp(ctx.master_key_descriptor, policy->master_key_descriptor,
|
||||
EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
|
||||
(ctx.flags ==
|
||||
policy->flags) &&
|
||||
(ctx.contents_encryption_mode ==
|
||||
policy->contents_encryption_mode) &&
|
||||
(ctx.filenames_encryption_mode ==
|
||||
@ -56,25 +58,25 @@ static int ext4_create_encryption_context_from_policy(
|
||||
printk(KERN_WARNING
|
||||
"%s: Invalid contents encryption mode %d\n", __func__,
|
||||
policy->contents_encryption_mode);
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!ext4_valid_filenames_enc_mode(policy->filenames_encryption_mode)) {
|
||||
printk(KERN_WARNING
|
||||
"%s: Invalid filenames encryption mode %d\n", __func__,
|
||||
policy->filenames_encryption_mode);
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
if (policy->flags & ~EXT4_POLICY_FLAGS_VALID)
|
||||
return -EINVAL;
|
||||
ctx.contents_encryption_mode = policy->contents_encryption_mode;
|
||||
ctx.filenames_encryption_mode = policy->filenames_encryption_mode;
|
||||
ctx.flags = policy->flags;
|
||||
BUILD_BUG_ON(sizeof(ctx.nonce) != EXT4_KEY_DERIVATION_NONCE_SIZE);
|
||||
get_random_bytes(ctx.nonce, EXT4_KEY_DERIVATION_NONCE_SIZE);
|
||||
|
||||
res = ext4_xattr_set(inode, EXT4_XATTR_INDEX_ENCRYPTION,
|
||||
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
|
||||
sizeof(ctx), 0);
|
||||
out:
|
||||
if (!res)
|
||||
ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
|
||||
return res;
|
||||
@ -115,6 +117,7 @@ int ext4_get_policy(struct inode *inode, struct ext4_encryption_policy *policy)
|
||||
policy->version = 0;
|
||||
policy->contents_encryption_mode = ctx.contents_encryption_mode;
|
||||
policy->filenames_encryption_mode = ctx.filenames_encryption_mode;
|
||||
policy->flags = ctx.flags;
|
||||
memcpy(&policy->master_key_descriptor, ctx.master_key_descriptor,
|
||||
EXT4_KEY_DESCRIPTOR_SIZE);
|
||||
return 0;
|
||||
@ -176,6 +179,7 @@ int ext4_inherit_context(struct inode *parent, struct inode *child)
|
||||
EXT4_ENCRYPTION_MODE_AES_256_XTS;
|
||||
ctx.filenames_encryption_mode =
|
||||
EXT4_ENCRYPTION_MODE_AES_256_CTS;
|
||||
ctx.flags = 0;
|
||||
memset(ctx.master_key_descriptor, 0x42,
|
||||
EXT4_KEY_DESCRIPTOR_SIZE);
|
||||
res = 0;
|
||||
|
@ -249,7 +249,7 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
|
||||
} else {
|
||||
/* Directory is encrypted */
|
||||
err = ext4_fname_disk_to_usr(enc_ctx,
|
||||
de, &fname_crypto_str);
|
||||
NULL, de, &fname_crypto_str);
|
||||
if (err < 0)
|
||||
goto errout;
|
||||
if (!dir_emit(ctx,
|
||||
|
@ -911,6 +911,7 @@ struct ext4_inode_info {
|
||||
|
||||
/* on-disk additional length */
|
||||
__u16 i_extra_isize;
|
||||
char i_crypt_policy_flags;
|
||||
|
||||
/* Indicate the inline data space. */
|
||||
u16 i_inline_off;
|
||||
@ -1066,12 +1067,6 @@ extern void ext4_set_bits(void *bm, int cur, int len);
|
||||
/* Metadata checksum algorithm codes */
|
||||
#define EXT4_CRC32C_CHKSUM 1
|
||||
|
||||
/* Encryption algorithms */
|
||||
#define EXT4_ENCRYPTION_MODE_INVALID 0
|
||||
#define EXT4_ENCRYPTION_MODE_AES_256_XTS 1
|
||||
#define EXT4_ENCRYPTION_MODE_AES_256_GCM 2
|
||||
#define EXT4_ENCRYPTION_MODE_AES_256_CBC 3
|
||||
|
||||
/*
|
||||
* Structure of the super block
|
||||
*/
|
||||
@ -2093,9 +2088,11 @@ u32 ext4_fname_crypto_round_up(u32 size, u32 blksize);
|
||||
int ext4_fname_crypto_alloc_buffer(struct ext4_fname_crypto_ctx *ctx,
|
||||
u32 ilen, struct ext4_str *crypto_str);
|
||||
int _ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx,
|
||||
struct dx_hash_info *hinfo,
|
||||
const struct ext4_str *iname,
|
||||
struct ext4_str *oname);
|
||||
int ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx,
|
||||
struct dx_hash_info *hinfo,
|
||||
const struct ext4_dir_entry_2 *de,
|
||||
struct ext4_str *oname);
|
||||
int ext4_fname_usr_to_disk(struct ext4_fname_crypto_ctx *ctx,
|
||||
@ -2104,11 +2101,12 @@ int ext4_fname_usr_to_disk(struct ext4_fname_crypto_ctx *ctx,
|
||||
int ext4_fname_usr_to_hash(struct ext4_fname_crypto_ctx *ctx,
|
||||
const struct qstr *iname,
|
||||
struct dx_hash_info *hinfo);
|
||||
int ext4_fname_disk_to_hash(struct ext4_fname_crypto_ctx *ctx,
|
||||
const struct ext4_dir_entry_2 *de,
|
||||
struct dx_hash_info *hinfo);
|
||||
int ext4_fname_crypto_namelen_on_disk(struct ext4_fname_crypto_ctx *ctx,
|
||||
u32 namelen);
|
||||
int ext4_fname_match(struct ext4_fname_crypto_ctx *ctx, struct ext4_str *cstr,
|
||||
int len, const char * const name,
|
||||
struct ext4_dir_entry_2 *de);
|
||||
|
||||
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
void ext4_put_fname_crypto_ctx(struct ext4_fname_crypto_ctx **ctx);
|
||||
|
@ -20,12 +20,20 @@ struct ext4_encryption_policy {
|
||||
char version;
|
||||
char contents_encryption_mode;
|
||||
char filenames_encryption_mode;
|
||||
char flags;
|
||||
char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
#define EXT4_ENCRYPTION_CONTEXT_FORMAT_V1 1
|
||||
#define EXT4_KEY_DERIVATION_NONCE_SIZE 16
|
||||
|
||||
#define EXT4_POLICY_FLAGS_PAD_4 0x00
|
||||
#define EXT4_POLICY_FLAGS_PAD_8 0x01
|
||||
#define EXT4_POLICY_FLAGS_PAD_16 0x02
|
||||
#define EXT4_POLICY_FLAGS_PAD_32 0x03
|
||||
#define EXT4_POLICY_FLAGS_PAD_MASK 0x03
|
||||
#define EXT4_POLICY_FLAGS_VALID 0x03
|
||||
|
||||
/**
|
||||
* Encryption context for inode
|
||||
*
|
||||
@ -41,7 +49,7 @@ struct ext4_encryption_context {
|
||||
char format;
|
||||
char contents_encryption_mode;
|
||||
char filenames_encryption_mode;
|
||||
char reserved;
|
||||
char flags;
|
||||
char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
|
||||
char nonce[EXT4_KEY_DERIVATION_NONCE_SIZE];
|
||||
} __attribute__((__packed__));
|
||||
@ -120,6 +128,7 @@ struct ext4_fname_crypto_ctx {
|
||||
struct crypto_hash *htfm;
|
||||
struct page *workpage;
|
||||
struct ext4_encryption_key key;
|
||||
unsigned flags : 8;
|
||||
unsigned has_valid_key : 1;
|
||||
unsigned ctfm_key_is_ready : 1;
|
||||
};
|
||||
|
@ -4927,13 +4927,6 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* currently supporting (pre)allocate mode for extent-based
|
||||
* files _only_
|
||||
*/
|
||||
if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (mode & FALLOC_FL_COLLAPSE_RANGE)
|
||||
return ext4_collapse_range(inode, offset, len);
|
||||
|
||||
@ -4955,6 +4948,14 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
|
||||
/*
|
||||
* We only support preallocation for extent-based files only
|
||||
*/
|
||||
if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) {
|
||||
ret = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(mode & FALLOC_FL_KEEP_SIZE) &&
|
||||
offset + len > i_size_read(inode)) {
|
||||
new_size = offset + len;
|
||||
|
@ -703,6 +703,14 @@ int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk,
|
||||
|
||||
BUG_ON(end < lblk);
|
||||
|
||||
if ((status & EXTENT_STATUS_DELAYED) &&
|
||||
(status & EXTENT_STATUS_WRITTEN)) {
|
||||
ext4_warning(inode->i_sb, "Inserting extent [%u/%u] as "
|
||||
" delayed and written which can potentially "
|
||||
" cause data loss.\n", lblk, len);
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
||||
newes.es_lblk = lblk;
|
||||
newes.es_len = len;
|
||||
ext4_es_store_pblock_status(&newes, pblk, status);
|
||||
|
@ -531,6 +531,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
|
||||
status = map->m_flags & EXT4_MAP_UNWRITTEN ?
|
||||
EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
|
||||
if (!(flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) &&
|
||||
!(status & EXTENT_STATUS_WRITTEN) &&
|
||||
ext4_find_delalloc_range(inode, map->m_lblk,
|
||||
map->m_lblk + map->m_len - 1))
|
||||
status |= EXTENT_STATUS_DELAYED;
|
||||
@ -635,6 +636,7 @@ found:
|
||||
status = map->m_flags & EXT4_MAP_UNWRITTEN ?
|
||||
EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
|
||||
if (!(flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) &&
|
||||
!(status & EXTENT_STATUS_WRITTEN) &&
|
||||
ext4_find_delalloc_range(inode, map->m_lblk,
|
||||
map->m_lblk + map->m_len - 1))
|
||||
status |= EXTENT_STATUS_DELAYED;
|
||||
|
@ -640,7 +640,7 @@ static struct stats dx_show_leaf(struct inode *dir,
|
||||
ext4_put_fname_crypto_ctx(&ctx);
|
||||
ctx = NULL;
|
||||
}
|
||||
res = ext4_fname_disk_to_usr(ctx, de,
|
||||
res = ext4_fname_disk_to_usr(ctx, NULL, de,
|
||||
&fname_crypto_str);
|
||||
if (res < 0) {
|
||||
printk(KERN_WARNING "Error "
|
||||
@ -653,15 +653,8 @@ static struct stats dx_show_leaf(struct inode *dir,
|
||||
name = fname_crypto_str.name;
|
||||
len = fname_crypto_str.len;
|
||||
}
|
||||
res = ext4_fname_disk_to_hash(ctx, de,
|
||||
&h);
|
||||
if (res < 0) {
|
||||
printk(KERN_WARNING "Error "
|
||||
"converting filename "
|
||||
"from disk to htree"
|
||||
"\n");
|
||||
h.hash = 0xDEADBEEF;
|
||||
}
|
||||
ext4fs_dirhash(de->name, de->name_len,
|
||||
&h);
|
||||
printk("%*.s:(E)%x.%u ", len, name,
|
||||
h.hash, (unsigned) ((char *) de
|
||||
- base));
|
||||
@ -1008,15 +1001,7 @@ static int htree_dirblock_to_tree(struct file *dir_file,
|
||||
/* silently ignore the rest of the block */
|
||||
break;
|
||||
}
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
err = ext4_fname_disk_to_hash(ctx, de, hinfo);
|
||||
if (err < 0) {
|
||||
count = err;
|
||||
goto errout;
|
||||
}
|
||||
#else
|
||||
ext4fs_dirhash(de->name, de->name_len, hinfo);
|
||||
#endif
|
||||
if ((hinfo->hash < start_hash) ||
|
||||
((hinfo->hash == start_hash) &&
|
||||
(hinfo->minor_hash < start_minor_hash)))
|
||||
@ -1032,7 +1017,7 @@ static int htree_dirblock_to_tree(struct file *dir_file,
|
||||
&tmp_str);
|
||||
} else {
|
||||
/* Directory is encrypted */
|
||||
err = ext4_fname_disk_to_usr(ctx, de,
|
||||
err = ext4_fname_disk_to_usr(ctx, hinfo, de,
|
||||
&fname_crypto_str);
|
||||
if (err < 0) {
|
||||
count = err;
|
||||
@ -1193,26 +1178,10 @@ static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de,
|
||||
int count = 0;
|
||||
char *base = (char *) de;
|
||||
struct dx_hash_info h = *hinfo;
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
struct ext4_fname_crypto_ctx *ctx = NULL;
|
||||
int err;
|
||||
|
||||
ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN);
|
||||
if (IS_ERR(ctx))
|
||||
return PTR_ERR(ctx);
|
||||
#endif
|
||||
|
||||
while ((char *) de < base + blocksize) {
|
||||
if (de->name_len && de->inode) {
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
err = ext4_fname_disk_to_hash(ctx, de, &h);
|
||||
if (err < 0) {
|
||||
ext4_put_fname_crypto_ctx(&ctx);
|
||||
return err;
|
||||
}
|
||||
#else
|
||||
ext4fs_dirhash(de->name, de->name_len, &h);
|
||||
#endif
|
||||
map_tail--;
|
||||
map_tail->hash = h.hash;
|
||||
map_tail->offs = ((char *) de - base)>>2;
|
||||
@ -1223,9 +1192,6 @@ static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de,
|
||||
/* XXX: do we need to check rec_len == 0 case? -Chris */
|
||||
de = ext4_next_entry(de, blocksize);
|
||||
}
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
ext4_put_fname_crypto_ctx(&ctx);
|
||||
#endif
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -1287,16 +1253,8 @@ static inline int ext4_match(struct ext4_fname_crypto_ctx *ctx,
|
||||
return 0;
|
||||
|
||||
#ifdef CONFIG_EXT4_FS_ENCRYPTION
|
||||
if (ctx) {
|
||||
/* Directory is encrypted */
|
||||
res = ext4_fname_disk_to_usr(ctx, de, fname_crypto_str);
|
||||
if (res < 0)
|
||||
return res;
|
||||
if (len != res)
|
||||
return 0;
|
||||
res = memcmp(name, fname_crypto_str->name, len);
|
||||
return (res == 0) ? 1 : 0;
|
||||
}
|
||||
if (ctx)
|
||||
return ext4_fname_match(ctx, fname_crypto_str, len, name, de);
|
||||
#endif
|
||||
if (len != de->name_len)
|
||||
return 0;
|
||||
@ -1324,16 +1282,6 @@ int search_dir(struct buffer_head *bh, char *search_buf, int buf_size,
|
||||
if (IS_ERR(ctx))
|
||||
return -1;
|
||||
|
||||
if (ctx != NULL) {
|
||||
/* Allocate buffer to hold maximum name length */
|
||||
res = ext4_fname_crypto_alloc_buffer(ctx, EXT4_NAME_LEN,
|
||||
&fname_crypto_str);
|
||||
if (res < 0) {
|
||||
ext4_put_fname_crypto_ctx(&ctx);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
de = (struct ext4_dir_entry_2 *)search_buf;
|
||||
dlimit = search_buf + buf_size;
|
||||
while ((char *) de < dlimit) {
|
||||
@ -1872,14 +1820,6 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
|
||||
return res;
|
||||
}
|
||||
reclen = EXT4_DIR_REC_LEN(res);
|
||||
|
||||
/* Allocate buffer to hold maximum name length */
|
||||
res = ext4_fname_crypto_alloc_buffer(ctx, EXT4_NAME_LEN,
|
||||
&fname_crypto_str);
|
||||
if (res < 0) {
|
||||
ext4_put_fname_crypto_ctx(&ctx);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
de = (struct ext4_dir_entry_2 *)buf;
|
||||
|
@ -1432,12 +1432,15 @@ static int ext4_flex_group_add(struct super_block *sb,
|
||||
goto exit;
|
||||
/*
|
||||
* We will always be modifying at least the superblock and GDT
|
||||
* block. If we are adding a group past the last current GDT block,
|
||||
* blocks. If we are adding a group past the last current GDT block,
|
||||
* we will also modify the inode and the dindirect block. If we
|
||||
* are adding a group with superblock/GDT backups we will also
|
||||
* modify each of the reserved GDT dindirect blocks.
|
||||
*/
|
||||
credit = flex_gd->count * 4 + reserved_gdb;
|
||||
credit = 3; /* sb, resize inode, resize inode dindirect */
|
||||
/* GDT blocks */
|
||||
credit += 1 + DIV_ROUND_UP(flex_gd->count, EXT4_DESC_PER_BLOCK(sb));
|
||||
credit += reserved_gdb; /* Reserved GDT dindirect blocks */
|
||||
handle = ext4_journal_start_sb(sb, EXT4_HT_RESIZE, credit);
|
||||
if (IS_ERR(handle)) {
|
||||
err = PTR_ERR(handle);
|
||||
|
@ -74,7 +74,7 @@ static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd)
|
||||
goto errout;
|
||||
}
|
||||
pstr.name = paddr;
|
||||
res = _ext4_fname_disk_to_usr(ctx, &cstr, &pstr);
|
||||
res = _ext4_fname_disk_to_usr(ctx, NULL, &cstr, &pstr);
|
||||
if (res < 0)
|
||||
goto errout;
|
||||
/* Null-terminate the name */
|
||||
|
Loading…
Reference in New Issue
Block a user