int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk,
sector_t pblk, unsigned int len)
{
- struct fscrypt_ctx *ctx;
+ struct fscrypt_ctx *ctx = NULL;
struct page *ciphertext_page = NULL;
struct bio *bio;
int ret, err = 0;
BUG_ON(inode->i_sb->s_blocksize != PAGE_SIZE);
- ctx = fscrypt_get_ctx(inode, GFP_NOFS);
- if (IS_ERR(ctx))
- return PTR_ERR(ctx);
+ if (fscrypt_disk_encrypted(inode)) {
+ ciphertext_page = fscrypt_alloc_bounce_page(NULL, GFP_NOWAIT);
+ if (!ciphertext_page || IS_ERR(ciphertext_page)) {
+ err = PTR_ERR(ciphertext_page);
+ goto errout;
+ }
+
+ memset(page_address(ciphertext_page), 0, PAGE_SIZE);
+ ciphertext_page->mapping = inode->i_mapping;
+ } else {
+ ctx = fscrypt_get_ctx(inode, GFP_NOFS);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
- ciphertext_page = fscrypt_alloc_bounce_page(ctx, GFP_NOWAIT);
- if (IS_ERR(ciphertext_page)) {
- err = PTR_ERR(ciphertext_page);
- goto errout;
+ ciphertext_page = fscrypt_alloc_bounce_page(ctx, GFP_NOWAIT);
+ if (IS_ERR(ciphertext_page)) {
+ err = PTR_ERR(ciphertext_page);
+ goto errout;
+ }
}
while (len--) {
- err = fscrypt_do_page_crypto(inode, FS_ENCRYPT, lblk,
- ZERO_PAGE(0), ciphertext_page,
- PAGE_SIZE, 0, GFP_NOFS);
- if (err)
- goto errout;
+ if (ctx) {
+ err = fscrypt_do_page_crypto(inode, FS_ENCRYPT, lblk,
+ ZERO_PAGE(0), ciphertext_page,
+ PAGE_SIZE, 0, GFP_NOFS);
+ if (err)
+ goto errout;
+ }
bio = bio_alloc(GFP_NOWAIT, 1);
if (!bio) {
err = -EIO;
goto errout;
}
+#ifdef CONFIG_CRYPTO_DISKCIPHER
+ if (fscrypt_has_encryption_key(inode))
+ fscrypt_set_bio(inode, bio);
+#endif
err = submit_bio_wait(bio);
if (err == 0 && bio->bi_status)
err = -EIO;
}
err = 0;
errout:
- fscrypt_release_ctx(ctx);
+ if (!ctx && ciphertext_page)
+ fscrypt_free_bounce_page(ciphertext_page);
+ else
+ fscrypt_release_ctx(ctx);
return err;
}
EXPORT_SYMBOL(fscrypt_zeroout_range);
+
+void fscrypt_set_bio(const struct inode *inode, struct bio *bio)
+{
+#ifdef CONFIG_CRYPTO_DISKCIPHER
+ if (inode->i_crypt_info->ci_dtfm)
+ crypto_diskcipher_set(bio, inode->i_crypt_info->ci_dtfm);
+#else
+ return;
+#endif
+}
if (ci == NULL)
return ERR_PTR(-ENOKEY);
+ if (fscrypt_disk_encrypted(inode))
+ return NULL;
+
/*
* We first try getting the ctx from a free list because in
* the common case the ctx will have an allocated and
struct page *fscrypt_alloc_bounce_page(struct fscrypt_ctx *ctx,
gfp_t gfp_flags)
{
- ctx->w.bounce_page = mempool_alloc(fscrypt_bounce_page_pool, gfp_flags);
- if (ctx->w.bounce_page == NULL)
+ void *pool = mempool_alloc(fscrypt_bounce_page_pool, gfp_flags);
+
+ if (pool == NULL)
return ERR_PTR(-ENOMEM);
- ctx->flags |= FS_CTX_HAS_BOUNCE_BUFFER_FL;
- return ctx->w.bounce_page;
+
+ if (ctx) {
+ ctx->w.bounce_page = pool;
+ ctx->flags |= FS_CTX_HAS_BOUNCE_BUFFER_FL;
+ }
+
+ return pool;
+}
+
+void fscrypt_free_bounce_page(void *pool)
+{
+ mempool_free(pool, fscrypt_bounce_page_pool);
}
/**
struct page *ciphertext_page = page;
int err;
+ if (fscrypt_disk_encrypted(inode))
+ return NULL;
+
if (inode->i_sb->s_cop->flags & FS_CFLG_OWN_PAGES) {
/* with inplace-encryption we just encrypt the page */
err = fscrypt_do_page_crypto(inode, FS_ENCRYPT, lblk_num, page,
!(inode->i_sb->s_cop->flags & FS_CFLG_OWN_PAGES)))
return -EINVAL;
+ if (fscrypt_disk_encrypted(page->mapping->host))
+ return 0;
return fscrypt_do_page_crypto(inode, FS_DECRYPT, lblk_num, page, page,
len, offs, GFP_NOFS);
}
#define __FS_HAS_ENCRYPTION 1
#include <linux/fscrypt.h>
#include <crypto/hash.h>
+#include <crypto/diskcipher.h>
/* Encryption parameters */
#define FS_IV_SIZE 16
u8 ci_data_mode;
u8 ci_filename_mode;
u8 ci_flags;
+#ifdef CONFIG_CRYPTO_DISKCIPHER
+ struct crypto_diskcipher *ci_dtfm;
+#endif
struct crypto_skcipher *ci_ctfm;
struct crypto_cipher *ci_essiv_tfm;
u8 ci_master_key[FS_KEY_DESCRIPTOR_SIZE];
#define fscrypt_err(sb, fmt, ...) \
fscrypt_msg(sb, KERN_ERR, fmt, ##__VA_ARGS__)
+extern void fscrypt_free_bounce_page(void *pool);
+
/* fname.c */
extern int fname_encrypt(struct inode *inode, const struct qstr *iname,
u8 *out, unsigned int olen);
/* keyinfo.c */
extern void __exit fscrypt_essiv_cleanup(void);
+static inline int fscrypt_disk_encrypted(const struct inode *inode)
+{
+#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
+#if IS_ENABLED(CONFIG_CRYPTO_DISKCIPHER)
+ if (inode && inode->i_crypt_info)
+ return (inode->i_crypt_info->ci_dtfm != NULL);
+#endif
+#endif
+ return 0;
+}
#endif /* _FSCRYPT_PRIVATE_H */
if (!ci)
return;
+#if defined(CONFIG_CRYPTO_DISKCIPHER)
+ if (ci->ci_dtfm)
+ crypto_free_diskcipher(ci->ci_dtfm);
+#endif
crypto_free_skcipher(ci->ci_ctfm);
crypto_free_cipher(ci->ci_essiv_tfm);
kmem_cache_free(fscrypt_info_cachep, ci);
crypt_info->ci_filename_mode = ctx.filenames_encryption_mode;
crypt_info->ci_ctfm = NULL;
crypt_info->ci_essiv_tfm = NULL;
+#if defined(CONFIG_CRYPTO_DISKCIPHER)
+ crypt_info->ci_dtfm = NULL;
+#endif
memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor,
sizeof(crypt_info->ci_master_key));
} else if (res) {
goto out;
}
+#if defined(CONFIG_CRYPTO_DISKCIPHER)
+ if (S_ISREG(inode->i_mode)) {
+ /* try discipher first */
+ crypt_info->ci_dtfm = crypto_alloc_diskcipher(cipher_str, 0, 0, 1);
+ if (crypt_info->ci_dtfm && !IS_ERR(crypt_info->ci_dtfm)) {
+ res = crypto_diskcipher_setkey(crypt_info->ci_dtfm,
+ raw_key, keysize, 0);
+ if (!res) {
+ if (cmpxchg(&inode->i_crypt_info, NULL, crypt_info) == NULL)
+ crypt_info = NULL;
+ pr_debug("%s: (inode %lu) uses diskcipher tfm\n",
+ __func__, inode->i_ino);
+ goto out;
+ } else {
+ pr_warn("%s: error %d fails to set diskciher key\n",
+ __func__, res);
+ crypto_free_diskcipher(crypt_info->ci_dtfm);
+ }
+ }
+ /* clear diskcipher. use skcipher */
+ pr_warn("%s: (inode %lu) fails to get diskcipher (%s, %d)\n",
+ __func__, inode->i_ino, cipher_str, res);
+ crypt_info->ci_dtfm = NULL;
+ }
+#endif
ctfm = crypto_alloc_skcipher(cipher_str, 0, 0);
if (IS_ERR(ctfm)) {
res = PTR_ERR(ctfm);
#include <linux/uio.h>
#include <linux/atomic.h>
#include <linux/prefetch.h>
+#include <linux/fscrypt.h>
/*
* How many user pages to map in one call to get_user_pages(). This determines
dio->refcount++;
spin_unlock_irqrestore(&dio->bio_lock, flags);
+#if defined(CONFIG_EXT4_FS_ENCRYPTION) && defined(CONFIG_CRYPTO_DISKCIPHER)
+ if (dio->inode && fscrypt_has_encryption_key(dio->inode))
+ fscrypt_set_bio(dio->inode, bio);
+#endif
if (dio->is_async && dio->op == REQ_OP_READ && dio->should_dirty)
bio_set_pages_dirty(bio);
ssize_t ret;
int rw = iov_iter_rw(iter);
-#ifdef CONFIG_EXT4_FS_ENCRYPTION
+#if defined(CONFIG_EXT4_FS_ENCRYPTION) && !defined(CONFIG_CRYPTO_DISKCIPHER)
if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode))
return 0;
+#elif defined(CONFIG_CRYPTO_DISKCIPHER)
+ if (ext4_encrypted_inode(inode) && !fscrypt_has_encryption_key(inode))
+ return 0;
#endif
/*
int io_op_flags = io->io_wbc->sync_mode == WB_SYNC_ALL ?
REQ_SYNC : 0;
io->io_bio->bi_write_hint = io->io_end->inode->i_write_hint;
+ if (bio->bi_opf & REQ_CRYPT)
+ io_op_flags |= (REQ_CRYPT | REQ_AUX_PRIV);
bio_set_op_attrs(io->io_bio, REQ_OP_WRITE, io_op_flags);
submit_bio(io->io_bio);
}
ret = io_submit_init_bio(io, bh);
if (ret)
return ret;
+#if defined(CONFIG_EXT4_FS_ENCRYPTION) && defined(CONFIG_CRYPTO_DISKCIPHER)
+ if (ext4_encrypted_inode(inode) &&
+ S_ISREG(inode->i_mode) && fscrypt_has_encryption_key(inode))
+ fscrypt_set_bio(inode, io->io_bio);
+#endif
io->io_bio->bi_write_hint = inode->i_write_hint;
}
ret = bio_add_page(io->io_bio, page, bh->b_size, bh_offset(bh));
static inline bool ext4_bio_encrypted(struct bio *bio)
{
#ifdef CONFIG_EXT4_FS_ENCRYPTION
+ /* REQ_CRYPT is used for diskcipher */
+ if (bio->bi_opf & REQ_CRYPT)
+ return false;
+
return unlikely(bio->bi_private != NULL);
#else
return false;
bio->bi_end_io = mpage_end_io;
bio->bi_private = ctx;
bio_set_op_attrs(bio, REQ_OP_READ, 0);
+#if defined(CONFIG_EXT4_FS_ENCRYPTION) && defined(CONFIG_CRYPTO_DISKCIPHER)
+ if (ext4_encrypted_inode(inode) &&
+ S_ISREG(inode->i_mode) && fscrypt_has_encryption_key(inode))
+ fscrypt_set_bio(inode, bio);
+#endif
}
length = first_hole << blkbits;
#define FS_CRYPTO_BLOCK_SIZE 16
+#ifndef __FS_HAS_ENCRYPTION
+#define __FS_HAS_ENCRYPTION (IS_ENABLED(CONFIG_EXT4_FS_ENCRYPTION) || \
+ IS_ENABLED(CONFIG_F2FS_FS_ENCRYPTION) || \
+ IS_ENABLED(CONFIG_UBIFS_FS_ENCRYPTION))
+#endif
+
struct fscrypt_ctx;
+
struct fscrypt_info;
struct fscrypt_str {
return -EOPNOTSUPP;
}
+static inline void fscrypt_set_bio(const struct inode *inode, struct bio *bio)
+{
+ return;
+}
+
static inline int __fscrypt_prepare_symlink(struct inode *dir,
unsigned int len,
unsigned int max_len,
extern void fscrypt_pullback_bio_page(struct page **, bool);
extern int fscrypt_zeroout_range(const struct inode *, pgoff_t, sector_t,
unsigned int);
-
+void fscrypt_set_bio(const struct inode *inode, struct bio *bio);
/* hooks.c */
extern int fscrypt_file_open(struct inode *inode, struct file *filp);
extern int __fscrypt_prepare_link(struct inode *inode, struct inode *dir);