2020-07-27 10:16:09 -04:00
// SPDX-License-Identifier: GPL-2.0
2022-04-18 14:59:56 +01:00
/*
* The base64 encode / decode code was copied from fscrypt :
* Copyright ( C ) 2015 , Google , Inc .
* Copyright ( C ) 2015 , Motorola Mobility
* Written by Uday Savagaonkar , 2014.
* Modified by Jaegeuk Kim , 2015.
*/
2020-07-27 10:16:09 -04:00
# include <linux/ceph/ceph_debug.h>
# include <linux/xattr.h>
# include <linux/fscrypt.h>
2022-08-25 09:31:14 -04:00
# include <linux/ceph/striper.h>
2020-07-27 10:16:09 -04:00
# include "super.h"
2020-09-08 09:47:40 -04:00
# include "mds_client.h"
2020-07-27 10:16:09 -04:00
# include "crypto.h"
2022-04-18 14:59:56 +01:00
/*
* The base64url encoding used by fscrypt includes the ' _ ' character , which may
* cause problems in snapshot names ( which can not start with ' _ ' ) . Thus , we
* used the base64 encoding defined for IMAP mailbox names ( RFC 3501 ) instead ,
* which replaces ' - ' and ' _ ' by ' + ' and ' , ' .
*/
static const char base64_table [ 65 ] =
" ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+, " ;
int ceph_base64_encode ( const u8 * src , int srclen , char * dst )
{
u32 ac = 0 ;
int bits = 0 ;
int i ;
char * cp = dst ;
for ( i = 0 ; i < srclen ; i + + ) {
ac = ( ac < < 8 ) | src [ i ] ;
bits + = 8 ;
do {
bits - = 6 ;
* cp + + = base64_table [ ( ac > > bits ) & 0x3f ] ;
} while ( bits > = 6 ) ;
}
if ( bits )
* cp + + = base64_table [ ( ac < < ( 6 - bits ) ) & 0x3f ] ;
return cp - dst ;
}
int ceph_base64_decode ( const char * src , int srclen , u8 * dst )
{
u32 ac = 0 ;
int bits = 0 ;
int i ;
u8 * bp = dst ;
for ( i = 0 ; i < srclen ; i + + ) {
const char * p = strchr ( base64_table , src [ i ] ) ;
if ( p = = NULL | | src [ i ] = = 0 )
return - 1 ;
ac = ( ac < < 6 ) | ( p - base64_table ) ;
bits + = 6 ;
if ( bits > = 8 ) {
bits - = 8 ;
* bp + + = ( u8 ) ( ac > > bits ) ;
}
}
if ( ac & ( ( 1 < < bits ) - 1 ) )
return - 1 ;
return bp - dst ;
}
2020-07-27 10:16:09 -04:00
static int ceph_crypt_get_context ( struct inode * inode , void * ctx , size_t len )
{
struct ceph_inode_info * ci = ceph_inode ( inode ) ;
struct ceph_fscrypt_auth * cfa = ( struct ceph_fscrypt_auth * ) ci - > fscrypt_auth ;
u32 ctxlen ;
/* Non existent or too short? */
if ( ! cfa | | ( ci - > fscrypt_auth_len < ( offsetof ( struct ceph_fscrypt_auth , cfa_blob ) + 1 ) ) )
return - ENOBUFS ;
/* Some format we don't recognize? */
if ( le32_to_cpu ( cfa - > cfa_version ) ! = CEPH_FSCRYPT_AUTH_VERSION )
return - ENOBUFS ;
ctxlen = le32_to_cpu ( cfa - > cfa_blob_len ) ;
if ( len < ctxlen )
return - ERANGE ;
memcpy ( ctx , cfa - > cfa_blob , ctxlen ) ;
return ctxlen ;
}
static int ceph_crypt_set_context ( struct inode * inode , const void * ctx ,
size_t len , void * fs_data )
{
int ret ;
struct iattr attr = { } ;
struct ceph_iattr cia = { } ;
struct ceph_fscrypt_auth * cfa ;
WARN_ON_ONCE ( fs_data ) ;
if ( len > FSCRYPT_SET_CONTEXT_MAX_SIZE )
return - EINVAL ;
cfa = kzalloc ( sizeof ( * cfa ) , GFP_KERNEL ) ;
if ( ! cfa )
return - ENOMEM ;
cfa - > cfa_version = cpu_to_le32 ( CEPH_FSCRYPT_AUTH_VERSION ) ;
cfa - > cfa_blob_len = cpu_to_le32 ( len ) ;
memcpy ( cfa - > cfa_blob , ctx , len ) ;
cia . fscrypt_auth = cfa ;
ret = __ceph_setattr ( inode , & attr , & cia ) ;
if ( ret = = 0 )
inode_set_flags ( inode , S_ENCRYPTED , S_ENCRYPTED ) ;
kfree ( cia . fscrypt_auth ) ;
return ret ;
}
static bool ceph_crypt_empty_dir ( struct inode * inode )
{
struct ceph_inode_info * ci = ceph_inode ( inode ) ;
return ci - > i_rsubdirs + ci - > i_rfiles = = 1 ;
}
2020-09-08 09:47:40 -04:00
static const union fscrypt_policy * ceph_get_dummy_policy ( struct super_block * sb )
{
2023-06-12 10:50:38 +08:00
return ceph_sb_to_fs_client ( sb ) - > fsc_dummy_enc_policy . policy ;
2020-09-08 09:47:40 -04:00
}
2020-07-27 10:16:09 -04:00
static struct fscrypt_operations ceph_fscrypt_ops = {
. get_context = ceph_crypt_get_context ,
. set_context = ceph_crypt_set_context ,
2020-09-08 09:47:40 -04:00
. get_dummy_policy = ceph_get_dummy_policy ,
2020-07-27 10:16:09 -04:00
. empty_dir = ceph_crypt_empty_dir ,
} ;
void ceph_fscrypt_set_ops ( struct super_block * sb )
{
fscrypt_set_ops ( sb , & ceph_fscrypt_ops ) ;
}
2020-09-08 09:47:40 -04:00
void ceph_fscrypt_free_dummy_policy ( struct ceph_fs_client * fsc )
{
fscrypt_free_dummy_policy ( & fsc - > fsc_dummy_enc_policy ) ;
}
int ceph_fscrypt_prepare_context ( struct inode * dir , struct inode * inode ,
struct ceph_acl_sec_ctx * as )
{
int ret , ctxsize ;
bool encrypted = false ;
struct ceph_inode_info * ci = ceph_inode ( inode ) ;
ret = fscrypt_prepare_new_inode ( dir , inode , & encrypted ) ;
if ( ret )
return ret ;
if ( ! encrypted )
return 0 ;
as - > fscrypt_auth = kzalloc ( sizeof ( * as - > fscrypt_auth ) , GFP_KERNEL ) ;
if ( ! as - > fscrypt_auth )
return - ENOMEM ;
ctxsize = fscrypt_context_for_new_inode ( as - > fscrypt_auth - > cfa_blob ,
inode ) ;
if ( ctxsize < 0 )
return ctxsize ;
as - > fscrypt_auth - > cfa_version = cpu_to_le32 ( CEPH_FSCRYPT_AUTH_VERSION ) ;
as - > fscrypt_auth - > cfa_blob_len = cpu_to_le32 ( ctxsize ) ;
WARN_ON_ONCE ( ci - > fscrypt_auth ) ;
kfree ( ci - > fscrypt_auth ) ;
ci - > fscrypt_auth_len = ceph_fscrypt_auth_len ( as - > fscrypt_auth ) ;
ci - > fscrypt_auth = kmemdup ( as - > fscrypt_auth , ci - > fscrypt_auth_len ,
GFP_KERNEL ) ;
if ( ! ci - > fscrypt_auth )
return - ENOMEM ;
inode - > i_flags | = S_ENCRYPTED ;
return 0 ;
}
void ceph_fscrypt_as_ctx_to_req ( struct ceph_mds_request * req ,
struct ceph_acl_sec_ctx * as )
{
swap ( req - > r_fscrypt_auth , as - > fscrypt_auth ) ;
}
2020-08-07 09:28:31 -04:00
2022-08-25 09:31:28 -04:00
/*
* User - created snapshots can ' t start with ' _ ' . Snapshots that start with this
* character are special ( hint : there aren ' t real snapshots ) and use the
* following format :
*
* _ < SNAPSHOT - NAME > _ < INODE - NUMBER >
*
* where :
* - < SNAPSHOT - NAME > - the real snapshot name that may need to be decrypted ,
* - < INODE - NUMBER > - the inode number ( in decimal ) for the actual snapshot
*
* This function parses these snapshot names and returns the inode
* < INODE - NUMBER > . ' name_len ' will also bet set with the < SNAPSHOT - NAME >
* length .
*/
static struct inode * parse_longname ( const struct inode * parent ,
const char * name , int * name_len )
2020-08-07 09:28:31 -04:00
{
2022-08-25 09:31:28 -04:00
struct inode * dir = NULL ;
struct ceph_vino vino = { . snap = CEPH_NOSNAP } ;
char * inode_number ;
char * name_end ;
int orig_len = * name_len ;
int ret = - EIO ;
/* Skip initial '_' */
name + + ;
name_end = strrchr ( name , ' _ ' ) ;
if ( ! name_end ) {
dout ( " Failed to parse long snapshot name: %s \n " , name ) ;
return ERR_PTR ( - EIO ) ;
}
* name_len = ( name_end - name ) ;
if ( * name_len < = 0 ) {
pr_err ( " Failed to parse long snapshot name \n " ) ;
return ERR_PTR ( - EIO ) ;
}
/* Get the inode number */
inode_number = kmemdup_nul ( name_end + 1 ,
orig_len - * name_len - 2 ,
GFP_KERNEL ) ;
if ( ! inode_number )
return ERR_PTR ( - ENOMEM ) ;
ret = kstrtou64 ( inode_number , 10 , & vino . ino ) ;
if ( ret ) {
dout ( " Failed to parse inode number: %s \n " , name ) ;
dir = ERR_PTR ( ret ) ;
goto out ;
}
/* And finally the inode */
dir = ceph_find_inode ( parent - > i_sb , vino ) ;
if ( ! dir ) {
/* This can happen if we're not mounting cephfs on the root */
dir = ceph_get_inode ( parent - > i_sb , vino , NULL ) ;
2023-09-08 12:20:20 +01:00
if ( IS_ERR ( dir ) )
dout ( " Can't find inode %s (%s) \n " , inode_number , name ) ;
2022-08-25 09:31:28 -04:00
}
out :
kfree ( inode_number ) ;
return dir ;
}
int ceph_encode_encrypted_dname ( struct inode * parent , struct qstr * d_name ,
char * buf )
{
struct inode * dir = parent ;
struct qstr iname ;
2020-08-07 09:28:31 -04:00
u32 len ;
2022-08-25 09:31:28 -04:00
int name_len ;
2020-08-07 09:28:31 -04:00
int elen ;
int ret ;
2022-08-25 09:31:28 -04:00
u8 * cryptbuf = NULL ;
iname . name = d_name - > name ;
name_len = d_name - > len ;
/* Handle the special case of snapshot names that start with '_' */
if ( ( ceph_snap ( dir ) = = CEPH_SNAPDIR ) & & ( name_len > 0 ) & &
( iname . name [ 0 ] = = ' _ ' ) ) {
dir = parse_longname ( parent , iname . name , & name_len ) ;
if ( IS_ERR ( dir ) )
return PTR_ERR ( dir ) ;
iname . name + + ; /* skip initial '_' */
}
iname . len = name_len ;
2020-08-07 09:28:31 -04:00
2022-08-25 09:31:28 -04:00
if ( ! fscrypt_has_encryption_key ( dir ) ) {
2022-03-14 10:28:35 +08:00
memcpy ( buf , d_name - > name , d_name - > len ) ;
2022-08-25 09:31:28 -04:00
elen = d_name - > len ;
goto out ;
2022-03-14 10:28:35 +08:00
}
2020-08-07 09:28:31 -04:00
/*
* Convert cleartext d_name to ciphertext . If result is longer than
* CEPH_NOHASH_NAME_MAX , sha256 the remaining bytes
*
* See : fscrypt_setup_filename
*/
2022-08-25 09:31:28 -04:00
if ( ! fscrypt_fname_encrypted_size ( dir , iname . len , NAME_MAX , & len ) ) {
elen = - ENAMETOOLONG ;
goto out ;
}
2020-08-07 09:28:31 -04:00
/* Allocate a buffer appropriate to hold the result */
cryptbuf = kmalloc ( len > CEPH_NOHASH_NAME_MAX ? NAME_MAX : len ,
GFP_KERNEL ) ;
2022-08-25 09:31:28 -04:00
if ( ! cryptbuf ) {
elen = - ENOMEM ;
goto out ;
}
2020-08-07 09:28:31 -04:00
2022-08-25 09:31:28 -04:00
ret = fscrypt_fname_encrypt ( dir , & iname , cryptbuf , len ) ;
2020-08-07 09:28:31 -04:00
if ( ret ) {
2022-08-25 09:31:28 -04:00
elen = ret ;
goto out ;
2020-08-07 09:28:31 -04:00
}
/* hash the end if the name is long enough */
if ( len > CEPH_NOHASH_NAME_MAX ) {
u8 hash [ SHA256_DIGEST_SIZE ] ;
u8 * extra = cryptbuf + CEPH_NOHASH_NAME_MAX ;
/*
* hash the extra bytes and overwrite crypttext beyond that
* point with it
*/
sha256 ( extra , len - CEPH_NOHASH_NAME_MAX , hash ) ;
memcpy ( extra , hash , SHA256_DIGEST_SIZE ) ;
len = CEPH_NOHASH_NAME_MAX + SHA256_DIGEST_SIZE ;
}
/* base64 encode the encrypted name */
elen = ceph_base64_encode ( cryptbuf , len , buf ) ;
dout ( " base64-encoded ciphertext name = %.*s \n " , elen , buf ) ;
2022-08-25 09:31:28 -04:00
/* To understand the 240 limit, see CEPH_NOHASH_NAME_MAX comments */
WARN_ON ( elen > 240 ) ;
if ( ( elen > 0 ) & & ( dir ! = parent ) ) {
char tmp_buf [ NAME_MAX ] ;
elen = snprintf ( tmp_buf , sizeof ( tmp_buf ) , " _%.*s_%ld " ,
elen , buf , dir - > i_ino ) ;
memcpy ( buf , tmp_buf , elen ) ;
}
out :
kfree ( cryptbuf ) ;
if ( dir ! = parent ) {
if ( ( dir - > i_state & I_NEW ) )
discard_new_inode ( dir ) ;
else
iput ( dir ) ;
}
2020-08-07 09:28:31 -04:00
return elen ;
}
2021-03-26 12:26:18 -04:00
2022-08-25 09:31:28 -04:00
int ceph_encode_encrypted_fname ( struct inode * parent , struct dentry * dentry ,
char * buf )
2022-03-14 10:28:35 +08:00
{
WARN_ON_ONCE ( ! fscrypt_has_encryption_key ( parent ) ) ;
return ceph_encode_encrypted_dname ( parent , & dentry - > d_name , buf ) ;
}
2021-03-26 12:26:18 -04:00
/**
* ceph_fname_to_usr - convert a filename for userland presentation
* @ fname : ceph_fname to be converted
* @ tname : temporary name buffer to use for conversion ( may be NULL )
* @ oname : where converted name should be placed
* @ is_nokey : set to true if key wasn ' t available during conversion ( may be NULL )
*
* Given a filename ( usually from the MDS ) , format it for presentation to
* userland . If @ parent is not encrypted , just pass it back as - is .
*
* Otherwise , base64 decode the string , and then ask fscrypt to format it
* for userland presentation .
*
* Returns 0 on success or negative error code on error .
*/
int ceph_fname_to_usr ( const struct ceph_fname * fname , struct fscrypt_str * tname ,
struct fscrypt_str * oname , bool * is_nokey )
{
2022-08-25 09:31:28 -04:00
struct inode * dir = fname - > dir ;
2021-03-26 12:26:18 -04:00
struct fscrypt_str _tname = FSTR_INIT ( NULL , 0 ) ;
struct fscrypt_str iname ;
2022-08-25 09:31:28 -04:00
char * name = fname - > name ;
int name_len = fname - > name_len ;
int ret ;
2021-03-26 12:26:18 -04:00
/* Sanity check that the resulting name will fit in the buffer */
if ( fname - > name_len > NAME_MAX | | fname - > ctext_len > NAME_MAX )
return - EIO ;
2022-08-25 09:31:28 -04:00
/* Handle the special case of snapshot names that start with '_' */
if ( ( ceph_snap ( dir ) = = CEPH_SNAPDIR ) & & ( name_len > 0 ) & &
( name [ 0 ] = = ' _ ' ) ) {
dir = parse_longname ( dir , name , & name_len ) ;
if ( IS_ERR ( dir ) )
return PTR_ERR ( dir ) ;
name + + ; /* skip initial '_' */
}
if ( ! IS_ENCRYPTED ( dir ) ) {
oname - > name = fname - > name ;
oname - > len = fname - > name_len ;
ret = 0 ;
goto out_inode ;
}
ret = ceph_fscrypt_prepare_readdir ( dir ) ;
if ( ret )
goto out_inode ;
2021-03-26 12:26:18 -04:00
/*
* Use the raw dentry name as sent by the MDS instead of
* generating a nokey name via fscrypt .
*/
2022-08-25 09:31:28 -04:00
if ( ! fscrypt_has_encryption_key ( dir ) ) {
2022-03-14 10:28:35 +08:00
if ( fname - > no_copy )
oname - > name = fname - > name ;
else
memcpy ( oname - > name , fname - > name , fname - > name_len ) ;
2021-03-26 12:26:18 -04:00
oname - > len = fname - > name_len ;
if ( is_nokey )
* is_nokey = true ;
2022-08-25 09:31:28 -04:00
ret = 0 ;
goto out_inode ;
2021-03-26 12:26:18 -04:00
}
if ( fname - > ctext_len = = 0 ) {
int declen ;
if ( ! tname ) {
ret = fscrypt_fname_alloc_buffer ( NAME_MAX , & _tname ) ;
if ( ret )
2022-08-25 09:31:28 -04:00
goto out_inode ;
2021-03-26 12:26:18 -04:00
tname = & _tname ;
}
2022-08-25 09:31:28 -04:00
declen = ceph_base64_decode ( name , name_len , tname - > name ) ;
2021-03-26 12:26:18 -04:00
if ( declen < = 0 ) {
ret = - EIO ;
goto out ;
}
iname . name = tname - > name ;
iname . len = declen ;
} else {
iname . name = fname - > ctext ;
iname . len = fname - > ctext_len ;
}
2022-08-25 09:31:28 -04:00
ret = fscrypt_fname_disk_to_usr ( dir , 0 , 0 , & iname , oname ) ;
if ( ! ret & & ( dir ! = fname - > dir ) ) {
char tmp_buf [ CEPH_BASE64_CHARS ( NAME_MAX ) ] ;
name_len = snprintf ( tmp_buf , sizeof ( tmp_buf ) , " _%.*s_%ld " ,
oname - > len , oname - > name , dir - > i_ino ) ;
memcpy ( oname - > name , tmp_buf , name_len ) ;
oname - > len = name_len ;
}
2021-03-26 12:26:18 -04:00
out :
fscrypt_fname_free_buffer ( & _tname ) ;
2022-08-25 09:31:28 -04:00
out_inode :
2023-09-29 10:12:06 +01:00
if ( dir ! = fname - > dir ) {
2022-08-25 09:31:28 -04:00
if ( ( dir - > i_state & I_NEW ) )
discard_new_inode ( dir ) ;
else
iput ( dir ) ;
}
2021-03-26 12:26:18 -04:00
return ret ;
}
2022-11-29 10:39:49 +00:00
/**
* ceph_fscrypt_prepare_readdir - simple __fscrypt_prepare_readdir ( ) wrapper
* @ dir : directory inode for readdir prep
*
* Simple wrapper around __fscrypt_prepare_readdir ( ) that will mark directory as
* non - complete if this call results in having the directory unlocked .
*
* Returns :
* 1 - if directory was locked and key is now loaded ( i . e . dir is unlocked )
* 0 - if directory is still locked
* < 0 - if __fscrypt_prepare_readdir ( ) fails
*/
int ceph_fscrypt_prepare_readdir ( struct inode * dir )
{
bool had_key = fscrypt_has_encryption_key ( dir ) ;
int err ;
if ( ! IS_ENCRYPTED ( dir ) )
return 0 ;
err = __fscrypt_prepare_readdir ( dir ) ;
if ( err )
return err ;
if ( ! had_key & & fscrypt_has_encryption_key ( dir ) ) {
/* directory just got unlocked, mark it as not complete */
ceph_dir_clear_complete ( dir ) ;
return 1 ;
}
return 0 ;
}
2022-08-25 09:31:14 -04:00
int ceph_fscrypt_decrypt_block_inplace ( const struct inode * inode ,
struct page * page , unsigned int len ,
unsigned int offs , u64 lblk_num )
{
dout ( " %s: len %u offs %u blk %llu \n " , __func__ , len , offs , lblk_num ) ;
return fscrypt_decrypt_block_inplace ( inode , page , len , offs , lblk_num ) ;
}
int ceph_fscrypt_encrypt_block_inplace ( const struct inode * inode ,
struct page * page , unsigned int len ,
unsigned int offs , u64 lblk_num ,
gfp_t gfp_flags )
{
dout ( " %s: len %u offs %u blk %llu \n " , __func__ , len , offs , lblk_num ) ;
return fscrypt_encrypt_block_inplace ( inode , page , len , offs , lblk_num ,
gfp_flags ) ;
}
/**
* ceph_fscrypt_decrypt_pages - decrypt an array of pages
* @ inode : pointer to inode associated with these pages
* @ page : pointer to page array
* @ off : offset into the file that the read data starts
* @ len : max length to decrypt
*
* Decrypt an array of fscrypt ' ed pages and return the amount of
* data decrypted . Any data in the page prior to the start of the
* first complete block in the read is ignored . Any incomplete
* crypto blocks at the end of the array are ignored ( and should
* probably be zeroed by the caller ) .
*
* Returns the length of the decrypted data or a negative errno .
*/
int ceph_fscrypt_decrypt_pages ( struct inode * inode , struct page * * page ,
u64 off , int len )
{
int i , num_blocks ;
u64 baseblk = off > > CEPH_FSCRYPT_BLOCK_SHIFT ;
int ret = 0 ;
/*
* We can ' t deal with partial blocks on an encrypted file , so mask off
* the last bit .
*/
num_blocks = ceph_fscrypt_blocks ( off , len & CEPH_FSCRYPT_BLOCK_MASK ) ;
/* Decrypt each block */
for ( i = 0 ; i < num_blocks ; + + i ) {
int blkoff = i < < CEPH_FSCRYPT_BLOCK_SHIFT ;
int pgidx = blkoff > > PAGE_SHIFT ;
unsigned int pgoffs = offset_in_page ( blkoff ) ;
int fret ;
fret = ceph_fscrypt_decrypt_block_inplace ( inode , page [ pgidx ] ,
CEPH_FSCRYPT_BLOCK_SIZE , pgoffs ,
baseblk + i ) ;
if ( fret < 0 ) {
if ( ret = = 0 )
ret = fret ;
break ;
}
ret + = CEPH_FSCRYPT_BLOCK_SIZE ;
}
return ret ;
}
/**
* ceph_fscrypt_decrypt_extents : decrypt received extents in given buffer
* @ inode : inode associated with pages being decrypted
* @ page : pointer to page array
* @ off : offset into the file that the data in page [ 0 ] starts
* @ map : pointer to extent array
* @ ext_cnt : length of extent array
*
* Given an extent map and a page array , decrypt the received data in - place ,
* skipping holes . Returns the offset into buffer of end of last decrypted
* block .
*/
int ceph_fscrypt_decrypt_extents ( struct inode * inode , struct page * * page ,
u64 off , struct ceph_sparse_extent * map ,
u32 ext_cnt )
{
int i , ret = 0 ;
struct ceph_inode_info * ci = ceph_inode ( inode ) ;
u64 objno , objoff ;
u32 xlen ;
/* Nothing to do for empty array */
if ( ext_cnt = = 0 ) {
dout ( " %s: empty array, ret 0 \n " , __func__ ) ;
return 0 ;
}
ceph_calc_file_object_mapping ( & ci - > i_layout , off , map [ 0 ] . len ,
& objno , & objoff , & xlen ) ;
for ( i = 0 ; i < ext_cnt ; + + i ) {
struct ceph_sparse_extent * ext = & map [ i ] ;
int pgsoff = ext - > off - objoff ;
int pgidx = pgsoff > > PAGE_SHIFT ;
int fret ;
if ( ( ext - > off | ext - > len ) & ~ CEPH_FSCRYPT_BLOCK_MASK ) {
pr_warn ( " %s: bad encrypted sparse extent idx %d off %llx len %llx \n " ,
__func__ , i , ext - > off , ext - > len ) ;
return - EIO ;
}
fret = ceph_fscrypt_decrypt_pages ( inode , & page [ pgidx ] ,
off + pgsoff , ext - > len ) ;
dout ( " %s: [%d] 0x%llx~0x%llx fret %d \n " , __func__ , i ,
ext - > off , ext - > len , fret ) ;
if ( fret < 0 ) {
if ( ret = = 0 )
ret = fret ;
break ;
}
ret = pgsoff + fret ;
}
dout ( " %s: ret %d \n " , __func__ , ret ) ;
return ret ;
}
/**
* ceph_fscrypt_encrypt_pages - encrypt an array of pages
* @ inode : pointer to inode associated with these pages
* @ page : pointer to page array
* @ off : offset into the file that the data starts
* @ len : max length to encrypt
* @ gfp : gfp flags to use for allocation
*
* Decrypt an array of cleartext pages and return the amount of
* data encrypted . Any data in the page prior to the start of the
* first complete block in the read is ignored . Any incomplete
* crypto blocks at the end of the array are ignored .
*
* Returns the length of the encrypted data or a negative errno .
*/
int ceph_fscrypt_encrypt_pages ( struct inode * inode , struct page * * page , u64 off ,
int len , gfp_t gfp )
{
int i , num_blocks ;
u64 baseblk = off > > CEPH_FSCRYPT_BLOCK_SHIFT ;
int ret = 0 ;
/*
* We can ' t deal with partial blocks on an encrypted file , so mask off
* the last bit .
*/
num_blocks = ceph_fscrypt_blocks ( off , len & CEPH_FSCRYPT_BLOCK_MASK ) ;
/* Encrypt each block */
for ( i = 0 ; i < num_blocks ; + + i ) {
int blkoff = i < < CEPH_FSCRYPT_BLOCK_SHIFT ;
int pgidx = blkoff > > PAGE_SHIFT ;
unsigned int pgoffs = offset_in_page ( blkoff ) ;
int fret ;
fret = ceph_fscrypt_encrypt_block_inplace ( inode , page [ pgidx ] ,
CEPH_FSCRYPT_BLOCK_SIZE , pgoffs ,
baseblk + i , gfp ) ;
if ( fret < 0 ) {
if ( ret = = 0 )
ret = fret ;
break ;
}
ret + = CEPH_FSCRYPT_BLOCK_SIZE ;
}
return ret ;
}