From: Omar Sandoval <osandov@xxxxxxxxxxx> Creating several new subvolumes out of snapshots of another subvolume, each for a different VM's storage, is a important usecase for btrfs. We would like to give each VM a unique encryption key to use for new writes to its subvolume, so that secure deletion of the VM's data is as simple as securely deleting the key; to avoid needing multiple keys in each VM, we envision the initial subvolume being unencrypted. However, this means that the snapshot's directories would have a mix of encrypted and unencrypted files. During lookup with a key, both unencrypted and encrypted forms of the desired name must be queried. To allow this, add another FS_CFLG to allow filesystems to opt into partially encrypted directories. Signed-off-by: Omar Sandoval <osandov@xxxxxxxxxxx> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@xxxxxxxxxx> --- fs/crypto/fname.c | 17 ++++++++++++++++- include/linux/fscrypt.h | 2 ++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index 5d5c26d827fd..c5dd19c1d19e 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -389,6 +389,7 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, fname->usr_fname = iname; if (!IS_ENCRYPTED(dir) || fscrypt_is_dot_dotdot(iname)) { +unencrypted: fname->disk_name.name = (unsigned char *)iname->name; fname->disk_name.len = iname->len; return 0; @@ -424,8 +425,16 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, * user-supplied name */ - if (iname->len > FSCRYPT_NOKEY_NAME_MAX_ENCODED) + if (iname->len > FSCRYPT_NOKEY_NAME_MAX_ENCODED) { + /* + * This isn't a valid nokey name, but it could be an unencrypted + * name if the filesystem allows partially encrypted + * directories. + */ + if (dir->i_sb->s_cop->flags & FS_CFLG_ALLOW_PARTIAL) + goto unencrypted; return -ENOENT; + } fname->crypto_buf.name = kmalloc(FSCRYPT_NOKEY_NAME_MAX, GFP_KERNEL); if (fname->crypto_buf.name == NULL) @@ -436,6 +445,12 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, if (ret < (int)offsetof(struct fscrypt_nokey_name, bytes[1]) || (ret > offsetof(struct fscrypt_nokey_name, sha256) && ret != FSCRYPT_NOKEY_NAME_MAX)) { + /* Again, this could be an unencrypted name. */ + if (dir->i_sb->s_cop->flags & FS_CFLG_ALLOW_PARTIAL) { + kfree(fname->crypto_buf.name); + fname->crypto_buf.name = NULL; + goto unencrypted; + } ret = -ENOENT; goto errout; } diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 6020b738c3b2..fb48961c46f6 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -102,6 +102,8 @@ struct fscrypt_nokey_name { * pages for writes and therefore won't need the fscrypt bounce page pool. */ #define FS_CFLG_OWN_PAGES (1U << 1) +/* The filesystem allows partially encrypted directories/files. */ +#define FS_CFLG_ALLOW_PARTIAL (1U << 2) /* Crypto operations for filesystems */ struct fscrypt_operations { -- 2.35.1