Skip to content

Commit 77cdb7e

Browse files
jtlaytonidryomov
authored andcommitted
ceph: add infrastructure for file encryption and decryption
...and allow test_dummy_encryption to bypass content encryption if mounted with test_dummy_encryption=clear. [ xiubli: remove test_dummy_encryption=clear support per Ilya ] Signed-off-by: Jeff Layton <jlayton@kernel.org> Reviewed-by: Xiubo Li <xiubli@redhat.com> Reviewed-and-tested-by: Luís Henriques <lhenriques@suse.de> Reviewed-by: Milind Changire <mchangir@redhat.com> Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
1 parent 0d91f0a commit 77cdb7e

2 files changed

Lines changed: 251 additions & 0 deletions

File tree

fs/ceph/crypto.c

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/ceph/ceph_debug.h>
1010
#include <linux/xattr.h>
1111
#include <linux/fscrypt.h>
12+
#include <linux/ceph/striper.h>
1213

1314
#include "super.h"
1415
#include "mds_client.h"
@@ -365,3 +366,173 @@ int ceph_fscrypt_prepare_readdir(struct inode *dir)
365366
}
366367
return 0;
367368
}
369+
370+
int ceph_fscrypt_decrypt_block_inplace(const struct inode *inode,
371+
struct page *page, unsigned int len,
372+
unsigned int offs, u64 lblk_num)
373+
{
374+
dout("%s: len %u offs %u blk %llu\n", __func__, len, offs, lblk_num);
375+
return fscrypt_decrypt_block_inplace(inode, page, len, offs, lblk_num);
376+
}
377+
378+
int ceph_fscrypt_encrypt_block_inplace(const struct inode *inode,
379+
struct page *page, unsigned int len,
380+
unsigned int offs, u64 lblk_num,
381+
gfp_t gfp_flags)
382+
{
383+
dout("%s: len %u offs %u blk %llu\n", __func__, len, offs, lblk_num);
384+
return fscrypt_encrypt_block_inplace(inode, page, len, offs, lblk_num,
385+
gfp_flags);
386+
}
387+
388+
/**
389+
* ceph_fscrypt_decrypt_pages - decrypt an array of pages
390+
* @inode: pointer to inode associated with these pages
391+
* @page: pointer to page array
392+
* @off: offset into the file that the read data starts
393+
* @len: max length to decrypt
394+
*
395+
* Decrypt an array of fscrypt'ed pages and return the amount of
396+
* data decrypted. Any data in the page prior to the start of the
397+
* first complete block in the read is ignored. Any incomplete
398+
* crypto blocks at the end of the array are ignored (and should
399+
* probably be zeroed by the caller).
400+
*
401+
* Returns the length of the decrypted data or a negative errno.
402+
*/
403+
int ceph_fscrypt_decrypt_pages(struct inode *inode, struct page **page,
404+
u64 off, int len)
405+
{
406+
int i, num_blocks;
407+
u64 baseblk = off >> CEPH_FSCRYPT_BLOCK_SHIFT;
408+
int ret = 0;
409+
410+
/*
411+
* We can't deal with partial blocks on an encrypted file, so mask off
412+
* the last bit.
413+
*/
414+
num_blocks = ceph_fscrypt_blocks(off, len & CEPH_FSCRYPT_BLOCK_MASK);
415+
416+
/* Decrypt each block */
417+
for (i = 0; i < num_blocks; ++i) {
418+
int blkoff = i << CEPH_FSCRYPT_BLOCK_SHIFT;
419+
int pgidx = blkoff >> PAGE_SHIFT;
420+
unsigned int pgoffs = offset_in_page(blkoff);
421+
int fret;
422+
423+
fret = ceph_fscrypt_decrypt_block_inplace(inode, page[pgidx],
424+
CEPH_FSCRYPT_BLOCK_SIZE, pgoffs,
425+
baseblk + i);
426+
if (fret < 0) {
427+
if (ret == 0)
428+
ret = fret;
429+
break;
430+
}
431+
ret += CEPH_FSCRYPT_BLOCK_SIZE;
432+
}
433+
return ret;
434+
}
435+
436+
/**
437+
* ceph_fscrypt_decrypt_extents: decrypt received extents in given buffer
438+
* @inode: inode associated with pages being decrypted
439+
* @page: pointer to page array
440+
* @off: offset into the file that the data in page[0] starts
441+
* @map: pointer to extent array
442+
* @ext_cnt: length of extent array
443+
*
444+
* Given an extent map and a page array, decrypt the received data in-place,
445+
* skipping holes. Returns the offset into buffer of end of last decrypted
446+
* block.
447+
*/
448+
int ceph_fscrypt_decrypt_extents(struct inode *inode, struct page **page,
449+
u64 off, struct ceph_sparse_extent *map,
450+
u32 ext_cnt)
451+
{
452+
int i, ret = 0;
453+
struct ceph_inode_info *ci = ceph_inode(inode);
454+
u64 objno, objoff;
455+
u32 xlen;
456+
457+
/* Nothing to do for empty array */
458+
if (ext_cnt == 0) {
459+
dout("%s: empty array, ret 0\n", __func__);
460+
return 0;
461+
}
462+
463+
ceph_calc_file_object_mapping(&ci->i_layout, off, map[0].len,
464+
&objno, &objoff, &xlen);
465+
466+
for (i = 0; i < ext_cnt; ++i) {
467+
struct ceph_sparse_extent *ext = &map[i];
468+
int pgsoff = ext->off - objoff;
469+
int pgidx = pgsoff >> PAGE_SHIFT;
470+
int fret;
471+
472+
if ((ext->off | ext->len) & ~CEPH_FSCRYPT_BLOCK_MASK) {
473+
pr_warn("%s: bad encrypted sparse extent idx %d off %llx len %llx\n",
474+
__func__, i, ext->off, ext->len);
475+
return -EIO;
476+
}
477+
fret = ceph_fscrypt_decrypt_pages(inode, &page[pgidx],
478+
off + pgsoff, ext->len);
479+
dout("%s: [%d] 0x%llx~0x%llx fret %d\n", __func__, i,
480+
ext->off, ext->len, fret);
481+
if (fret < 0) {
482+
if (ret == 0)
483+
ret = fret;
484+
break;
485+
}
486+
ret = pgsoff + fret;
487+
}
488+
dout("%s: ret %d\n", __func__, ret);
489+
return ret;
490+
}
491+
492+
/**
493+
* ceph_fscrypt_encrypt_pages - encrypt an array of pages
494+
* @inode: pointer to inode associated with these pages
495+
* @page: pointer to page array
496+
* @off: offset into the file that the data starts
497+
* @len: max length to encrypt
498+
* @gfp: gfp flags to use for allocation
499+
*
500+
* Decrypt an array of cleartext pages and return the amount of
501+
* data encrypted. Any data in the page prior to the start of the
502+
* first complete block in the read is ignored. Any incomplete
503+
* crypto blocks at the end of the array are ignored.
504+
*
505+
* Returns the length of the encrypted data or a negative errno.
506+
*/
507+
int ceph_fscrypt_encrypt_pages(struct inode *inode, struct page **page, u64 off,
508+
int len, gfp_t gfp)
509+
{
510+
int i, num_blocks;
511+
u64 baseblk = off >> CEPH_FSCRYPT_BLOCK_SHIFT;
512+
int ret = 0;
513+
514+
/*
515+
* We can't deal with partial blocks on an encrypted file, so mask off
516+
* the last bit.
517+
*/
518+
num_blocks = ceph_fscrypt_blocks(off, len & CEPH_FSCRYPT_BLOCK_MASK);
519+
520+
/* Encrypt each block */
521+
for (i = 0; i < num_blocks; ++i) {
522+
int blkoff = i << CEPH_FSCRYPT_BLOCK_SHIFT;
523+
int pgidx = blkoff >> PAGE_SHIFT;
524+
unsigned int pgoffs = offset_in_page(blkoff);
525+
int fret;
526+
527+
fret = ceph_fscrypt_encrypt_block_inplace(inode, page[pgidx],
528+
CEPH_FSCRYPT_BLOCK_SIZE, pgoffs,
529+
baseblk + i, gfp);
530+
if (fret < 0) {
531+
if (ret == 0)
532+
ret = fret;
533+
break;
534+
}
535+
ret += CEPH_FSCRYPT_BLOCK_SIZE;
536+
}
537+
return ret;
538+
}

fs/ceph/crypto.h

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,44 @@ int ceph_fname_to_usr(const struct ceph_fname *fname, struct fscrypt_str *tname,
105105
struct fscrypt_str *oname, bool *is_nokey);
106106
int ceph_fscrypt_prepare_readdir(struct inode *dir);
107107

108+
static inline unsigned int ceph_fscrypt_blocks(u64 off, u64 len)
109+
{
110+
/* crypto blocks cannot span more than one page */
111+
BUILD_BUG_ON(CEPH_FSCRYPT_BLOCK_SHIFT > PAGE_SHIFT);
112+
113+
return ((off+len+CEPH_FSCRYPT_BLOCK_SIZE-1) >> CEPH_FSCRYPT_BLOCK_SHIFT) -
114+
(off >> CEPH_FSCRYPT_BLOCK_SHIFT);
115+
}
116+
117+
/*
118+
* If we have an encrypted inode then we must adjust the offset and
119+
* range of the on-the-wire read to cover an entire encryption block.
120+
* The copy will be done using the original offset and length, after
121+
* we've decrypted the result.
122+
*/
123+
static inline void ceph_fscrypt_adjust_off_and_len(struct inode *inode,
124+
u64 *off, u64 *len)
125+
{
126+
if (IS_ENCRYPTED(inode)) {
127+
*len = ceph_fscrypt_blocks(*off, *len) * CEPH_FSCRYPT_BLOCK_SIZE;
128+
*off &= CEPH_FSCRYPT_BLOCK_MASK;
129+
}
130+
}
131+
132+
int ceph_fscrypt_decrypt_block_inplace(const struct inode *inode,
133+
struct page *page, unsigned int len,
134+
unsigned int offs, u64 lblk_num);
135+
int ceph_fscrypt_encrypt_block_inplace(const struct inode *inode,
136+
struct page *page, unsigned int len,
137+
unsigned int offs, u64 lblk_num,
138+
gfp_t gfp_flags);
139+
int ceph_fscrypt_decrypt_pages(struct inode *inode, struct page **page,
140+
u64 off, int len);
141+
int ceph_fscrypt_decrypt_extents(struct inode *inode, struct page **page,
142+
u64 off, struct ceph_sparse_extent *map,
143+
u32 ext_cnt);
144+
int ceph_fscrypt_encrypt_pages(struct inode *inode, struct page **page, u64 off,
145+
int len, gfp_t gfp);
108146
#else /* CONFIG_FS_ENCRYPTION */
109147

110148
static inline void ceph_fscrypt_set_ops(struct super_block *sb)
@@ -166,6 +204,48 @@ static inline int ceph_fscrypt_prepare_readdir(struct inode *dir)
166204
{
167205
return 0;
168206
}
207+
208+
static inline void ceph_fscrypt_adjust_off_and_len(struct inode *inode,
209+
u64 *off, u64 *len)
210+
{
211+
}
212+
213+
static inline int ceph_fscrypt_decrypt_block_inplace(const struct inode *inode,
214+
struct page *page, unsigned int len,
215+
unsigned int offs, u64 lblk_num)
216+
{
217+
return 0;
218+
}
219+
220+
static inline int ceph_fscrypt_encrypt_block_inplace(const struct inode *inode,
221+
struct page *page, unsigned int len,
222+
unsigned int offs, u64 lblk_num,
223+
gfp_t gfp_flags)
224+
{
225+
return 0;
226+
}
227+
228+
static inline int ceph_fscrypt_decrypt_pages(struct inode *inode,
229+
struct page **page, u64 off,
230+
int len)
231+
{
232+
return 0;
233+
}
234+
235+
static inline int ceph_fscrypt_decrypt_extents(struct inode *inode,
236+
struct page **page, u64 off,
237+
struct ceph_sparse_extent *map,
238+
u32 ext_cnt)
239+
{
240+
return 0;
241+
}
242+
243+
static inline int ceph_fscrypt_encrypt_pages(struct inode *inode,
244+
struct page **page, u64 off,
245+
int len, gfp_t gfp)
246+
{
247+
return 0;
248+
}
169249
#endif /* CONFIG_FS_ENCRYPTION */
170250

171251
#endif

0 commit comments

Comments
 (0)