Commit 8663da2c authored by Linus Torvalds's avatar Linus Torvalds

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
parents 101a6fd3 2c869b26
...@@ -64,8 +64,8 @@ config EXT4_FS_SECURITY ...@@ -64,8 +64,8 @@ config EXT4_FS_SECURITY
If you are not using a security module that requires using If you are not using a security module that requires using
extended attributes for file security labels, say N. extended attributes for file security labels, say N.
config EXT4_FS_ENCRYPTION config EXT4_ENCRYPTION
bool "Ext4 Encryption" tristate "Ext4 Encryption"
depends on EXT4_FS depends on EXT4_FS
select CRYPTO_AES select CRYPTO_AES
select CRYPTO_CBC select CRYPTO_CBC
...@@ -81,6 +81,11 @@ config EXT4_FS_ENCRYPTION ...@@ -81,6 +81,11 @@ config EXT4_FS_ENCRYPTION
efficient since it avoids caching the encrypted and efficient since it avoids caching the encrypted and
decrypted pages in the page cache. decrypted pages in the page cache.
config EXT4_FS_ENCRYPTION
bool
default y
depends on EXT4_ENCRYPTION
config EXT4_DEBUG config EXT4_DEBUG
bool "EXT4 debugging support" bool "EXT4 debugging support"
depends on EXT4_FS depends on EXT4_FS
......
...@@ -66,6 +66,7 @@ static int ext4_fname_encrypt(struct ext4_fname_crypto_ctx *ctx, ...@@ -66,6 +66,7 @@ static int ext4_fname_encrypt(struct ext4_fname_crypto_ctx *ctx,
int res = 0; int res = 0;
char iv[EXT4_CRYPTO_BLOCK_SIZE]; char iv[EXT4_CRYPTO_BLOCK_SIZE];
struct scatterlist sg[1]; struct scatterlist sg[1];
int padding = 4 << (ctx->flags & EXT4_POLICY_FLAGS_PAD_MASK);
char *workbuf; char *workbuf;
if (iname->len <= 0 || iname->len > ctx->lim) if (iname->len <= 0 || iname->len > ctx->lim)
...@@ -73,6 +74,7 @@ static int ext4_fname_encrypt(struct ext4_fname_crypto_ctx *ctx, ...@@ -73,6 +74,7 @@ static int ext4_fname_encrypt(struct ext4_fname_crypto_ctx *ctx,
ciphertext_len = (iname->len < EXT4_CRYPTO_BLOCK_SIZE) ? ciphertext_len = (iname->len < EXT4_CRYPTO_BLOCK_SIZE) ?
EXT4_CRYPTO_BLOCK_SIZE : iname->len; EXT4_CRYPTO_BLOCK_SIZE : iname->len;
ciphertext_len = ext4_fname_crypto_round_up(ciphertext_len, padding);
ciphertext_len = (ciphertext_len > ctx->lim) ciphertext_len = (ciphertext_len > ctx->lim)
? ctx->lim : ciphertext_len; ? ctx->lim : ciphertext_len;
...@@ -101,7 +103,7 @@ static int ext4_fname_encrypt(struct ext4_fname_crypto_ctx *ctx, ...@@ -101,7 +103,7 @@ static int ext4_fname_encrypt(struct ext4_fname_crypto_ctx *ctx,
/* Create encryption request */ /* Create encryption request */
sg_init_table(sg, 1); sg_init_table(sg, 1);
sg_set_page(sg, ctx->workpage, PAGE_SIZE, 0); 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); res = crypto_ablkcipher_encrypt(req);
if (res == -EINPROGRESS || res == -EBUSY) { if (res == -EINPROGRESS || res == -EBUSY) {
BUG_ON(req->base.data != &ecr); BUG_ON(req->base.data != &ecr);
...@@ -198,106 +200,57 @@ static int ext4_fname_decrypt(struct ext4_fname_crypto_ctx *ctx, ...@@ -198,106 +200,57 @@ static int ext4_fname_decrypt(struct ext4_fname_crypto_ctx *ctx,
return oname->len; return oname->len;
} }
static const char *lookup_table =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
/** /**
* ext4_fname_encode_digest() - * ext4_fname_encode_digest() -
* *
* Encodes the input digest using characters from the set [a-zA-Z0-9_+]. * 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. * 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 = int i = 0, bits = 0, ac = 0;
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_+"; char *cp = dst;
u32 current_chunk, num_chunks, i;
char tmp_buf[3]; while (i < len) {
u32 c0, c1, c2, c3; ac += (((unsigned char) src[i]) << bits);
bits += 8;
current_chunk = 0; do {
num_chunks = len/3; *cp++ = lookup_table[ac & 0x3f];
for (i = 0; i < num_chunks; i++) { ac >>= 6;
c0 = src[3*i] & 0x3f; bits -= 6;
c1 = (((src[3*i]>>6)&0x3) | ((src[3*i+1] & 0xf)<<2)) & 0x3f; } while (bits >= 6);
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];
i++; i++;
} }
return (i * 4); if (bits)
*cp++ = lookup_table[ac & 0x3f];
return cp - dst;
} }
/** static int digest_decode(const char *src, int len, char *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)
{ {
struct scatterlist sg; int i = 0, bits = 0, ac = 0;
struct hash_desc desc = { const char *p;
.tfm = (struct crypto_hash *)ctx->htfm, char *cp = dst;
.flags = CRYPTO_TFM_REQ_MAY_SLEEP
}; while (i < len) {
int res = 0; p = strchr(lookup_table, src[i]);
if (p == NULL || src[i] == 0)
if (iname->len <= EXT4_FNAME_CRYPTO_DIGEST_SIZE) { return -2;
res = ext4_fname_encode_digest(oname->name, iname->name, ac += (p - lookup_table) << bits;
iname->len); bits += 6;
oname->len = res; if (bits >= 8) {
return res; *cp++ = ac & 0xff;
} ac >>= 8;
bits -= 8;
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); i++;
if (res) {
printk(KERN_ERR
"%s: Error updating crypto hash; res = [%d]\n",
__func__, res);
goto out;
} }
res = crypto_hash_final(&desc, if (ac)
&oname->name[EXT4_FNAME_CRYPTO_DIGEST_SIZE]); return -1;
if (res) { return cp - dst;
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;
} }
/** /**
...@@ -405,6 +358,7 @@ struct ext4_fname_crypto_ctx *ext4_get_fname_crypto_ctx( ...@@ -405,6 +358,7 @@ struct ext4_fname_crypto_ctx *ext4_get_fname_crypto_ctx(
if (IS_ERR(ctx)) if (IS_ERR(ctx))
return ctx; return ctx;
ctx->flags = ei->i_crypt_policy_flags;
if (ctx->has_valid_key) { if (ctx->has_valid_key) {
if (ctx->key.mode != EXT4_ENCRYPTION_MODE_AES_256_CTS) { if (ctx->key.mode != EXT4_ENCRYPTION_MODE_AES_256_CTS) {
printk_once(KERN_WARNING printk_once(KERN_WARNING
...@@ -517,6 +471,7 @@ int ext4_fname_crypto_namelen_on_disk(struct ext4_fname_crypto_ctx *ctx, ...@@ -517,6 +471,7 @@ int ext4_fname_crypto_namelen_on_disk(struct ext4_fname_crypto_ctx *ctx,
u32 namelen) u32 namelen)
{ {
u32 ciphertext_len; u32 ciphertext_len;
int padding = 4 << (ctx->flags & EXT4_POLICY_FLAGS_PAD_MASK);
if (ctx == NULL) if (ctx == NULL)
return -EIO; return -EIO;
...@@ -524,6 +479,7 @@ int ext4_fname_crypto_namelen_on_disk(struct ext4_fname_crypto_ctx *ctx, ...@@ -524,6 +479,7 @@ int ext4_fname_crypto_namelen_on_disk(struct ext4_fname_crypto_ctx *ctx,
return -EACCES; return -EACCES;
ciphertext_len = (namelen < EXT4_CRYPTO_BLOCK_SIZE) ? ciphertext_len = (namelen < EXT4_CRYPTO_BLOCK_SIZE) ?
EXT4_CRYPTO_BLOCK_SIZE : namelen; EXT4_CRYPTO_BLOCK_SIZE : namelen;
ciphertext_len = ext4_fname_crypto_round_up(ciphertext_len, padding);
ciphertext_len = (ciphertext_len > ctx->lim) ciphertext_len = (ciphertext_len > ctx->lim)
? ctx->lim : ciphertext_len; ? ctx->lim : ciphertext_len;
return (int) ciphertext_len; return (int) ciphertext_len;
...@@ -539,10 +495,13 @@ int ext4_fname_crypto_alloc_buffer(struct ext4_fname_crypto_ctx *ctx, ...@@ -539,10 +495,13 @@ int ext4_fname_crypto_alloc_buffer(struct ext4_fname_crypto_ctx *ctx,
u32 ilen, struct ext4_str *crypto_str) u32 ilen, struct ext4_str *crypto_str)
{ {
unsigned int olen; unsigned int olen;
int padding = 4 << (ctx->flags & EXT4_POLICY_FLAGS_PAD_MASK);
if (!ctx) if (!ctx)
return -EIO; 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; crypto_str->len = olen;
if (olen < EXT4_FNAME_CRYPTO_DIGEST_SIZE*2) if (olen < EXT4_FNAME_CRYPTO_DIGEST_SIZE*2)
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) ...@@ -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 * 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, int _ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx,
struct dx_hash_info *hinfo,
const struct ext4_str *iname, const struct ext4_str *iname,
struct ext4_str *oname) struct ext4_str *oname)
{ {
char buf[24];
int ret;
if (ctx == NULL) if (ctx == NULL)
return -EIO; return -EIO;
if (iname->len < 3) { if (iname->len < 3) {
...@@ -587,18 +550,33 @@ int _ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx, ...@@ -587,18 +550,33 @@ int _ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx,
} }
if (ctx->has_valid_key) if (ctx->has_valid_key)
return ext4_fname_decrypt(ctx, iname, oname); 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, int ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx,
struct dx_hash_info *hinfo,
const struct ext4_dir_entry_2 *de, const struct ext4_dir_entry_2 *de,
struct ext4_str *oname) struct ext4_str *oname)
{ {
struct ext4_str iname = {.name = (unsigned char *) de->name, struct ext4_str iname = {.name = (unsigned char *) de->name,
.len = de->name_len }; .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, ...@@ -640,10 +618,11 @@ int ext4_fname_usr_to_hash(struct ext4_fname_crypto_ctx *ctx,
const struct qstr *iname, const struct qstr *iname,
struct dx_hash_info *hinfo) struct dx_hash_info *hinfo)
{ {
struct ext4_str tmp, tmp2; struct ext4_str tmp;
int ret = 0; int ret = 0;
char buf[EXT4_FNAME_CRYPTO_DIGEST_SIZE+1];
if (!ctx || !ctx->has_valid_key || if (!ctx ||
((iname->name[0] == '.') && ((iname->name[0] == '.') &&
((iname->len == 1) || ((iname->len == 1) ||
((iname->name[1] == '.') && (iname->len == 2))))) { ((iname->name[1] == '.') && (iname->len == 2))))) {
...@@ -651,59 +630,90 @@ int ext4_fname_usr_to_hash(struct ext4_fname_crypto_ctx *ctx, ...@@ -651,59 +630,90 @@ int ext4_fname_usr_to_hash(struct ext4_fname_crypto_ctx *ctx,
return 0; 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 */ /* First encrypt the plaintext name */
ret = ext4_fname_crypto_alloc_buffer(ctx, iname->len, &tmp); ret = ext4_fname_crypto_alloc_buffer(ctx, iname->len, &tmp);
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = ext4_fname_encrypt(ctx, iname, &tmp); ret = ext4_fname_encrypt(ctx, iname, &tmp);
if (ret < 0) if (ret >= 0) {
goto out; ext4fs_dirhash(tmp.name, tmp.len, hinfo);
ret = 0;
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); ext4_fname_crypto_free_buffer(&tmp);
return ret; return ret;
} }
/** int ext4_fname_match(struct ext4_fname_crypto_ctx *ctx, struct ext4_str *cstr,
* ext4_fname_disk_to_htree() - converts a filename from disk space to htree-access string int len, const char * const name,
*/ struct ext4_dir_entry_2 *de)
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, int ret = -ENOENT;
.len = de->name_len}; int bigname = (*name == '_');
struct ext4_str tmp;
int ret;
if (!ctx || if (ctx->has_valid_key) {
((iname.name[0] == '.') && if (cstr->name == NULL) {
((iname.len == 1) || struct qstr istr;
((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; ret = ext4_fname_crypto_alloc_buffer(ctx, len, cstr);
tmp.name = kmalloc(tmp.len + 1, GFP_KERNEL); if (ret < 0)
if (tmp.name == NULL) 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; return -ENOMEM;
if ((bigname && (len != 33)) ||
ret = ext4_fname_hash(ctx, &iname, &tmp); (!bigname && (len > 43)))
if (ret > 0) goto errout;
ext4fs_dirhash(tmp.name, tmp.len, hinfo); ret = digest_decode(name+bigname, len-bigname,
ext4_fname_crypto_free_buffer(&tmp); 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; return ret;
} }
...@@ -110,6 +110,7 @@ int ext4_generate_encryption_key(struct inode *inode) ...@@ -110,6 +110,7 @@ int ext4_generate_encryption_key(struct inode *inode)
} }
res = 0; res = 0;
ei->i_crypt_policy_flags = ctx.flags;
if (S_ISREG(inode->i_mode)) if (S_ISREG(inode->i_mode))
crypt_key->mode = ctx.contents_encryption_mode; crypt_key->mode = ctx.contents_encryption_mode;
else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_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( ...@@ -37,6 +37,8 @@ static int ext4_is_encryption_context_consistent_with_policy(
return 0; return 0;
return (memcmp(ctx.master_key_descriptor, policy->master_key_descriptor, return (memcmp(ctx.master_key_descriptor, policy->master_key_descriptor,
EXT4_KEY_DESCRIPTOR_SIZE) == 0 && EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
(ctx.flags ==
policy->flags) &&
(ctx.contents_encryption_mode == (ctx.contents_encryption_mode ==
policy->contents_encryption_mode) && policy->contents_encryption_mode) &&
(ctx.filenames_encryption_mode == (ctx.filenames_encryption_mode ==
...@@ -56,25 +58,25 @@ static int ext4_create_encryption_context_from_policy( ...@@ -56,25 +58,25 @@ static int ext4_create_encryption_context_from_policy(
printk(KERN_WARNING printk(KERN_WARNING
"%s: Invalid contents encryption mode %d\n", __func__, "%s: Invalid contents encryption mode %d\n", __func__,
policy->contents_encryption_mode); policy->contents_encryption_mode);
res = -EINVAL; return -EINVAL;
goto out;
} }
if (!ext4_valid_filenames_enc_mode(policy->filenames_encryption_mode)) { if (!ext4_valid_filenames_enc_mode(policy->filenames_encryption_mode)) {
printk(KERN_WARNING printk(KERN_WARNING
"%s: Invalid filenames encryption mode %d\n", __func__, "%s: Invalid filenames encryption mode %d\n", __func__,
policy->filenames_encryption_mode); policy->filenames_encryption_mode);
res = -EINVAL; return -EINVAL;
goto out;
} }
if (policy->flags & ~EXT4_POLICY_FLAGS_VALID)
return -EINVAL;
ctx.contents_encryption_mode = policy->contents_encryption_mode; ctx.contents_encryption_mode = policy->contents_encryption_mode;
ctx.filenames_encryption_mode = policy->filenames_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); BUILD_BUG_ON(sizeof(ctx.nonce) != EXT4_KEY_DERIVATION_NONCE_SIZE);
get_random_bytes(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, res = ext4_xattr_set(inode, EXT4_XATTR_INDEX_ENCRYPTION,
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx, EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
sizeof(ctx), 0); sizeof(ctx), 0);
out:
if (!res) if (!res)
ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT); ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
return res; return res;
...@@ -115,6 +117,7 @@ int ext4_get_policy(struct inode *inode, struct ext4_encryption_policy *policy) ...@@ -115,6 +117,7 @@ int ext4_get_policy(struct inode *inode, struct ext4_encryption_policy *policy)
policy->version = 0; policy->version = 0;
policy->contents_encryption_mode = ctx.contents_encryption_mode; policy->contents_encryption_mode = ctx.contents_encryption_mode;
policy->filenames_encryption_mode = ctx.filenames_encryption_mode; policy->filenames_encryption_mode = ctx.filenames_encryption_mode;
policy->flags = ctx.flags;
memcpy(&policy->master_key_descriptor, ctx.master_key_descriptor, memcpy(&policy->master_key_descriptor, ctx.master_key_descriptor,
EXT4_KEY_DESCRIPTOR_SIZE); EXT4_KEY_DESCRIPTOR_SIZE);
return 0; return 0;
...@@ -176,6 +179,7 @@ int ext4_inherit_context(struct inode *parent, struct inode *child) ...@@ -176,6 +179,7 @@ int ext4_inherit_context(struct inode *parent, struct inode *child)
EXT4_ENCRYPTION_MODE_AES_256_XTS; EXT4_ENCRYPTION_MODE_AES_256_XTS;
ctx.filenames_encryption_mode = ctx.filenames_encryption_mode =
EXT4_ENCRYPTION_MODE_AES_256_CTS; EXT4_ENCRYPTION_MODE_AES_256_CTS;
ctx.flags = 0;
memset(ctx.master_key_descriptor, 0x42, memset(ctx.master_key_descriptor, 0x42,
EXT4_KEY_DESCRIPTOR_SIZE); EXT4_KEY_DESCRIPTOR_SIZE);
res = 0; res = 0;
......
...@@ -249,7 +249,7 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx) ...@@ -249,7 +249,7 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
} else { } else {
/* Directory is encrypted */ /* Directory is encrypted */
err = ext4_fname_disk_to_usr(enc_ctx, err = ext4_fname_disk_to_usr(enc_ctx,
de, &fname_crypto_str); NULL, de, &fname_crypto_str);
if (err < 0) if (err < 0)
goto errout; goto errout;
if (!dir_emit(ctx, if (!dir_emit(ctx,
......
...@@ -911,6 +911,7 @@ struct ext4_inode_info { ...@@ -911,6 +911,7 @@ struct ext4_inode_info {
/* on-disk additional length */ /* on-disk additional length */
__u16 i_extra_isize; __u16 i_extra_isize;
char i_crypt_policy_flags;
/* Indicate the inline data space. */ /* Indicate the inline data space. */
u16 i_inline_off; u16 i_inline_off;
...@@ -1066,12 +1067,6 @@ extern void ext4_set_bits(void *bm, int cur, int len); ...@@ -1066,12 +1067,6 @@ extern void ext4_set_bits(void *bm, int cur, int len);
/* Metadata checksum algorithm codes */ /* Metadata checksum algorithm codes */
#define EXT4_CRC32C_CHKSUM 1 #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 * Structure of the super block
*/ */
...@@ -2093,9 +2088,11 @@ u32 ext4_fname_crypto_round_up(u32 size, u32 blksize); ...@@ -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, int ext4_fname_crypto_alloc_buffer(struct ext4_fname_crypto_ctx *ctx,
u32 ilen, struct ext4_str *crypto_str); u32 ilen, struct ext4_str *crypto_str);
int _ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx, int _ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx,
struct dx_hash_info *hinfo,
const struct ext4_str *iname, const struct ext4_str *iname,
struct ext4_str *oname); struct ext4_str *oname);
int ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx, int ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx,
struct dx_hash_info *hinfo,
const struct ext4_dir_entry_2 *de, const struct ext4_dir_entry_2 *de,
struct ext4_str *oname); struct ext4_str *oname);
int ext4_fname_usr_to_disk(struct ext4_fname_crypto_ctx *ctx, 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, ...@@ -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, int ext4_fname_usr_to_hash(struct ext4_fname_crypto_ctx *ctx,
const struct qstr *iname, const struct qstr *iname,
struct dx_hash_info *hinfo); 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, int ext4_fname_crypto_namelen_on_disk(struct ext4_fname_crypto_ctx *ctx,
u32 namelen); 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 #ifdef CONFIG_EXT4_FS_ENCRYPTION
void ext4_put_fname_crypto_ctx(struct ext4_fname_crypto_ctx **ctx); void ext4_put_fname_crypto_ctx(struct ext4_fname_crypto_ctx **ctx);
......
...@@ -20,12 +20,20 @@ struct ext4_encryption_policy { ...@@ -20,12 +20,20 @@ struct ext4_encryption_policy {
char version; char version;
char contents_encryption_mode; char contents_encryption_mode;
char filenames_encryption_mode; char filenames_encryption_mode;
char flags;
char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE]; char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
} __attribute__((__packed__)); } __attribute__((__packed__));
#define EXT4_ENCRYPTION_CONTEXT_FORMAT_V1 1 #define EXT4_ENCRYPTION_CONTEXT_FORMAT_V1 1
#define EXT4_KEY_DERIVATION_NONCE_SIZE 16 #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 * Encryption context for inode
* *
...@@ -41,7 +49,7 @@ struct ext4_encryption_context { ...@@ -41,7 +49,7 @@ struct ext4_encryption_context {
char format; char format;
char contents_encryption_mode; char contents_encryption_mode;
char filenames_encryption_mode; char filenames_encryption_mode;
char reserved; char flags;
char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE]; char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
char nonce[EXT4_KEY_DERIVATION_NONCE_SIZE]; char nonce[EXT4_KEY_DERIVATION_NONCE_SIZE];
} __attribute__((__packed__)); } __attribute__((__packed__));
...@@ -120,6 +128,7 @@ struct ext4_fname_crypto_ctx { ...@@ -120,6 +128,7 @@ struct ext4_fname_crypto_ctx {
struct crypto_hash *htfm; struct crypto_hash *htfm;
struct page *workpage; struct page *workpage;
struct ext4_encryption_key key; struct ext4_encryption_key key;
unsigned flags : 8;
unsigned has_valid_key : 1; unsigned has_valid_key : 1;
unsigned ctfm_key_is_ready : 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) ...@@ -4927,13 +4927,6 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
if (ret) if (ret)
return 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) if (mode & FALLOC_FL_COLLAPSE_RANGE)
return ext4_collapse_range(inode, offset, len); 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) ...@@ -4955,6 +4948,14 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
mutex_lock(&inode->i_mutex); 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) && if (!(mode & FALLOC_FL_KEEP_SIZE) &&
offset + len > i_size_read(inode)) { offset + len > i_size_read(inode)) {
new_size = offset + len; new_size = offset + len;
......
...@@ -703,6 +703,14 @@ int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk, ...@@ -703,6 +703,14 @@ int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk,
BUG_ON(end < 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_lblk = lblk;
newes.es_len = len; newes.es_len = len;
ext4_es_store_pblock_status(&newes, pblk, status); ext4_es_store_pblock_status(&newes, pblk, status);
......
...@@ -531,6 +531,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, ...@@ -531,6 +531,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
status = map->m_flags & EXT4_MAP_UNWRITTEN ? status = map->m_flags & EXT4_MAP_UNWRITTEN ?
EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN; EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
if (!(flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) && if (!(flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) &&
!(status & EXTENT_STATUS_WRITTEN) &&
ext4_find_delalloc_range(inode, map->m_lblk, ext4_find_delalloc_range(inode, map->m_lblk,
map->m_lblk + map->m_len - 1)) map->m_lblk + map->m_len - 1))
status |= EXTENT_STATUS_DELAYED; status |= EXTENT_STATUS_DELAYED;
...@@ -635,6 +636,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, ...@@ -635,6 +636,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
status = map->m_flags & EXT4_MAP_UNWRITTEN ? status = map->m_flags & EXT4_MAP_UNWRITTEN ?
EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN; EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
if (!(flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) && if (!(flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) &&
!(status & EXTENT_STATUS_WRITTEN) &&
ext4_find_delalloc_range(inode, map->m_lblk, ext4_find_delalloc_range(inode, map->m_lblk,
map->m_lblk + map->m_len - 1)) map->m_lblk + map->m_len - 1))
status |= EXTENT_STATUS_DELAYED; status |= EXTENT_STATUS_DELAYED;
......
...@@ -640,7 +640,7 @@ static struct stats dx_show_leaf(struct inode *dir, ...@@ -640,7 +640,7 @@ static struct stats dx_show_leaf(struct inode *dir,
ext4_put_fname_crypto_ctx(&ctx); ext4_put_fname_crypto_ctx(&ctx);
ctx = NULL; ctx = NULL;
} }
res = ext4_fname_disk_to_usr(ctx, de, res = ext4_fname_disk_to_usr(ctx, NULL, de,
&fname_crypto_str); &fname_crypto_str);
if (res < 0) { if (res < 0) {
printk(KERN_WARNING "Error " printk(KERN_WARNING "Error "
...@@ -653,15 +653,8 @@ static struct stats dx_show_leaf(struct inode *dir, ...@@ -653,15 +653,8 @@ static struct stats dx_show_leaf(struct inode *dir,
name = fname_crypto_str.name; name = fname_crypto_str.name;
len = fname_crypto_str.len; len = fname_crypto_str.len;
} }
res = ext4_fname_disk_to_hash(ctx, de, ext4fs_dirhash(de->name, de->name_len,
&h); &h);
if (res < 0) {
printk(KERN_WARNING "Error "
"converting filename "
"from disk to htree"
"\n");
h.hash = 0xDEADBEEF;
}
printk("%*.s:(E)%x.%u ", len, name, printk("%*.s:(E)%x.%u ", len, name,
h.hash, (unsigned) ((char *) de h.hash, (unsigned) ((char *) de
- base)); - base));
...@@ -1008,15 +1001,7 @@ static int htree_dirblock_to_tree(struct file *dir_file, ...@@ -1008,15 +1001,7 @@ static int htree_dirblock_to_tree(struct file *dir_file,
/* silently ignore the rest of the block */ /* silently ignore the rest of the block */
break; 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); ext4fs_dirhash(de->name, de->name_len, hinfo);
#endif
if ((hinfo->hash < start_hash) || if ((hinfo->hash < start_hash) ||
((hinfo->hash == start_hash) && ((hinfo->hash == start_hash) &&
(hinfo->minor_hash < start_minor_hash))) (hinfo->minor_hash < start_minor_hash)))
...@@ -1032,7 +1017,7 @@ static int htree_dirblock_to_tree(struct file *dir_file, ...@@ -1032,7 +1017,7 @@ static int htree_dirblock_to_tree(struct file *dir_file,
&tmp_str); &tmp_str);
} else { } else {
/* Directory is encrypted */ /* Directory is encrypted */
err = ext4_fname_disk_to_usr(ctx, de, err = ext4_fname_disk_to_usr(ctx, hinfo, de,
&fname_crypto_str); &fname_crypto_str);
if (err < 0) { if (err < 0) {
count = err; count = err;
...@@ -1193,26 +1178,10 @@ static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de, ...@@ -1193,26 +1178,10 @@ static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de,
int count = 0; int count = 0;
char *base = (char *) de; char *base = (char *) de;
struct dx_hash_info h = *hinfo; 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) { while ((char *) de < base + blocksize) {
if (de->name_len && de->inode) { 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); ext4fs_dirhash(de->name, de->name_len, &h);
#endif
map_tail--; map_tail--;
map_tail->hash = h.hash; map_tail->hash = h.hash;
map_tail->offs = ((char *) de - base)>>2; 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, ...@@ -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 */ /* XXX: do we need to check rec_len == 0 case? -Chris */
de = ext4_next_entry(de, blocksize); de = ext4_next_entry(de, blocksize);
} }
#ifdef CONFIG_EXT4_FS_ENCRYPTION
ext4_put_fname_crypto_ctx(&ctx);
#endif
return count; return count;
} }
...@@ -1287,16 +1253,8 @@ static inline int ext4_match(struct ext4_fname_crypto_ctx *ctx, ...@@ -1287,16 +1253,8 @@ static inline int ext4_match(struct ext4_fname_crypto_ctx *ctx,
return 0; return 0;
#ifdef CONFIG_EXT4_FS_ENCRYPTION #ifdef CONFIG_EXT4_FS_ENCRYPTION
if (ctx) { if (ctx)
/* Directory is encrypted */ return ext4_fname_match(ctx, fname_crypto_str, len, name, de);
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;
}
#endif #endif
if (len != de->name_len) if (len != de->name_len)
return 0; return 0;
...@@ -1324,16 +1282,6 @@ int search_dir(struct buffer_head *bh, char *search_buf, int buf_size, ...@@ -1324,16 +1282,6 @@ int search_dir(struct buffer_head *bh, char *search_buf, int buf_size,
if (IS_ERR(ctx)) if (IS_ERR(ctx))
return -1; 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; de = (struct ext4_dir_entry_2 *)search_buf;
dlimit = search_buf + buf_size; dlimit = search_buf + buf_size;
while ((char *) de < dlimit) { while ((char *) de < dlimit) {
...@@ -1872,14 +1820,6 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, ...@@ -1872,14 +1820,6 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
return res; return res;
} }
reclen = EXT4_DIR_REC_LEN(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; de = (struct ext4_dir_entry_2 *)buf;
......
...@@ -1432,12 +1432,15 @@ static int ext4_flex_group_add(struct super_block *sb, ...@@ -1432,12 +1432,15 @@ static int ext4_flex_group_add(struct super_block *sb,
goto exit; goto exit;
/* /*
* We will always be modifying at least the superblock and GDT * 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 * we will also modify the inode and the dindirect block. If we
* are adding a group with superblock/GDT backups we will also * are adding a group with superblock/GDT backups we will also
* modify each of the reserved GDT dindirect blocks. * 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); handle = ext4_journal_start_sb(sb, EXT4_HT_RESIZE, credit);
if (IS_ERR(handle)) { if (IS_ERR(handle)) {
err = PTR_ERR(handle); err = PTR_ERR(handle);
......
...@@ -74,7 +74,7 @@ static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd) ...@@ -74,7 +74,7 @@ static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd)
goto errout; goto errout;
} }
pstr.name = paddr; 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) if (res < 0)
goto errout; goto errout;
/* Null-terminate the name */ /* Null-terminate the name */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment