@@ -241,6 +241,12 @@ struct fscrypt_info {
/* Back-pointer to the inode */
struct inode *ci_inode;
+ /*
+ * Back-pointer to the info pointer in the extent, for infos owned by
+ * an extent.
+ */
+ struct fscrypt_info **ci_info_ptr;
+
/* The superblock of the filesystem to which this info pertains */
struct super_block *ci_sb;
@@ -625,12 +625,17 @@ static int
fscrypt_setup_encryption_info(struct inode *inode,
const union fscrypt_policy *policy,
const u8 nonce[FSCRYPT_FILE_NONCE_SIZE],
- bool need_dirhash_key)
+ bool need_dirhash_key,
+ struct fscrypt_info **info_ptr)
{
struct fscrypt_info *crypt_info;
struct fscrypt_mode *mode;
struct fscrypt_master_key *mk = NULL;
int res;
+ bool info_for_extent = !!info_ptr;
+
+ if (!info_ptr)
+ info_ptr = &inode->i_crypt_info;
res = fscrypt_initialize(inode->i_sb);
if (res)
@@ -640,7 +645,11 @@ fscrypt_setup_encryption_info(struct inode *inode,
if (!crypt_info)
return -ENOMEM;
- crypt_info->ci_inode = inode;
+ if (fscrypt_uses_extent_encryption(inode) && info_for_extent)
+ crypt_info->ci_info_ptr = info_ptr;
+ else
+ crypt_info->ci_inode = inode;
+
crypt_info->ci_sb = inode->i_sb;
crypt_info->ci_policy = *policy;
memcpy(crypt_info->ci_nonce, nonce, FSCRYPT_FILE_NONCE_SIZE);
@@ -656,6 +665,12 @@ fscrypt_setup_encryption_info(struct inode *inode,
res = fscrypt_select_encryption_impl(crypt_info);
if (res)
goto out;
+ if (info_for_extent && !fscrypt_using_inline_encryption(crypt_info)) {
+ fscrypt_warn(inode,
+ "extent encryption requires inlinecrypt mount option");
+ res = -EINVAL;
+ goto out;
+ }
res = find_and_lock_master_key(crypt_info, &mk);
if (res)
@@ -701,7 +716,7 @@ fscrypt_setup_encryption_info(struct inode *inode,
* fscrypt_get_info(). I.e., here we publish ->i_crypt_info with a
* RELEASE barrier so that other tasks can ACQUIRE it.
*/
- if (cmpxchg_release(&inode->i_crypt_info, NULL, crypt_info) == NULL) {
+ if (cmpxchg_release(info_ptr, NULL, crypt_info) == NULL) {
/*
* We won the race and set ->i_crypt_info to our crypt_info.
* Now link it into the master key's inode list.
@@ -735,7 +750,7 @@ fscrypt_setup_encryption_info(struct inode *inode,
* %false unless the operation being performed is needed in
* order for files (or directories) to be deleted.
*
- * Set up ->i_crypt_info, if it hasn't already been done.
+ * Set up inode->i_crypt_info, if it hasn't already been done.
*
* Note: unless ->i_crypt_info is already set, this isn't %GFP_NOFS-safe. So
* generally this shouldn't be called from within a filesystem transaction.
@@ -747,8 +762,9 @@ fscrypt_setup_encryption_info(struct inode *inode,
int fscrypt_get_encryption_info(struct inode *inode, bool allow_unsupported)
{
int res;
- union fscrypt_context ctx = { 0 };
+ union fscrypt_context ctx;
union fscrypt_policy policy;
+ const u8 *nonce;
if (fscrypt_has_encryption_key(inode))
return 0;
@@ -756,7 +772,7 @@ int fscrypt_get_encryption_info(struct inode *inode, bool allow_unsupported)
if (fscrypt_uses_extent_encryption(inode)) {
/*
* Nothing will be encrypted with this info, so we can borrow
- * the parent (dir) inode's policy and use a zero nonce.
+ * the parent (dir) inode's policy and nonce.
*/
struct dentry *dentry = d_find_any_alias(inode);
struct dentry *parent_dentry = dget_parent(dentry);
@@ -789,6 +805,7 @@ int fscrypt_get_encryption_info(struct inode *inode, bool allow_unsupported)
"Unrecognized or corrupt encryption context");
return res;
}
+ nonce = fscrypt_context_nonce(&ctx);
}
if (!fscrypt_supported_policy(&policy, inode)) {
@@ -797,10 +814,10 @@ int fscrypt_get_encryption_info(struct inode *inode, bool allow_unsupported)
return -EINVAL;
}
- res = fscrypt_setup_encryption_info(inode, &policy,
- fscrypt_context_nonce(&ctx),
+ res = fscrypt_setup_encryption_info(inode, &policy, nonce,
IS_CASEFOLDED(inode) &&
- S_ISDIR(inode->i_mode));
+ S_ISDIR(inode->i_mode),
+ NULL);
if (res == -ENOPKG && allow_unsupported) /* Algorithm unavailable? */
res = 0;
@@ -834,7 +851,8 @@ int fscrypt_prepare_new_inode(struct inode *dir, struct inode *inode,
bool *encrypt_ret)
{
const union fscrypt_policy *policy;
- u8 nonce[FSCRYPT_FILE_NONCE_SIZE];
+ u8 nonce_bytes[FSCRYPT_FILE_NONCE_SIZE];
+ const u8 *nonce;
policy = fscrypt_policy_to_inherit(dir);
if (policy == NULL)
@@ -856,10 +874,17 @@ int fscrypt_prepare_new_inode(struct inode *dir, struct inode *inode,
*encrypt_ret = true;
- get_random_bytes(nonce, FSCRYPT_FILE_NONCE_SIZE);
+ if (fscrypt_uses_extent_encryption(inode)) {
+ nonce = dir->i_crypt_info->ci_nonce;
+ } else {
+ get_random_bytes(nonce_bytes, FSCRYPT_FILE_NONCE_SIZE);
+ nonce = nonce_bytes;
+ }
+
return fscrypt_setup_encryption_info(inode, policy, nonce,
IS_CASEFOLDED(dir) &&
- S_ISDIR(inode->i_mode));
+ S_ISDIR(inode->i_mode),
+ NULL);
}
EXPORT_SYMBOL_GPL(fscrypt_prepare_new_inode);
In order to notify extents when their info is part of a master key which is going away, the fscrypt_info must have a backpointer to the extent somehow. Similarly, if a fscrypt_info is owned by an extent, the info must not have a pointer to an inode -- multiple inodes may reference a extent, and the first inode to cause an extent's creation may have a lifetime much shorter than the extent, so there is no inode pointer safe to track in an extent-owned info. Therefore, this adds a new pointer for extent-owned infos to track their extent and updates fscrypt_setup_encryption_info() accordingly. Since it's simple to track the piece of extent memory pointing to the info, and for the extent to then go from such a pointer to the whole extent via container_of(), we store that. Although some sort of generic void * or some artificial fscrypt_extent embedded structure would also work, those would require additional plumbing which doesn't seem strictly required or clarifying. Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me> --- fs/crypto/fscrypt_private.h | 6 +++++ fs/crypto/keysetup.c | 49 ++++++++++++++++++++++++++++--------- 2 files changed, 43 insertions(+), 12 deletions(-)