diff options
Diffstat (limited to 'fs/ubifs/crypto.c')
-rw-r--r-- | fs/ubifs/crypto.c | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/fs/ubifs/crypto.c b/fs/ubifs/crypto.c new file mode 100644 index 000000000000..3402720f2b28 --- /dev/null +++ b/fs/ubifs/crypto.c @@ -0,0 +1,97 @@ +#include "ubifs.h" + +static int ubifs_crypt_get_context(struct inode *inode, void *ctx, size_t len) +{ + return ubifs_xattr_get(inode, UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT, + ctx, len); +} + +static int ubifs_crypt_set_context(struct inode *inode, const void *ctx, + size_t len, void *fs_data) +{ + return ubifs_xattr_set(inode, UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT, + ctx, len, 0); +} + +static bool ubifs_crypt_empty_dir(struct inode *inode) +{ + return ubifs_check_dir_empty(inode) == 0; +} + +static unsigned int ubifs_crypt_max_namelen(struct inode *inode) +{ + if (S_ISLNK(inode->i_mode)) + return UBIFS_MAX_INO_DATA; + else + return UBIFS_MAX_NLEN; +} + +static int ubifs_key_prefix(struct inode *inode, u8 **key) +{ + static char prefix[] = "ubifs:"; + + *key = prefix; + + return sizeof(prefix) - 1; +} + +int ubifs_encrypt(const struct inode *inode, struct ubifs_data_node *dn, + unsigned int in_len, unsigned int *out_len, int block) +{ + struct ubifs_info *c = inode->i_sb->s_fs_info; + void *p = &dn->data; + struct page *ret; + unsigned int pad_len = round_up(in_len, UBIFS_CIPHER_BLOCK_SIZE); + + ubifs_assert(pad_len <= *out_len); + dn->compr_size = cpu_to_le16(in_len); + + /* pad to full block cipher length */ + if (pad_len != in_len) + memset(p + in_len, 0, pad_len - in_len); + + ret = fscrypt_encrypt_page(inode, virt_to_page(&dn->data), pad_len, + offset_in_page(&dn->data), block, GFP_NOFS); + if (IS_ERR(ret)) { + ubifs_err(c, "fscrypt_encrypt_page failed: %ld", PTR_ERR(ret)); + return PTR_ERR(ret); + } + *out_len = pad_len; + + return 0; +} + +int ubifs_decrypt(const struct inode *inode, struct ubifs_data_node *dn, + unsigned int *out_len, int block) +{ + struct ubifs_info *c = inode->i_sb->s_fs_info; + int err; + unsigned int clen = le16_to_cpu(dn->compr_size); + unsigned int dlen = *out_len; + + if (clen <= 0 || clen > UBIFS_BLOCK_SIZE || clen > dlen) { + ubifs_err(c, "bad compr_size: %i", clen); + return -EINVAL; + } + + ubifs_assert(dlen <= UBIFS_BLOCK_SIZE); + err = fscrypt_decrypt_page(inode, virt_to_page(&dn->data), dlen, + offset_in_page(&dn->data), block); + if (err) { + ubifs_err(c, "fscrypt_decrypt_page failed: %i", err); + return err; + } + *out_len = clen; + + return 0; +} + +struct fscrypt_operations ubifs_crypt_operations = { + .flags = FS_CFLG_OWN_PAGES, + .get_context = ubifs_crypt_get_context, + .set_context = ubifs_crypt_set_context, + .is_encrypted = __ubifs_crypt_is_encrypted, + .empty_dir = ubifs_crypt_empty_dir, + .max_namelen = ubifs_crypt_max_namelen, + .key_prefix = ubifs_key_prefix, +}; |