diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst index c0dd35f1af12..a1b0b50da869 100644 --- a/Documentation/filesystems/fscrypt.rst +++ b/Documentation/filesystems/fscrypt.rst @@ -283,6 +283,21 @@ alternative master keys or to support rotating master keys. Instead, the master keys may be wrapped in userspace, e.g. as is done by the `fscrypt `_ tool. +Per-extent encryption keys +-------------------------- + +For certain file systems, such as btrfs, it's desired to derive a +per-extent encryption key. This is to enable features such as snapshots +and reflink, where you could have different inodes pointing at the same +extent. When a new extent is created fscrypt randomly generates a +16-byte nonce and the file system stores it alongside the extent. +Then, it uses a KDF (as described in `Key derivation function`_) to +derive the extent's key from the master key and nonce. + +Currently the inode's master key and encryption policy must match the +extent, so you cannot share extents between inodes that were encrypted +differently. + DIRECT_KEY policies ------------------- @@ -1483,6 +1498,27 @@ by the kernel and is used as KDF input or as a tweak to cause different files to be encrypted differently; see `Per-file encryption keys`_ and `DIRECT_KEY policies`_. +Extent encryption context +------------------------- + +The extent encryption context mirrors the important parts of the above +`Encryption context`_, with a few omissions. The struct is defined as +follows:: + + struct fscrypt_extent_context { + u8 version; + u8 encryption_mode; + u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]; + u8 nonce[FSCRYPT_FILE_NONCE_SIZE]; + }; + +Currently all fields much match the containing inode's encryption +context, with the exception of the nonce. + +Additionally extent encryption is only supported with +FSCRYPT_EXTENT_CONTEXT_V2 using the standard policy; all other policies +are disallowed. + Data path changes ----------------- @@ -1506,6 +1542,11 @@ buffer. Some filesystems, such as UBIFS, already use temporary buffers regardless of encryption. Other filesystems, such as ext4 and F2FS, have to allocate bounce pages specially for encryption. +Inline encryption is not optional for extent encryption based file +systems; the amount of objects required to be kept around is too much. +Inline encryption handles the object lifetime details which results in a +cleaner implementation. + Filename hashing and encoding ----------------------------- diff --git a/block/blk-crypto-fallback.c b/block/blk-crypto-fallback.c index 61f595410832..5d35f0687bf8 100644 --- a/block/blk-crypto-fallback.c +++ b/block/blk-crypto-fallback.c @@ -330,6 +330,17 @@ static void __blk_crypto_fallback_encrypt_bio(struct bio *src_bio, } } + /* Process the encrypted bio before we submit it. */ + if (bc->bc_key->crypto_cfg.process_bio) { + blk_status_t status; + + status = bc->bc_key->crypto_cfg.process_bio(src_bio, enc_bio); + if (status != BLK_STS_OK) { + enc_bio->bi_status = status; + goto out_free_enc_bio; + } + } + submit_bio(enc_bio); return; @@ -358,6 +369,16 @@ static void blk_crypto_fallback_encrypt_bio(struct bio *src_bio) struct blk_crypto_keyslot *slot; blk_status_t status; + /* + * We cannot split bio's that have process_bio, as they require the original bio. + * The upper layer must make sure to limit the submitted bio's appropriately. + */ + if (bio_segments(src_bio) > BIO_MAX_VECS && bc->bc_key->crypto_cfg.process_bio) { + src_bio->bi_status = BLK_STS_RESOURCE; + bio_endio(src_bio); + return; + } + status = blk_crypto_get_keyslot(blk_crypto_fallback_profile, bc->bc_key, &slot); if (status != BLK_STS_OK) { @@ -427,6 +448,13 @@ static void blk_crypto_fallback_decrypt_bio(struct work_struct *work) struct blk_crypto_keyslot *slot; blk_status_t status; + /* Process the bio first before trying to decrypt. */ + if (bc->bc_key->crypto_cfg.process_bio) { + status = bc->bc_key->crypto_cfg.process_bio(bio, bio); + if (status != BLK_STS_OK) + goto out; + } + status = blk_crypto_get_keyslot(blk_crypto_fallback_profile, bc->bc_key, &slot); if (status == BLK_STS_OK) { @@ -435,12 +463,25 @@ static void blk_crypto_fallback_decrypt_bio(struct work_struct *work) blk_crypto_fallback_tfm(slot)); blk_crypto_put_keyslot(slot); } +out: mempool_free(f_ctx, bio_fallback_crypt_ctx_pool); bio->bi_status = status; bio_endio(bio); } +/** + * blk_crypto_profile_is_fallback - check if this profile is the fallback + * profile + * @profile: the profile we're checking + * + * This is just a quick check to make sure @profile is the fallback profile. + */ +bool blk_crypto_profile_is_fallback(struct blk_crypto_profile *profile) +{ + return profile == blk_crypto_fallback_profile; +} + /** * blk_crypto_fallback_decrypt_endio - queue bio for fallback decryption * diff --git a/block/blk-crypto-internal.h b/block/blk-crypto-internal.h index 742694213529..c857ebeb9637 100644 --- a/block/blk-crypto-internal.h +++ b/block/blk-crypto-internal.h @@ -226,6 +226,8 @@ int blk_crypto_fallback_start_using_mode(enum blk_crypto_mode_num mode_num); int blk_crypto_fallback_evict_key(const struct blk_crypto_key *key); +bool blk_crypto_profile_is_fallback(struct blk_crypto_profile *profile); + #else /* CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK */ static inline int @@ -241,6 +243,12 @@ blk_crypto_fallback_evict_key(const struct blk_crypto_key *key) return 0; } +static inline bool +blk_crypto_profile_is_fallback(struct blk_crypto_profile *profile) +{ + return false; +} + #endif /* CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK */ #endif /* __LINUX_BLK_CRYPTO_INTERNAL_H */ diff --git a/block/blk-crypto-profile.c b/block/blk-crypto-profile.c index 4ac74443687a..ced35ee186f0 100644 --- a/block/blk-crypto-profile.c +++ b/block/blk-crypto-profile.c @@ -352,6 +352,8 @@ bool __blk_crypto_cfg_supported(struct blk_crypto_profile *profile, return false; if (!(profile->key_types_supported & cfg->key_type)) return false; + if (cfg->process_bio && !blk_crypto_profile_is_fallback(profile)) + return false; return true; } diff --git a/block/blk-crypto.c b/block/blk-crypto.c index 856d3c5b1fa0..d93c593ae1eb 100644 --- a/block/blk-crypto.c +++ b/block/blk-crypto.c @@ -300,6 +300,8 @@ int __blk_crypto_rq_bio_prep(struct request *rq, struct bio *bio, * @dun_bytes: number of bytes that will be used to specify the DUN when this * key is used * @data_unit_size: the data unit size to use for en/decryption + * @process_bio: the call back if the upper layer needs to process the encrypted + * bio * * Return: 0 on success, -errno on failure. The caller is responsible for * zeroizing both blk_key and key_bytes when done with them. @@ -309,7 +311,8 @@ int blk_crypto_init_key(struct blk_crypto_key *blk_key, enum blk_crypto_key_type key_type, enum blk_crypto_mode_num crypto_mode, unsigned int dun_bytes, - unsigned int data_unit_size) + unsigned int data_unit_size, + blk_crypto_process_bio_t process_bio) { const struct blk_crypto_mode *mode; @@ -343,6 +346,7 @@ int blk_crypto_init_key(struct blk_crypto_key *blk_key, blk_key->crypto_cfg.dun_bytes = dun_bytes; blk_key->crypto_cfg.data_unit_size = data_unit_size; blk_key->crypto_cfg.key_type = key_type; + blk_key->crypto_cfg.process_bio = process_bio; blk_key->data_unit_size_bits = ilog2(data_unit_size); blk_key->size = key_size; memcpy(blk_key->bytes, key_bytes, key_size); diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig index 5e75438e0b73..3a41392135da 100644 --- a/fs/btrfs/Kconfig +++ b/fs/btrfs/Kconfig @@ -16,6 +16,10 @@ config BTRFS_FS select RAID6_PQ select XOR_BLOCKS select XXHASH + select FS_ENCRYPTION_ALGS if FS_ENCRYPTION + select FS_ENCRYPTION_INLINE_CRYPT if FS_ENCRYPTION + select BLK_INLINE_ENCRYPTION if FS_ENCRYPTION + select BLK_INLINE_ENCRYPTION_FALLBACK if FS_ENCRYPTION depends on PAGE_SIZE_LESS_THAN_256KB help diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile index b15122aa26f9..7397db45eb36 100644 --- a/fs/btrfs/Makefile +++ b/fs/btrfs/Makefile @@ -38,6 +38,7 @@ btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o btrfs-$(CONFIG_BTRFS_DEBUG) += ref-verify.o btrfs-$(CONFIG_BLK_DEV_ZONED) += zoned.o btrfs-$(CONFIG_FS_VERITY) += verity.o +btrfs-$(CONFIG_FS_ENCRYPTION) += fscrypt.o btrfs-$(CONFIG_BTRFS_FS_RUN_SANITY_TESTS) += tests/free-space-tests.o \ tests/extent-buffer-tests.o tests/btrfs-tests.o \ diff --git a/fs/btrfs/accessors.h b/fs/btrfs/accessors.h index 8938357fcb40..09ed68fa2041 100644 --- a/fs/btrfs/accessors.h +++ b/fs/btrfs/accessors.h @@ -907,6 +907,8 @@ BTRFS_SETGET_STACK_FUNCS(stack_file_extent_disk_num_bytes, struct btrfs_file_extent_item, disk_num_bytes, 64); BTRFS_SETGET_STACK_FUNCS(stack_file_extent_compression, struct btrfs_file_extent_item, compression, 8); +BTRFS_SETGET_STACK_FUNCS(stack_file_extent_encryption, + struct btrfs_file_extent_item, encryption, 8); BTRFS_SETGET_FUNCS(file_extent_type, struct btrfs_file_extent_item, type, 8); diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index 273924ca912c..33d5df99be8e 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -20,6 +20,7 @@ #include "extent-tree.h" #include "relocation.h" #include "tree-checker.h" +#include "fscrypt.h" /* Just arbitrary numbers so we can be sure one of these happened. */ #define BACKREF_FOUND_SHARED 6 @@ -2104,6 +2105,39 @@ int btrfs_find_one_extref(struct btrfs_root *root, u64 inode_objectid, return ret; } +static int copy_resolved_iref_to_buf(struct btrfs_root *fs_root, + struct extent_buffer *eb, char *dest, + u64 parent, unsigned long name_off, + u32 name_len, s64 *bytes_left) +{ + struct btrfs_fs_info *fs_info = fs_root->fs_info; + struct fscrypt_str fname = FSTR_INIT(NULL, 0); + int ret; + + /* No encryption, just copy the name in. */ + if (!btrfs_fs_incompat(fs_info, ENCRYPT)) { + *bytes_left -= name_len; + if (*bytes_left >= 0) + read_extent_buffer(eb, dest + *bytes_left, name_off, name_len); + return 0; + } + + ret = fscrypt_fname_alloc_buffer(BTRFS_NAME_LEN, &fname); + if (ret) + return ret; + + ret = btrfs_decrypt_name(fs_root, eb, name_off, name_len, parent, &fname); + if (ret) + goto out; + + *bytes_left -= fname.len; + if (*bytes_left >= 0) + memcpy(dest + *bytes_left, fname.name, fname.len); +out: + fscrypt_fname_free_buffer(&fname); + return ret; +} + /* * this iterates to turn a name (from iref/extref) into a full filesystem path. * Elements of the path are separated by '/' and the path is guaranteed to be @@ -2135,15 +2169,16 @@ char *btrfs_ref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path, dest[bytes_left] = '\0'; while (1) { - bytes_left -= name_len; - if (bytes_left >= 0) - read_extent_buffer(eb, dest + bytes_left, - name_off, name_len); + ret = copy_resolved_iref_to_buf(fs_root, eb, dest, parent, + name_off, name_len, &bytes_left); if (eb != eb_in) { if (!path->skip_locking) btrfs_tree_read_unlock(eb); free_extent_buffer(eb); } + if (ret) + break; + ret = btrfs_find_item(fs_root, path, parent, 0, BTRFS_INODE_REF_KEY, &found_key); if (ret > 0) diff --git a/fs/btrfs/bio.c b/fs/btrfs/bio.c index cc0bd03048ba..3a8f32c5157f 100644 --- a/fs/btrfs/bio.c +++ b/fs/btrfs/bio.c @@ -15,6 +15,7 @@ #include "zoned.h" #include "file-item.h" #include "raid-stripe-tree.h" +#include "fscrypt.h" static struct bio_set btrfs_bioset; static struct bio_set btrfs_clone_bioset; @@ -97,6 +98,9 @@ static struct btrfs_bio *btrfs_split_bio(struct btrfs_fs_info *fs_info, bbio->ordered = orig_bbio->ordered; bbio->orig_logical = orig_bbio->orig_logical; orig_bbio->orig_logical += map_length; + } else if (is_data_bbio(bbio)) { + bbio->fscrypt_info = fscrypt_get_extent_info(orig_bbio->fscrypt_info); + bbio->orig_start = orig_bbio->orig_start; } bbio->csum_search_commit_root = orig_bbio->csum_search_commit_root; @@ -124,6 +128,8 @@ void btrfs_bio_end_io(struct btrfs_bio *bbio, blk_status_t status) /* Free bio that was never submitted to the underlying device. */ if (bbio_has_ordered_extent(bbio)) btrfs_put_ordered_extent(bbio->ordered); + else if (is_data_bbio(bbio)) + fscrypt_put_extent_info(bbio->fscrypt_info); bio_put(&bbio->bio); bbio = orig_bbio; @@ -147,6 +153,8 @@ void btrfs_bio_end_io(struct btrfs_bio *bbio, blk_status_t status) bbio->end_io(bbio); btrfs_put_ordered_extent(ordered); } else { + if (is_data_bbio(bbio)) + fscrypt_put_extent_info(bbio->fscrypt_info); bbio->end_io(bbio); } } @@ -174,6 +182,23 @@ static void btrfs_repair_done(struct btrfs_failed_bio *fbio) } } +static void handle_repair(struct btrfs_bio *repair_bbio, phys_addr_t *paddrs) +{ + struct btrfs_failed_bio *fbio = repair_bbio->private; + struct btrfs_inode *inode = repair_bbio->inode; + struct btrfs_fs_info *fs_info = inode->root->fs_info; + const u32 step = min(fs_info->sectorsize, PAGE_SIZE); + const u64 logical = repair_bbio->saved_iter.bi_sector << SECTOR_SHIFT; + int mirror = repair_bbio->mirror_num; + + do { + mirror = prev_repair_mirror(fbio, mirror); + btrfs_repair_io_failure(fs_info, btrfs_ino(inode), + repair_bbio->file_offset, fs_info->sectorsize, + logical, paddrs, step, mirror); + } while (mirror != fbio->bbio->mirror_num); +} + static void btrfs_end_repair_bio(struct btrfs_bio *repair_bbio, struct btrfs_device *dev) { @@ -186,7 +211,6 @@ static void btrfs_end_repair_bio(struct btrfs_bio *repair_bbio, */ struct bvec_iter saved_iter = repair_bbio->saved_iter; const u32 step = min(fs_info->sectorsize, PAGE_SIZE); - const u64 logical = repair_bbio->saved_iter.bi_sector << SECTOR_SHIFT; const u32 nr_steps = repair_bbio->saved_iter.bi_size / step; int mirror = repair_bbio->mirror_num; phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE]; @@ -196,6 +220,13 @@ static void btrfs_end_repair_bio(struct btrfs_bio *repair_bbio, /* Repair bbio should be eaxctly one block sized. */ ASSERT(repair_bbio->saved_iter.bi_size == fs_info->sectorsize); + /* + * If we got here from the encrypted path with ->csum_ok set then + * we've already csumed and repaired this sector, we're all done. + */ + if (repair_bbio->csum_ok) + goto done; + btrfs_bio_for_each_block(paddr, &repair_bbio->bio, &saved_iter, step) { ASSERT(slot < nr_steps); paddrs[slot] = paddr; @@ -214,17 +245,17 @@ static void btrfs_end_repair_bio(struct btrfs_bio *repair_bbio, goto done; } + fscrypt_set_bio_crypt_ctx_from_extent(&repair_bbio->bio, + repair_bbio->fscrypt_info, + repair_bbio->file_offset - + repair_bbio->orig_start, + GFP_NOFS); + btrfs_submit_bbio(repair_bbio, mirror); return; } - do { - mirror = prev_repair_mirror(fbio, mirror); - btrfs_repair_io_failure(fs_info, btrfs_ino(inode), - repair_bbio->file_offset, fs_info->sectorsize, - logical, paddrs, step, mirror); - } while (mirror != fbio->bbio->mirror_num); - + handle_repair(repair_bbio, paddrs); done: btrfs_repair_done(fbio); bio_put(&repair_bbio->bio); @@ -293,6 +324,14 @@ static struct btrfs_failed_bio *repair_one_sector(struct btrfs_bio *failed_bbio, repair_bbio = btrfs_bio(repair_bio); btrfs_bio_init(repair_bbio, failed_bbio->inode, failed_bbio->file_offset + bio_offset, NULL, fbio); + repair_bbio->fscrypt_info = fscrypt_get_extent_info(failed_bbio->fscrypt_info); + repair_bbio->orig_start = failed_bbio->orig_start; + + fscrypt_set_bio_crypt_ctx_from_extent(repair_bio, + failed_bbio->fscrypt_info, + repair_bbio->file_offset - + failed_bbio->orig_start, + GFP_NOFS); mirror = next_repair_mirror(fbio, failed_bbio->mirror_num); btrfs_debug(fs_info, "submitting repair read to mirror %d", mirror); @@ -300,6 +339,62 @@ static struct btrfs_failed_bio *repair_one_sector(struct btrfs_bio *failed_bbio, return fbio; } +blk_status_t btrfs_check_encrypted_read_bio(struct btrfs_bio *bbio, struct bio *enc_bio) +{ + struct btrfs_inode *inode = bbio->inode; + struct btrfs_fs_info *fs_info = inode->root->fs_info; + struct bvec_iter iter = bbio->saved_iter; + struct btrfs_device *dev = bbio->bio.bi_private; + const u32 blocksize = fs_info->sectorsize; + const u32 step = min(blocksize, PAGE_SIZE); + const u32 nr_steps = iter.bi_size / step; + phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE]; + phys_addr_t paddr; + unsigned int slot = 0; + u32 offset = 0; + + /* + * We have to use a copy of iter in case there's an error, + * btrfs_check_read_bio will handle submitting the repair bios. + */ + btrfs_bio_for_each_block(paddr, enc_bio, &iter, step) { + ASSERT(slot < nr_steps); + paddrs[slot] = paddr; + slot++; + offset += step; + if (IS_ALIGNED(offset, blocksize)) { + if (!btrfs_data_csum_ok(bbio, dev, offset - blocksize, paddrs)) + return BLK_STS_IOERR; + slot = 0; + } + } + + /* + * Read repair is slightly different for encrypted bio's. This + * callback is before we decrypt the bio in the block crypto layer, + * we're not actually in the endio handler. + * + * We don't trigger the repair process here either, that is handled + * in the actual endio path because we don't want to create another + * pseudo endio path through this callback. This is because when we + * call btrfs_repair_done() we want to call the endio for the original + * bbio. Short circuiting that for the encrypted case would be ugly. + * We really want to the repair case to be handled generically. + * + * However for the actual repair part we need to use this page + * pre-decrypted, which is why we call the btrfs_repair_io_failure() + * code from this path. The repair path is synchronous so we are + * safe there. Then we simply mark the repair bbio as completed so + * the actual btrfs_end_repair_bio() code can skip the repair part. + */ + if (bbio->bio.bi_pool == &btrfs_repair_bioset) + handle_repair(bbio, paddrs); + bbio->csum_ok = true; + fscrypt_put_extent_info(bbio->fscrypt_info); + bbio->fscrypt_info = NULL; + return BLK_STS_OK; +} + static void btrfs_check_read_bio(struct btrfs_bio *bbio, struct btrfs_device *dev) { struct btrfs_inode *inode = bbio->inode; @@ -329,6 +424,10 @@ static void btrfs_check_read_bio(struct btrfs_bio *bbio, struct btrfs_device *de /* Clear the I/O error. A failed repair will reset it. */ bbio->bio.bi_status = BLK_STS_OK; + /* This was an encrypted bio and we've already done the csum check. */ + if (status == BLK_STS_OK && bbio->csum_ok) + goto out; + btrfs_bio_for_each_block(paddr, &bbio->bio, iter, step) { paddrs[(offset / step) % nr_steps] = paddr; offset += step; @@ -340,6 +439,7 @@ static void btrfs_check_read_bio(struct btrfs_bio *bbio, struct btrfs_device *de paddrs, fbio); } } +out: if (bbio->csum != bbio->csum_inline) kvfree(bbio->csum); @@ -535,7 +635,7 @@ static void btrfs_submit_dev_bio(struct btrfs_device *dev, struct bio *bio) if (bio->bi_opf & REQ_BTRFS_CGROUP_PUNT) blkcg_punt_bio_submit(bio); else - submit_bio(bio); + blk_crypto_submit_bio(bio); } static void btrfs_submit_mirrored_bio(struct btrfs_io_context *bioc, int dev_nr) @@ -600,9 +700,9 @@ static int btrfs_bio_csum(struct btrfs_bio *bbio) if (bbio->bio.bi_opf & REQ_META) return btree_csum_one_bio(bbio); #ifdef CONFIG_BTRFS_EXPERIMENTAL - return btrfs_csum_one_bio(bbio, true); + return btrfs_csum_one_bio(bbio, &bbio->bio, true); #else - return btrfs_csum_one_bio(bbio, false); + return btrfs_csum_one_bio(bbio, &bbio->bio, false); #endif } @@ -759,6 +859,7 @@ static bool btrfs_submit_chunk(struct btrfs_bio *bbio, int mirror_num) u64 logical = bio->bi_iter.bi_sector << SECTOR_SHIFT; u64 length = bio->bi_iter.bi_size; u64 map_length = length; + u64 max_bio_len = length; struct btrfs_io_context *bioc = NULL; struct btrfs_io_stripe smap; blk_status_t status; @@ -770,6 +871,31 @@ static bool btrfs_submit_chunk(struct btrfs_bio *bbio, int mirror_num) smap.rst_search_commit_root = false; btrfs_bio_counter_inc_blocked(fs_info); + + /* + * The blk-crypto-fallback limits bio sizes to 256 segments, because it + * has no way of controlling the pages it gets for the bounce bio it + * submits. + * + * If we don't pre-split our bio blk-crypto-fallback will do it for us, + * and then call into our checksum callback with a random cloned bio + * that isn't a btrfs_bio. + * + * To account for this we must truncate the bio ourselves, so we need to + * get our length at 256 segments and return that. We must do this + * before btrfs_map_block because the RAID5/6 code relies on having + * properly stripe aligned things, so we return the truncated length + * here so that btrfs_map_block() does the correct thing. Further down + * we actually truncate map_length to map_bio_len because map_length + * will be set to whatever the mapping length is for the underlying + * geometry. This will work properly with RAID5/6 as it will have + * already setup everything for the expected length, and everything else + * can handle with a truncated map_length. + */ + if (bio_has_crypt_ctx(bio)) + max_bio_len = btrfs_fscrypt_bio_length(bio, map_length); + + map_length = max_bio_len; ret = btrfs_map_block(fs_info, btrfs_op(bio), logical, &map_length, &bioc, &smap, &mirror_num); if (ret) { @@ -788,7 +914,7 @@ static bool btrfs_submit_chunk(struct btrfs_bio *bbio, int mirror_num) bbio->can_use_append = btrfs_use_zone_append(bbio); - map_length = min(map_length, length); + map_length = min(map_length, max_bio_len); if (bbio->can_use_append) map_length = btrfs_append_map_length(bbio, map_length); @@ -832,10 +958,13 @@ static bool btrfs_submit_chunk(struct btrfs_bio *bbio, int mirror_num) /* * Csum items for reloc roots have already been cloned at this * point, so they are handled as part of the no-checksum case. + * + * Encrypted inodes are csum'ed via the ->process_bio callback. */ if (!(inode->flags & BTRFS_INODE_NODATASUM) && !test_bit(BTRFS_FS_STATE_NO_DATA_CSUMS, &fs_info->fs_state) && - !btrfs_is_data_reloc_root(inode->root) && !bbio->is_remap) { + !btrfs_is_data_reloc_root(inode->root) && !bbio->is_remap && + !IS_ENCRYPTED(&inode->vfs_inode)) { if (should_async_write(bbio) && btrfs_wq_submit_bio(bbio, bioc, &smap, mirror_num)) goto done; diff --git a/fs/btrfs/bio.h b/fs/btrfs/bio.h index 303ed6c7103d..7a8ff4378cba 100644 --- a/fs/btrfs/bio.h +++ b/fs/btrfs/bio.h @@ -15,6 +15,7 @@ struct btrfs_bio; struct btrfs_fs_info; struct btrfs_inode; +struct fscrypt_extent_info; #define BTRFS_BIO_INLINE_CSUM_SIZE 64 @@ -38,12 +39,20 @@ struct btrfs_bio { union { /* * For data reads: checksumming and original I/O information. - * (for internal use in the btrfs_submit_bbio() machinery only) + * (for internal use in the btrfs_submit_bbio() machinery only). + * + * The fscrypt context is used for read repair, this is the + * only thing not internal to btrfs_submit_bbio() machinery. */ struct { u8 *csum; u8 csum_inline[BTRFS_BIO_INLINE_CSUM_SIZE]; + bool csum_ok; struct bvec_iter saved_iter; + + /* Used for read repair. */ + struct fscrypt_extent_info *fscrypt_info; + u64 orig_start; }; /* @@ -59,6 +68,7 @@ struct btrfs_bio { struct btrfs_ordered_sum *sums; struct work_struct csum_work; struct completion csum_done; + struct bio *csum_bio; struct bvec_iter csum_saved_iter; u64 orig_physical; u64 orig_logical; @@ -129,5 +139,7 @@ void btrfs_submit_repair_write(struct btrfs_bio *bbio, int mirror_num, bool dev_ int btrfs_repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 fileoff, u32 length, u64 logical, const phys_addr_t paddrs[], unsigned int step, int mirror_num); +blk_status_t btrfs_check_encrypted_read_bio(struct btrfs_bio *bbio, + struct bio *enc_bio); #endif diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index 55c272fe5d92..9499e15609b3 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -339,6 +339,10 @@ struct btrfs_inode { struct rw_semaphore i_mmap_lock; +#ifdef CONFIG_FS_ENCRYPTION + struct fscrypt_inode_info *i_crypt_info; +#endif + struct inode vfs_inode; }; @@ -556,7 +560,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry); int btrfs_set_inode_index(struct btrfs_inode *dir, u64 *index); int btrfs_unlink_inode(struct btrfs_trans_handle *trans, struct btrfs_inode *dir, struct btrfs_inode *inode, - const struct fscrypt_str *name); + struct fscrypt_name *name); int btrfs_add_link(struct btrfs_trans_handle *trans, struct btrfs_inode *parent_inode, struct btrfs_inode *inode, const struct fscrypt_str *name, bool add_backref, u64 index); @@ -582,6 +586,7 @@ struct btrfs_new_inode_args { struct posix_acl *default_acl; struct posix_acl *acl; struct fscrypt_name fname; + bool encrypt; }; int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args, diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index b2393a48a8fe..8269258d0dc4 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -33,6 +33,7 @@ #include "subpage.h" #include "messages.h" #include "super.h" +#include "fscrypt.h" static struct bio_set btrfs_compressed_bioset; @@ -333,6 +334,8 @@ void btrfs_submit_compressed_write(struct btrfs_ordered_extent *ordered, cb->bbio.bio.bi_iter.bi_sector = ordered->disk_bytenr >> SECTOR_SHIFT; cb->bbio.ordered = ordered; + fscrypt_set_bio_crypt_ctx_from_extent(&cb->bbio.bio, ordered->fscrypt_info, 0, GFP_NOFS); + btrfs_submit_bbio(&cb->bbio, 0); } @@ -589,7 +592,10 @@ void btrfs_submit_compressed_read(struct btrfs_bio *bbio) cb->compress_type = btrfs_extent_map_compression(em); cb->orig_bbio = bbio; cb->bbio.csum_search_commit_root = bbio->csum_search_commit_root; + cb->bbio.fscrypt_info = fscrypt_get_extent_info(em->fscrypt_info); + cb->bbio.orig_start = 0; + fscrypt_set_bio_crypt_ctx_from_extent(&cb->bbio.bio, em->fscrypt_info, 0, GFP_NOFS); btrfs_free_extent_map(em); for (int i = 0; i * min_folio_size < compressed_len; i++) { diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 6de7ad191e04..89d3c3137786 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -400,6 +400,9 @@ struct btrfs_replace_extent_info { u64 file_offset; /* Pointer to a file extent item of type regular or prealloc. */ char *extent_buf; + /* The fscrypt_extent_info for a new extent. */ + u8 *fscrypt_ctx; + u32 fscrypt_context_size; /* * Set to true when attempting to replace a file range with a new extent * described by this structure, set to false when attempting to clone an diff --git a/fs/btrfs/defrag.c b/fs/btrfs/defrag.c index 7e2db5d3a4d4..652b8423f01e 100644 --- a/fs/btrfs/defrag.c +++ b/fs/btrfs/defrag.c @@ -16,6 +16,7 @@ #include "file-item.h" #include "super.h" #include "compression.h" +#include "fscrypt.h" static struct kmem_cache *btrfs_inode_defrag_cachep; @@ -125,6 +126,14 @@ void btrfs_add_inode_defrag(struct btrfs_inode *inode, u32 extent_thresh) if (!need_auto_defrag(fs_info)) return; + /* + * Since we have to read the inode at defrag time disable auto defrag + * for encrypted inodes until we have code to read the parent and load + * the encryption context. + */ + if (IS_ENCRYPTED(&inode->vfs_inode)) + return; + if (test_bit(BTRFS_INODE_IN_DEFRAG, &inode->runtime_flags)) return; @@ -720,6 +729,11 @@ static struct extent_map *defrag_get_extent(struct btrfs_inode *inode, if (ret > 0) goto not_found; } + btrfs_release_path(&path); + + ret = btrfs_fscrypt_load_extent_info(inode, &path, &key, em); + if (ret) + goto err; return em; not_found: diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c index 09795439b9fb..097fe6ec60ba 100644 --- a/fs/btrfs/delayed-inode.c +++ b/fs/btrfs/delayed-inode.c @@ -1784,7 +1784,9 @@ bool btrfs_should_delete_dir_index(const struct list_head *del_list, u64 index) /* * Read dir info stored in the delayed tree. */ -bool btrfs_readdir_delayed_dir_index(struct dir_context *ctx, +bool btrfs_readdir_delayed_dir_index(const struct inode *inode, + struct fscrypt_str *fstr, + struct dir_context *ctx, const struct list_head *ins_list) { struct btrfs_dir_item *di; @@ -1793,6 +1795,7 @@ bool btrfs_readdir_delayed_dir_index(struct dir_context *ctx, char *name; int name_len; unsigned char d_type; + size_t fstr_len = fstr->len; /* * Changing the data of the delayed item is impossible. So @@ -1819,7 +1822,25 @@ bool btrfs_readdir_delayed_dir_index(struct dir_context *ctx, d_type = fs_ftype_to_dtype(btrfs_dir_flags_to_ftype(di->type)); btrfs_disk_key_to_cpu(&location, &di->location); - over = !dir_emit(ctx, name, name_len, location.objectid, d_type); + if (di->type & BTRFS_FT_ENCRYPTED) { + int ret; + struct fscrypt_str iname = FSTR_INIT(name, name_len); + + fstr->len = fstr_len; + /* + * The hash is only used when the encryption key is not + * available. But if we have delayed insertions, then we + * must have the encryption key available or we wouldn't + * have been able to create entries in the directory. + * So, we don't calculate the hash. + */ + ret = fscrypt_fname_disk_to_usr(inode, 0, 0, &iname, fstr); + if (ret) + return true; + over = !dir_emit(ctx, fstr->name, fstr->len, location.objectid, d_type); + } else { + over = !dir_emit(ctx, name, name_len, location.objectid, d_type); + } if (refcount_dec_and_test(&curr->refs)) kfree(curr); diff --git a/fs/btrfs/delayed-inode.h b/fs/btrfs/delayed-inode.h index fc752863f89b..9bff11478632 100644 --- a/fs/btrfs/delayed-inode.h +++ b/fs/btrfs/delayed-inode.h @@ -19,6 +19,7 @@ #include #include "ctree.h" +struct fscrypt_str; struct btrfs_disk_key; struct btrfs_fs_info; struct btrfs_inode; @@ -159,7 +160,9 @@ void btrfs_readdir_put_delayed_items(struct btrfs_inode *inode, struct list_head *ins_list, struct list_head *del_list); bool btrfs_should_delete_dir_index(const struct list_head *del_list, u64 index); -bool btrfs_readdir_delayed_dir_index(struct dir_context *ctx, +bool btrfs_readdir_delayed_dir_index(const struct inode *inode, + struct fscrypt_str *fstr, + struct dir_context *ctx, const struct list_head *ins_list); /* Used during directory logging. */ diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c index 84f1c64423d3..09ce1b85af74 100644 --- a/fs/btrfs/dir-item.c +++ b/fs/btrfs/dir-item.c @@ -6,6 +6,7 @@ #include "messages.h" #include "ctree.h" #include "disk-io.h" +#include "fscrypt.h" #include "transaction.h" #include "accessors.h" #include "dir-item.h" @@ -227,6 +228,81 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans, return di; } +/* + * If appropriate, populate the disk name for a fscrypt_name looked up without + * a key. + * + * @path: The path to the extent buffer in which the name was found. + * @di: The dir item corresponding. + * @fname: The fscrypt_name to perhaps populate. + * + * Returns: 0 if the name is already populated or the dir item doesn't exist + * or the name was successfully populated, else an error code. + */ +static int ensure_disk_name_from_dir_item(struct btrfs_path *path, + struct btrfs_dir_item *di, + struct fscrypt_name *name) +{ + int ret; + + if (name->disk_name.name || !di) + return 0; + + ret = btrfs_fscrypt_get_disk_name(path->nodes[0], di, &name->disk_name); + if (ret) + return ret; + + name->crypto_buf.name = name->disk_name.name; + name->crypto_buf.len = name->disk_name.len; + return 0; +} + +/* + * Lookup for a directory item by fscrypt_name. + * + * @trans: The transaction handle to use. + * @root: The root of the target tree. + * @path: Path to use for the search. + * @dir: The inode number (objectid) of the directory. + * @name: The fscrypt_name associated to the directory entry + * @mod: Used to indicate if the tree search is meant for a read only + * lookup or for a deletion lookup, so its value should be 0 or + * -1, respectively. + * + * Returns: NULL if the dir item does not exists, an error pointer if an error + * happened, or a pointer to a dir item if a dir item exists for the given name. + */ +struct btrfs_dir_item *btrfs_lookup_dir_item_fname(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, u64 dir, + struct fscrypt_name *name, int mod) +{ + struct btrfs_key key; + struct btrfs_dir_item *di = NULL; + int ret = 0; + + key.objectid = dir; + key.type = BTRFS_DIR_ITEM_KEY; + + if (!name->disk_name.name) + key.offset = name->hash | ((u64)name->minor_hash << 32); + else + key.offset = btrfs_name_hash(name->disk_name.name, name->disk_name.len); + + ret = btrfs_search_slot(trans, root, &key, path, mod, -mod); + if (ret == 0) + di = btrfs_match_dir_item_fname(path, name); + + if (ret == -ENOENT || (di && IS_ERR(di) && PTR_ERR(di) == -ENOENT)) + return NULL; + if (ret == 0) + ret = ensure_disk_name_from_dir_item(path, di, name); + if (ret < 0) + di = ERR_PTR(ret); + + return di; +} + int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir_ino, const struct fscrypt_str *name) { @@ -276,9 +352,9 @@ int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir_ino, } /* - * Lookup for a directory index item by name and index number. + * Lookup for a directory index item by fscrypt_name and index number. * - * @trans: The transaction handle to use. Can be NULL if @mod is 0. + * @trans: The transaction handle to use. * @root: The root of the target tree. * @path: Path to use for the search. * @dir: The inode number (objectid) of the directory. @@ -316,7 +392,7 @@ btrfs_lookup_dir_index_item(struct btrfs_trans_handle *trans, struct btrfs_dir_item * btrfs_search_dir_index_item(struct btrfs_root *root, struct btrfs_path *path, - u64 dirid, const struct fscrypt_str *name) + u64 dirid, struct fscrypt_name *name) { struct btrfs_dir_item *di; struct btrfs_key key; @@ -330,7 +406,11 @@ btrfs_search_dir_index_item(struct btrfs_root *root, struct btrfs_path *path, if (key.objectid != dirid || key.type != BTRFS_DIR_INDEX_KEY) break; - di = btrfs_match_dir_item_name(path, name->name, name->len); + di = btrfs_match_dir_item_fname(path, name); + if (di) + ret = ensure_disk_name_from_dir_item(path, di, name); + if (ret) + break; if (di) return di; } @@ -366,8 +446,8 @@ struct btrfs_dir_item *btrfs_lookup_xattr(struct btrfs_trans_handle *trans, * this walks through all the entries in a dir item and finds one * for a specific name. */ -struct btrfs_dir_item *btrfs_match_dir_item_name(const struct btrfs_path *path, - const char *name, int name_len) +struct btrfs_dir_item *btrfs_match_dir_item_fname(const struct btrfs_path *path, + struct fscrypt_name *name) { struct btrfs_dir_item *dir_item; unsigned long name_ptr; @@ -386,8 +466,8 @@ struct btrfs_dir_item *btrfs_match_dir_item_name(const struct btrfs_path *path, btrfs_dir_data_len(leaf, dir_item); name_ptr = (unsigned long)(dir_item + 1); - if (btrfs_dir_name_len(leaf, dir_item) == name_len && - memcmp_extent_buffer(leaf, name, name_ptr, name_len) == 0) + if (btrfs_fscrypt_match_name(name, leaf, name_ptr, + btrfs_dir_name_len(leaf, dir_item))) return dir_item; cur += this_len; @@ -397,6 +477,20 @@ struct btrfs_dir_item *btrfs_match_dir_item_name(const struct btrfs_path *path, return NULL; } +/* + * helper function to look at the directory item pointed to by 'path' + * this walks through all the entries in a dir item and finds one + * for a specific name. + */ +struct btrfs_dir_item *btrfs_match_dir_item_name(const struct btrfs_path *path, + const char *name, int name_len) +{ + struct fscrypt_name fname = { + .disk_name = FSTR_INIT((char *) name, name_len) + }; + return btrfs_match_dir_item_fname(path, &fname); +} + /* * given a pointer into a directory item, delete it. This * handles items that have more than one entry in them. diff --git a/fs/btrfs/dir-item.h b/fs/btrfs/dir-item.h index e52174a8baf9..f89d12ee28a7 100644 --- a/fs/btrfs/dir-item.h +++ b/fs/btrfs/dir-item.h @@ -7,6 +7,7 @@ #include struct fscrypt_str; +struct fscrypt_name; struct btrfs_fs_info; struct btrfs_key; struct btrfs_path; @@ -23,6 +24,11 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, u64 dir, const struct fscrypt_str *name, int mod); +struct btrfs_dir_item *btrfs_lookup_dir_item_fname( + struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, u64 dir, + struct fscrypt_name *name, int mod); struct btrfs_dir_item *btrfs_lookup_dir_index_item( struct btrfs_trans_handle *trans, struct btrfs_root *root, @@ -30,7 +36,7 @@ struct btrfs_dir_item *btrfs_lookup_dir_index_item( u64 index, const struct fscrypt_str *name, int mod); struct btrfs_dir_item *btrfs_search_dir_index_item(struct btrfs_root *root, struct btrfs_path *path, u64 dirid, - const struct fscrypt_str *name); + struct fscrypt_name *name); int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, @@ -48,6 +54,8 @@ struct btrfs_dir_item *btrfs_lookup_xattr(struct btrfs_trans_handle *trans, struct btrfs_dir_item *btrfs_match_dir_item_name(const struct btrfs_path *path, const char *name, int name_len); +struct btrfs_dir_item *btrfs_match_dir_item_fname(const struct btrfs_path *path, + struct fscrypt_name *name); static inline u64 btrfs_name_hash(const char *name, int len) { diff --git a/fs/btrfs/direct-io.c b/fs/btrfs/direct-io.c index 57167d56dc72..bef4e2f953e4 100644 --- a/fs/btrfs/direct-io.c +++ b/fs/btrfs/direct-io.c @@ -12,11 +12,14 @@ #include "volumes.h" #include "bio.h" #include "ordered-data.h" +#include "fscrypt.h" struct btrfs_dio_data { ssize_t submitted; struct extent_changeset *data_reserved; struct btrfs_ordered_extent *ordered; + struct fscrypt_extent_info *fscrypt_info; + u64 orig_start; bool data_space_reserved; bool nocow_done; }; @@ -140,7 +143,7 @@ static int lock_extent_direct(struct inode *inode, u64 lockstart, u64 lockend, static struct extent_map *btrfs_create_dio_extent(struct btrfs_inode *inode, struct btrfs_dio_data *dio_data, const u64 start, - const struct btrfs_file_extent *file_extent, + struct btrfs_file_extent *file_extent, const int type) { struct extent_map *em = NULL; @@ -150,8 +153,10 @@ static struct extent_map *btrfs_create_dio_extent(struct btrfs_inode *inode, em = btrfs_create_io_em(inode, start, file_extent, type); if (IS_ERR(em)) goto out; + file_extent->fscrypt_info = em->fscrypt_info; } + file_extent->orig_offset = start - file_extent->offset; ordered = btrfs_alloc_ordered_extent(inode, start, file_extent, (1U << type) | (1U << BTRFS_ORDERED_DIRECT)); @@ -202,6 +207,7 @@ static struct extent_map *btrfs_new_extent_direct(struct btrfs_inode *inode, file_extent.ram_bytes = ins.offset; file_extent.offset = 0; file_extent.compression = BTRFS_COMPRESS_NONE; + file_extent.fscrypt_info = NULL; em = btrfs_create_dio_extent(inode, dio_data, start, &file_extent, BTRFS_ORDERED_REGULAR); btrfs_dec_block_group_reservations(fs_info, ins.objectid); @@ -275,6 +281,7 @@ static int btrfs_get_blocks_direct_write(struct extent_map **map, } space_reserved = true; + file_extent.fscrypt_info = em->fscrypt_info; em2 = btrfs_create_dio_extent(BTRFS_I(inode), dio_data, start, &file_extent, type); btrfs_dec_nocow_writers(bg); @@ -547,6 +554,9 @@ static int btrfs_dio_iomap_begin(struct inode *inode, loff_t start, release_offset, release_len); } + } else { + dio_data->fscrypt_info = fscrypt_get_extent_info(em->fscrypt_info); + dio_data->orig_start = em->start - em->offset; } /* @@ -637,6 +647,11 @@ static int btrfs_dio_iomap_end(struct inode *inode, loff_t pos, loff_t length, dio_data->ordered = NULL; } + if (dio_data->fscrypt_info) { + fscrypt_put_extent_info(dio_data->fscrypt_info); + dio_data->fscrypt_info = NULL; + } + if (write) extent_changeset_free(dio_data->data_reserved); return ret; @@ -713,6 +728,8 @@ static void btrfs_dio_submit_io(const struct iomap_iter *iter, struct bio *bio, struct btrfs_dio_private *dip = container_of(bbio, struct btrfs_dio_private, bbio); struct btrfs_dio_data *dio_data = iter->private; + struct fscrypt_extent_info *fscrypt_info = NULL; + u64 offset = 0; btrfs_bio_init(bbio, BTRFS_I(iter->inode), file_offset, btrfs_dio_end_io, bio->bi_private); @@ -732,6 +749,9 @@ static void btrfs_dio_submit_io(const struct iomap_iter *iter, struct bio *bio, if (iter->flags & IOMAP_WRITE) { int ret; + fscrypt_info = dio_data->ordered->fscrypt_info; + offset = file_offset - dio_data->ordered->orig_offset; + ret = btrfs_extract_ordered_extent(bbio, dio_data->ordered); if (ret) { btrfs_finish_ordered_extent(dio_data->ordered, @@ -741,8 +761,14 @@ static void btrfs_dio_submit_io(const struct iomap_iter *iter, struct bio *bio, iomap_dio_bio_end_io(bio); return; } + } else { + fscrypt_info = dio_data->fscrypt_info; + offset = file_offset - dio_data->orig_start; + bbio->fscrypt_info = fscrypt_get_extent_info(fscrypt_info); + bbio->orig_start = dio_data->orig_start; } + fscrypt_set_bio_crypt_ctx_from_extent(&bbio->bio, fscrypt_info, offset, GFP_NOFS); btrfs_submit_bbio(bbio, 0); } diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 8a11be02eeb9..4bfd851e1e9b 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1230,6 +1230,7 @@ void btrfs_free_fs_info(struct btrfs_fs_info *fs_info) btrfs_extent_buffer_leak_debug_check(fs_info); kfree(fs_info->super_copy); kfree(fs_info->super_for_commit); + fscrypt_free_dummy_policy(&fs_info->dummy_enc_policy); kvfree(fs_info); } @@ -3411,7 +3412,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device * Handle the space caching options appropriately now that we have the * super block loaded and validated. */ - btrfs_set_free_space_cache_settings(fs_info); + btrfs_set_free_space_cache_settings(fs_info, &fs_info->mount_opt); if (!btrfs_check_options(fs_info, &fs_info->mount_opt, sb->s_flags)) { ret = -EINVAL; diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 2275189b7860..32e39837294b 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -35,6 +35,7 @@ #include "dev-replace.h" #include "super.h" #include "transaction.h" +#include "fscrypt.h" static struct kmem_cache *extent_buffer_cache; @@ -150,6 +151,10 @@ struct btrfs_bio_ctrl { * inside the same inode. */ u64 last_em_start; + + /* This is set for reads and we have encryption. */ + struct fscrypt_extent_info *fscrypt_info; + u64 orig_start; }; /* @@ -710,9 +715,28 @@ static int alloc_eb_folio_array(struct extent_buffer *eb, bool nofail) static bool btrfs_bio_is_contig(struct btrfs_bio_ctrl *bio_ctrl, u64 disk_bytenr, loff_t file_offset) { - struct bio *bio = &bio_ctrl->bbio->bio; + struct btrfs_bio *bbio = bio_ctrl->bbio; + struct bio *bio = &bbio->bio; + struct inode *inode = &bbio->inode->vfs_inode; const sector_t sector = disk_bytenr >> SECTOR_SHIFT; + if (IS_ENCRYPTED(inode)) { + u64 offset = 0; + struct fscrypt_extent_info *fscrypt_info = NULL; + + /* bio_ctrl->fscrypt_info is only set in the READ case. */ + if (bio_ctrl->fscrypt_info) { + fscrypt_info = bio_ctrl->fscrypt_info; + offset = file_offset - bio_ctrl->orig_start; + } else if (bbio->ordered) { + fscrypt_info = bbio->ordered->fscrypt_info; + offset = file_offset - bbio->ordered->orig_offset; + } + + if (!fscrypt_mergeable_extent_bio(bio, fscrypt_info, offset)) + return false; + } + if (bio_ctrl->compress_type != BTRFS_COMPRESS_NONE) { /* * For compression, all IO should have its logical bytenr set @@ -735,6 +759,8 @@ static void alloc_new_bio(struct btrfs_inode *inode, { struct btrfs_fs_info *fs_info = inode->root->fs_info; struct btrfs_bio *bbio; + struct fscrypt_extent_info *fscrypt_info = NULL; + u64 offset = 0; bbio = btrfs_bio_alloc(BIO_MAX_VECS, bio_ctrl->opf, inode, file_offset, bio_ctrl->end_io_func, NULL); @@ -754,6 +780,8 @@ static void alloc_new_bio(struct btrfs_inode *inode, ordered->file_offset + ordered->disk_num_bytes - file_offset); bbio->ordered = ordered; + fscrypt_info = ordered->fscrypt_info; + offset = file_offset - ordered->orig_offset; } /* @@ -764,7 +792,14 @@ static void alloc_new_bio(struct btrfs_inode *inode, */ bio_set_dev(&bbio->bio, fs_info->fs_devices->latest_dev->bdev); wbc_init_bio(bio_ctrl->wbc, &bbio->bio); + } else { + fscrypt_info = bio_ctrl->fscrypt_info; + offset = file_offset - bio_ctrl->orig_start; + bbio->fscrypt_info = fscrypt_get_extent_info(fscrypt_info); + bbio->orig_start = bio_ctrl->orig_start; } + + fscrypt_set_bio_crypt_ctx_from_extent(&bbio->bio, fscrypt_info, offset, GFP_NOFS); } /* @@ -810,6 +845,19 @@ static void submit_extent_folio(struct btrfs_bio_ctrl *bio_ctrl, len = bio_ctrl->len_to_oe_boundary; } + /* + * Encryption has to allocate bounce buffers to encrypt the bio, + * and we need to make sure that it doesn't split the bio so we + * retain all of our special info in the btrfs_bio, so submit + * any bio that gets up to BIO_MAX_VECS worth of segments. + */ + if (IS_ENCRYPTED(&inode->vfs_inode) && + bio_data_dir(&bio_ctrl->bbio->bio) == WRITE && + bio_segments(&bio_ctrl->bbio->bio) == BIO_MAX_VECS) { + submit_one_bio(bio_ctrl); + continue; + } + if (!bio_add_folio(&bio_ctrl->bbio->bio, folio, len, pg_offset)) { /* bio full: move on to a new one */ submit_one_bio(bio_ctrl); @@ -1031,6 +1079,8 @@ static int btrfs_do_readpage(struct folio *folio, struct extent_map **em_cached, u64 block_start; u64 em_gen; + bio_ctrl->fscrypt_info = NULL; + ASSERT(IS_ALIGNED(cur, fs_info->sectorsize)); if (cur >= last_byte) { folio_zero_range(folio, pg_offset, end - cur + 1); @@ -1120,6 +1170,21 @@ static int btrfs_do_readpage(struct folio *folio, struct extent_map **em_cached, bio_ctrl->last_em_start = em->start; + /* + * We use the extent offset for the IV when decrypting the page, + * so we have to set the extent_offset based on the orig_start + * for this extent. Also save the fscrypt_info so the bio ctx + * can be set properly. If this inode isn't encrypted this + * won't do anything. + * + * If we're compressed we'll handle all of this in + * btrfs_submit_compressed_read. + */ + if (compress_type == BTRFS_COMPRESS_NONE) { + bio_ctrl->orig_start = em->start - em->offset; + bio_ctrl->fscrypt_info = fscrypt_get_extent_info(em->fscrypt_info); + } + em_gen = em->generation; btrfs_free_extent_map(em); em = NULL; @@ -1128,11 +1193,17 @@ static int btrfs_do_readpage(struct folio *folio, struct extent_map **em_cached, if (block_start == EXTENT_MAP_HOLE) { folio_zero_range(folio, pg_offset, blocksize); end_folio_read(vi, folio, true, cur, blocksize); + + /* This shouldn't be set, but clear it just in case. */ + fscrypt_put_extent_info(bio_ctrl->fscrypt_info); continue; } /* the get_extent function already copied into the folio */ if (block_start == EXTENT_MAP_INLINE) { end_folio_read(vi, folio, true, cur, blocksize); + + /* This shouldn't be set, but clear it just in case. */ + fscrypt_put_extent_info(bio_ctrl->fscrypt_info); continue; } @@ -1145,6 +1216,7 @@ static int btrfs_do_readpage(struct folio *folio, struct extent_map **em_cached, submit_one_bio(bio_ctrl); submit_extent_folio(bio_ctrl, disk_bytenr, folio, blocksize, pg_offset, em_gen); + fscrypt_put_extent_info(bio_ctrl->fscrypt_info); } return 0; } @@ -4160,6 +4232,47 @@ static void assert_eb_folio_uptodate(const struct extent_buffer *eb, int i) } } +/* Take a sha256 of a portion of an extent buffer. */ +void extent_buffer_sha256(const struct extent_buffer *eb, + unsigned long start, + unsigned long len, u8 *out) +{ + size_t cur; + size_t offset; + char *kaddr; + unsigned long i = get_eb_folio_index(eb, start); + struct sha256_ctx sctx; + + if (check_eb_range(eb, start, len)) + return; + + if (eb->addr) { + sha256(eb->addr + start, len, out); + return; + } + + offset = get_eb_offset_in_folio(eb, start); + + /* + * TODO: This should maybe be using the crypto API, not the fallback, + * but fscrypt uses the fallback and this is only used in emulation of + * fscrypt's buffer sha256 method. + */ + sha256_init(&sctx); + while (len > 0) { + assert_eb_folio_uptodate(eb, i); + + cur = min(len, PAGE_SIZE - offset); + kaddr = folio_address(eb->folios[i]); + sha256_update(&sctx, (u8 *)(kaddr + offset), cur); + + len -= cur; + offset = 0; + i++; + } + sha256_final(&sctx, out); +} + static void __write_extent_buffer(const struct extent_buffer *eb, const void *srcv, unsigned long start, unsigned long len, bool use_memmove) diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index b310a5145cf6..8c7da2962cd4 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -329,6 +329,9 @@ static inline bool extent_buffer_uptodate(const struct extent_buffer *eb) int memcmp_extent_buffer(const struct extent_buffer *eb, const void *ptrv, unsigned long start, unsigned long len); +void extent_buffer_sha256(const struct extent_buffer *eb, + unsigned long start, + unsigned long len, u8 *out); void read_extent_buffer(const struct extent_buffer *eb, void *dst, unsigned long start, unsigned long len); diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c index 9284c0a81bef..d13fb42dafab 100644 --- a/fs/btrfs/extent_map.c +++ b/fs/btrfs/extent_map.c @@ -34,7 +34,9 @@ void __cold btrfs_extent_map_exit(void) void btrfs_extent_map_tree_init(struct extent_map_tree *tree) { tree->root = RB_ROOT; + tree->flags = 0; INIT_LIST_HEAD(&tree->modified_extents); + INIT_LIST_HEAD(&tree->freed_extents); rwlock_init(&tree->lock); } @@ -51,9 +53,16 @@ struct extent_map *btrfs_alloc_extent_map(void) RB_CLEAR_NODE(&em->rb_node); refcount_set(&em->refs, 1); INIT_LIST_HEAD(&em->list); + INIT_LIST_HEAD(&em->free_list); return em; } +static void free_extent_map(struct extent_map *em) +{ + fscrypt_put_extent_info(em->fscrypt_info); + kmem_cache_free(extent_map_cache, em); +} + /* * Drop the reference out on @em by one and free the structure if the reference * count hits zero. @@ -65,10 +74,81 @@ void btrfs_free_extent_map(struct extent_map *em) if (refcount_dec_and_test(&em->refs)) { WARN_ON(btrfs_extent_map_in_tree(em)); WARN_ON(!list_empty(&em->list)); - kmem_cache_free(extent_map_cache, em); + free_extent_map(em); } } +/* + * Drop a ref for the extent map in the given tree. + * + * @tree: tree that the em is a part of. + * @em: the em to drop the reference to. + * + * Drop the reference count on @em by one, if the reference count hits 0 and + * there is an object on the em that can't be safely freed in the current + * context (if we are holding the extent_map_tree->lock for example), then add + * it to the freed_extents list on the extent_map_tree for later processing. + * + * This must be followed by a btrfs_free_pending_extent_maps() to clear + * the pending frees. + */ +void btrfs_free_extent_map_safe(struct extent_map_tree *tree, + struct extent_map *em) +{ + lockdep_assert_held_write(&tree->lock); + + if (!em) + return; + + if (!refcount_dec_and_test(&em->refs)) + return; + + WARN_ON(btrfs_extent_map_in_tree(em)); + WARN_ON(!list_empty(&em->list)); + + /* + * We could take a lock freeing the fscrypt_info, so add this to the + * list of freed_extents to be freed later. + */ + if (em->fscrypt_info) { + list_add_tail(&em->free_list, &tree->freed_extents); + set_bit(EXTENT_MAP_TREE_PENDING_FREES, &tree->flags); + return; + } + + /* Nothing scary here, just free the object. */ + free_extent_map(em); +} + +/* + * Free the em objects that exist on the em tree + * + * @tree: the tree to free the objects from. + * + * If there are any objects on the em->freed_extents list go ahead and + * free them here in a safe way. This is to be coupled with any uses of + * btrfs_free_extent_map_safe(). + */ +void btrfs_free_pending_extent_maps(struct extent_map_tree *tree) +{ + struct extent_map *em; + + /* Avoid taking the write lock if we don't have any pending frees. */ + if (!test_and_clear_bit(EXTENT_MAP_TREE_PENDING_FREES, &tree->flags)) + return; + + write_lock(&tree->lock); + while ((em = list_first_entry_or_null(&tree->freed_extents, + struct extent_map, free_list))) { + list_del_init(&em->free_list); + write_unlock(&tree->lock); + free_extent_map(em); + cond_resched(); + write_lock(&tree->lock); + } + write_unlock(&tree->lock); +} + /* Do the math around the end of an extent, handling wrapping. */ static u64 range_end(u64 start, u64 len) { @@ -224,6 +304,10 @@ static bool can_merge_extent_map(const struct extent_map *em) if (!list_empty(&em->list)) return false; + /* We can't merge encrypted extents. */ + if (em->fscrypt_info) + return false; + return true; } @@ -244,6 +328,10 @@ static bool mergeable_maps(const struct extent_map *prev, const struct extent_ma if (next->disk_bytenr < EXTENT_MAP_LAST_BYTE - 1) return btrfs_extent_map_block_start(next) == extent_map_block_end(prev); + /* Don't merge adjacent encrypted maps. */ + if (prev->fscrypt_info || next->fscrypt_info) + return false; + /* HOLES and INLINE extents. */ return next->disk_bytenr == prev->disk_bytenr; } @@ -784,7 +872,7 @@ static void drop_all_extent_maps_fast(struct btrfs_inode *inode) em = rb_entry(node, struct extent_map, rb_node); em->flags &= ~(EXTENT_FLAG_PINNED | EXTENT_FLAG_LOGGING); btrfs_remove_extent_mapping(inode, em); - btrfs_free_extent_map(em); + btrfs_free_extent_map_safe(tree, em); if (cond_resched_rwlock_write(&tree->lock)) node = rb_first(&tree->root); @@ -792,6 +880,8 @@ static void drop_all_extent_maps_fast(struct btrfs_inode *inode) node = next; } write_unlock(&tree->lock); + + btrfs_free_pending_extent_maps(tree); } /* @@ -908,6 +998,7 @@ void btrfs_drop_extent_map_range(struct btrfs_inode *inode, u64 start, u64 end, split->generation = gen; split->flags = flags; + split->fscrypt_info = fscrypt_get_extent_info(em->fscrypt_info); replace_extent_mapping(inode, em, split, modified); btrfs_free_extent_map(split); split = split2; @@ -936,6 +1027,7 @@ void btrfs_drop_extent_map_range(struct btrfs_inode *inode, u64 start, u64 end, split->ram_bytes = split->len; } + split->fscrypt_info = fscrypt_get_extent_info(em->fscrypt_info); if (btrfs_extent_map_in_tree(em)) { replace_extent_mapping(inode, em, split, modified); } else { @@ -986,13 +1078,14 @@ void btrfs_drop_extent_map_range(struct btrfs_inode *inode, u64 start, u64 end, btrfs_free_extent_map(em); next: /* Once for us (for our lookup reference). */ - btrfs_free_extent_map(em); + btrfs_free_extent_map_safe(em_tree, em); em = next_em; } write_unlock(&em_tree->lock); + btrfs_free_pending_extent_maps(em_tree); btrfs_free_extent_map(split); btrfs_free_extent_map(split2); } @@ -1093,6 +1186,7 @@ int btrfs_split_extent_map(struct btrfs_inode *inode, u64 start, u64 len, u64 pr split_pre->ram_bytes = split_pre->len; split_pre->flags = flags; split_pre->generation = em->generation; + split_pre->fscrypt_info = fscrypt_get_extent_info(em->fscrypt_info); replace_extent_mapping(inode, em, split_pre, true); @@ -1110,6 +1204,8 @@ int btrfs_split_extent_map(struct btrfs_inode *inode, u64 start, u64 len, u64 pr split_mid->ram_bytes = split_mid->len; split_mid->flags = flags; split_mid->generation = em->generation; + split_mid->fscrypt_info = fscrypt_get_extent_info(em->fscrypt_info); + add_extent_mapping(inode, split_mid, true); /* Once for us */ diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h index 6f685f3c9327..a0d5be758e7e 100644 --- a/fs/btrfs/extent_map.h +++ b/fs/btrfs/extent_map.h @@ -24,6 +24,7 @@ enum { ENUM_BIT(EXTENT_FLAG_COMPRESS_ZLIB), ENUM_BIT(EXTENT_FLAG_COMPRESS_LZO), ENUM_BIT(EXTENT_FLAG_COMPRESS_ZSTD), + ENUM_BIT(EXTENT_FLAG_ENCRYPT_FSCRYPT), /* pre-allocated extent */ ENUM_BIT(EXTENT_FLAG_PREALLOC), /* Logging this extent */ @@ -96,17 +97,39 @@ struct extent_map { u64 generation; u32 flags; refcount_t refs; + struct fscrypt_extent_info *fscrypt_info; struct list_head list; + struct list_head free_list; +}; + +enum extent_map_flags { + EXTENT_MAP_TREE_PENDING_FREES, }; struct extent_map_tree { struct rb_root root; + unsigned long flags; struct list_head modified_extents; + struct list_head freed_extents; rwlock_t lock; }; struct btrfs_inode; +static inline void btrfs_extent_map_set_encryption(struct extent_map *em, + enum btrfs_encryption_type type) +{ + if (type == BTRFS_ENCRYPTION_FSCRYPT) + em->flags |= EXTENT_FLAG_ENCRYPT_FSCRYPT; +} + +static inline enum btrfs_encryption_type btrfs_extent_map_encryption(const struct extent_map *em) +{ + if (em->flags & EXTENT_FLAG_ENCRYPT_FSCRYPT) + return BTRFS_ENCRYPTION_FSCRYPT; + return BTRFS_ENCRYPTION_NONE; +} + static inline void btrfs_extent_map_set_compression(struct extent_map *em, enum btrfs_compression_type type) { @@ -175,6 +198,9 @@ int btrfs_split_extent_map(struct btrfs_inode *inode, u64 start, u64 len, u64 pr struct extent_map *btrfs_alloc_extent_map(void); void btrfs_free_extent_map(struct extent_map *em); +void btrfs_free_extent_map_safe(struct extent_map_tree *tree, + struct extent_map *em); +void btrfs_free_pending_extent_maps(struct extent_map_tree *tree); int __init btrfs_extent_map_init(void); void __cold btrfs_extent_map_exit(void); int btrfs_unpin_extent_cache(struct btrfs_inode *inode, u64 start, u64 len, u64 gen); diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index d72249390030..72d9d3243460 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -338,6 +338,14 @@ static int search_csum_tree(struct btrfs_fs_info *fs_info, return ret; } +static inline bool inode_skip_csum(struct btrfs_inode *inode) +{ + struct btrfs_fs_info *fs_info = inode->root->fs_info; + + return (inode->flags & BTRFS_INODE_NODATASUM) || + test_bit(BTRFS_FS_STATE_NO_DATA_CSUMS, &fs_info->fs_state); +} + /* * Lookup the checksum for the read bio in csum tree. * @@ -357,8 +365,7 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio) int ret = 0; u32 bio_offset = 0; - if ((inode->flags & BTRFS_INODE_NODATASUM) || - test_bit(BTRFS_FS_STATE_NO_DATA_CSUMS, &fs_info->fs_state)) + if (inode_skip_csum(inode)) return 0; /* @@ -771,11 +778,10 @@ int btrfs_lookup_csums_bitmap(struct btrfs_root *root, struct btrfs_path *path, return ret; } -static void csum_one_bio(struct btrfs_bio *bbio, struct bvec_iter *src) +static void csum_one_bio(struct btrfs_bio *bbio, struct bio *bio, struct bvec_iter *src) { struct btrfs_inode *inode = bbio->inode; struct btrfs_fs_info *fs_info = inode->root->fs_info; - struct bio *bio = &bbio->bio; struct btrfs_ordered_sum *sums = bbio->sums; struct bvec_iter iter = *src; phys_addr_t paddr; @@ -803,22 +809,24 @@ static void csum_one_bio_work(struct work_struct *work) ASSERT(btrfs_op(&bbio->bio) == BTRFS_MAP_WRITE); ASSERT(bbio->async_csum == true); - csum_one_bio(bbio, &bbio->csum_saved_iter); + csum_one_bio(bbio, bbio->csum_bio, &bbio->csum_saved_iter); complete(&bbio->csum_done); } /* * Calculate checksums of the data contained inside a bio. */ -int btrfs_csum_one_bio(struct btrfs_bio *bbio, bool async) +int btrfs_csum_one_bio(struct btrfs_bio *bbio, struct bio *bio, bool async) { struct btrfs_ordered_extent *ordered = bbio->ordered; struct btrfs_inode *inode = bbio->inode; struct btrfs_fs_info *fs_info = inode->root->fs_info; - struct bio *bio = &bbio->bio; struct btrfs_ordered_sum *sums; unsigned nofs_flag; + if (inode_skip_csum(inode)) + return 0; + nofs_flag = memalloc_nofs_save(); sums = kvzalloc(btrfs_ordered_sum_size(fs_info, bio->bi_iter.bi_size), GFP_KERNEL); @@ -834,12 +842,13 @@ int btrfs_csum_one_bio(struct btrfs_bio *bbio, bool async) btrfs_add_ordered_sum(ordered, sums); if (!async) { - csum_one_bio(bbio, &bbio->bio.bi_iter); + csum_one_bio(bbio, bio, &bio->bi_iter); return 0; } init_completion(&bbio->csum_done); bbio->async_csum = true; - bbio->csum_saved_iter = bbio->bio.bi_iter; + bbio->csum_bio = bio; + bbio->csum_saved_iter = bio->bi_iter; INIT_WORK(&bbio->csum_work, csum_one_bio_work); schedule_work(&bbio->csum_work); return 0; @@ -1364,6 +1373,7 @@ void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode, if (type == BTRFS_FILE_EXTENT_PREALLOC) em->flags |= EXTENT_FLAG_PREALLOC; } + btrfs_extent_map_set_encryption(em, btrfs_file_extent_encryption(leaf, fi)); } else if (type == BTRFS_FILE_EXTENT_INLINE) { /* Tree-checker has ensured this. */ ASSERT(extent_start == 0); diff --git a/fs/btrfs/file-item.h b/fs/btrfs/file-item.h index 6c678787c770..e170bac310ac 100644 --- a/fs/btrfs/file-item.h +++ b/fs/btrfs/file-item.h @@ -64,7 +64,7 @@ int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans, int btrfs_insert_data_csums(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_ordered_sum *sums); -int btrfs_csum_one_bio(struct btrfs_bio *bbio, bool async); +int btrfs_csum_one_bio(struct btrfs_bio *bbio, struct bio *bio, bool async); int btrfs_alloc_dummy_sum(struct btrfs_bio *bbio); int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end, struct list_head *list, int search_commit, diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index cf1cb5c4db75..b845556d2100 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -38,6 +38,7 @@ #include "file.h" #include "super.h" #include "print-tree.h" +#include "fscrypt.h" /* * Unlock folio after btrfs_file_write() is done with it. @@ -151,6 +152,7 @@ int btrfs_drop_extents(struct btrfs_trans_handle *trans, u64 extent_offset = 0; u64 extent_end = 0; u64 last_end = args->start; + u64 first_ctx = 1, last_ctx = 0; int del_nr = 0; int del_slot = 0; int extent_type; @@ -408,6 +410,12 @@ int btrfs_drop_extents(struct btrfs_trans_handle *trans, del_nr++; } + if (btrfs_file_extent_encryption(leaf, fi) == BTRFS_ENCRYPTION_FSCRYPT) { + if (first_ctx > last_ctx) + first_ctx = key.offset; + last_ctx = key.offset; + } + if (update_refs && extent_type == BTRFS_FILE_EXTENT_INLINE) { args->bytes_found += extent_end - key.offset; @@ -497,6 +505,64 @@ int btrfs_drop_extents(struct btrfs_trans_handle *trans, args->extent_inserted = true; } + if (first_ctx <= last_ctx) { + int slot, nritems; + + btrfs_release_path(path); + + key.objectid = ino; + key.type = BTRFS_FSCRYPT_CTX_KEY; + key.offset = first_ctx; + + ret = btrfs_search_slot(trans, root, &key, path, modify_tree, !!modify_tree); + if (ret < 0) + goto out_ctx; +next_leaf: + leaf = path->nodes[0]; + slot = path->slots[0]; + + del_slot = slot; + del_nr = 0; + nritems = btrfs_header_nritems(leaf); + while (slot < nritems) { + btrfs_item_key_to_cpu(leaf, &key, slot); + if (key.objectid > ino || + key.type > BTRFS_FSCRYPT_CTX_KEY || + key.offset > last_ctx) + break; + del_nr++; + slot++; + } + if (del_nr) { + ret = btrfs_del_items(trans, root, path, del_slot, del_nr); + if (unlikely(ret)) { + btrfs_abort_transaction(trans, ret); + goto out_ctx; + } + + if (slot == nritems) { + ret = btrfs_next_leaf(root, path); + if (!ret) + goto next_leaf; + if (ret > 0) + ret = 0; + } + } +out_ctx: + if (args->path && args->extent_inserted) { + int err; + + btrfs_release_path(path); + + key.objectid = ino; + key.type = BTRFS_EXTENT_DATA_KEY; + key.offset = args->start; + err = btrfs_search_slot(trans, root, &key, path, 0, 0); + if (err && ret >= 0) + ret = err; + } + } + if (!args->path) btrfs_free_path(path); else if (!args->extent_inserted) @@ -2341,6 +2407,16 @@ static int btrfs_insert_replace_extent(struct btrfs_trans_handle *trans, if (extent_info->is_new_extent) btrfs_set_file_extent_generation(leaf, extent, trans->transid); btrfs_release_path(path); + if (extent_info->fscrypt_context_size) { + key.type = BTRFS_FSCRYPT_CTX_KEY; + ret = btrfs_insert_empty_item(trans, root, path, &key, + extent_info->fscrypt_context_size); + if (ret) + return ret; + btrfs_fscrypt_save_extent_info(path, + extent_info->fscrypt_ctx, + extent_info->fscrypt_context_size); + } ret = btrfs_inode_set_file_extent_range(inode, extent_info->file_offset, replace_len); @@ -3813,6 +3889,9 @@ static int btrfs_file_open(struct inode *inode, struct file *filp) return -EIO; filp->f_mode |= FMODE_NOWAIT | FMODE_CAN_ODIRECT; + ret = fscrypt_file_open(inode, filp); + if (ret) + return ret; ret = fsverity_file_open(inode, filp); if (ret) diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h index a4758d94b32e..b21909d37b56 100644 --- a/fs/btrfs/fs.h +++ b/fs/btrfs/fs.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include "extent-io-tree.h" @@ -270,6 +271,7 @@ enum { BTRFS_MOUNT_IGNOREMETACSUMS = (1ULL << 31), BTRFS_MOUNT_IGNORESUPERFLAGS = (1ULL << 32), BTRFS_MOUNT_REF_TRACKER = (1ULL << 33), + BTRFS_MOUNT_TEST_DUMMY_ENCRYPTION = (1ULL << 34), }; /* These mount options require a full read-only fs, no new transaction is allowed. */ @@ -322,7 +324,8 @@ enum { (BTRFS_FEATURE_INCOMPAT_SUPP_STABLE | \ BTRFS_FEATURE_INCOMPAT_RAID_STRIPE_TREE | \ BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2 | \ - BTRFS_FEATURE_INCOMPAT_REMAP_TREE) + BTRFS_FEATURE_INCOMPAT_REMAP_TREE | \ + BTRFS_FEATURE_INCOMPAT_ENCRYPT) #else @@ -957,6 +960,7 @@ struct btrfs_fs_info { spinlock_t eb_leak_lock; struct list_head allocated_ebs; #endif + struct fscrypt_dummy_policy dummy_enc_policy; }; #define folio_to_inode(_folio) (BTRFS_I(_Generic((_folio), \ diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c new file mode 100644 index 000000000000..b57d2b0ca9fa --- /dev/null +++ b/fs/btrfs/fscrypt.c @@ -0,0 +1,413 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include "ctree.h" +#include "accessors.h" +#include "btrfs_inode.h" +#include "disk-io.h" +#include "ioctl.h" +#include "fs.h" +#include "fscrypt.h" +#include "ioctl.h" +#include "messages.h" +#include "root-tree.h" +#include "super.h" +#include "transaction.h" +#include "volumes.h" +#include "xattr.h" +#include "file-item.h" + +/* + * From a given location in a leaf, read a name into a qstr (usually a + * fscrypt_name's disk_name), allocating the required buffer. Used for + * nokey names. + */ +int btrfs_fscrypt_get_disk_name(struct extent_buffer *leaf, + struct btrfs_dir_item *dir_item, + struct fscrypt_str *name) +{ + unsigned long de_name_len = btrfs_dir_name_len(leaf, dir_item); + unsigned long de_name = (unsigned long)(dir_item + 1); + /* + * For no-key names, we use this opportunity to find the disk + * name, so future searches don't need to deal with nokey names + * and we know what the encrypted size is. + */ + name->name = kmalloc(de_name_len, GFP_NOFS); + + if (!name->name) + return -ENOMEM; + + read_extent_buffer(leaf, name->name, de_name, de_name_len); + + name->len = de_name_len; + return 0; +} + +/* + * This function is extremely similar to fscrypt_match_name() but uses an + * extent_buffer. + */ +bool btrfs_fscrypt_match_name(struct fscrypt_name *fname, + struct extent_buffer *leaf, unsigned long de_name, + u32 de_name_len) +{ + const struct fscrypt_nokey_name *nokey_name = + (const struct fscrypt_nokey_name *)fname->crypto_buf.name; + u8 digest[SHA256_DIGEST_SIZE]; + + if (likely(fname->disk_name.name)) { + if (de_name_len != fname->disk_name.len) + return false; + return !memcmp_extent_buffer(leaf, fname->disk_name.name, de_name, de_name_len); + } + + if (de_name_len <= sizeof(nokey_name->bytes)) + return false; + + if (memcmp_extent_buffer(leaf, nokey_name->bytes, de_name, sizeof(nokey_name->bytes))) + return false; + + extent_buffer_sha256(leaf, de_name + sizeof(nokey_name->bytes), + de_name_len - sizeof(nokey_name->bytes), digest); + + return !memcmp(digest, nokey_name->sha256, sizeof(digest)); +} + +static int btrfs_fscrypt_get_context(struct inode *inode, void *ctx, size_t len) +{ + struct btrfs_key key = { + .objectid = btrfs_ino(BTRFS_I(inode)), + .type = BTRFS_FSCRYPT_INODE_CTX_KEY, + .offset = 0, + }; + struct btrfs_path *path; + struct extent_buffer *leaf; + unsigned long ptr; + int ret; + + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + ret = btrfs_search_slot(NULL, BTRFS_I(inode)->root, &key, path, 0, 0); + if (ret) { + len = -ENOENT; + goto out; + } + + leaf = path->nodes[0]; + ptr = btrfs_item_ptr_offset(leaf, path->slots[0]); + /* fscrypt provides max context length, but it could be less */ + len = min_t(size_t, len, btrfs_item_size(leaf, path->slots[0])); + read_extent_buffer(leaf, ctx, ptr, len); + +out: + btrfs_free_path(path); + return len; +} + +static int btrfs_fscrypt_set_context(struct inode *inode, const void *ctx, + size_t len, void *fs_data) +{ + struct btrfs_trans_handle *trans = fs_data; + struct btrfs_key key = { + .objectid = btrfs_ino(BTRFS_I(inode)), + .type = BTRFS_FSCRYPT_INODE_CTX_KEY, + .offset = 0, + }; + struct btrfs_path *path = NULL; + struct extent_buffer *leaf; + unsigned long ptr; + int ret; + + if (!trans) + trans = btrfs_start_transaction(BTRFS_I(inode)->root, 2); + if (IS_ERR(trans)) + return PTR_ERR(trans); + + path = btrfs_alloc_path(); + if (!path) { + ret = -ENOMEM; + goto out_err; + } + + ret = btrfs_search_slot(trans, BTRFS_I(inode)->root, &key, path, 0, 1); + if (ret < 0) + goto out_err; + + if (ret > 0) { + btrfs_release_path(path); + ret = btrfs_insert_empty_item(trans, BTRFS_I(inode)->root, path, &key, len); + if (ret) + goto out_err; + } + + set_bit(BTRFS_INODE_NEEDS_FULL_SYNC, &BTRFS_I(inode)->runtime_flags); + leaf = path->nodes[0]; + ptr = btrfs_item_ptr_offset(leaf, path->slots[0]); + + len = min_t(size_t, len, btrfs_item_size(leaf, path->slots[0])); + write_extent_buffer(leaf, ctx, ptr, len); + btrfs_mark_buffer_dirty(trans, leaf); + btrfs_release_path(path); + + if (fs_data) + goto out_err; + + BTRFS_I(inode)->flags |= BTRFS_INODE_ENCRYPT; + btrfs_sync_inode_flags_to_i_flags(BTRFS_I(inode)); + inode_inc_iversion(inode); + inode_set_ctime_current(inode); + ret = btrfs_update_inode(trans, BTRFS_I(inode)); + if (ret) + goto out_abort; + btrfs_free_path(path); + btrfs_end_transaction(trans); + return 0; +out_abort: + btrfs_abort_transaction(trans, ret); +out_err: + if (!fs_data) + btrfs_end_transaction(trans); + btrfs_free_path(path); + return ret; +} + +static bool btrfs_fscrypt_empty_dir(struct inode *inode) +{ + return inode->i_size == BTRFS_EMPTY_DIR_SIZE; +} + +static struct block_device **btrfs_fscrypt_get_devices(struct super_block *sb, + unsigned int *num_devs) +{ + struct btrfs_fs_info *fs_info = btrfs_sb(sb); + struct btrfs_fs_devices *fs_devices = fs_info->fs_devices; + int nr_devices = fs_devices->open_devices; + struct block_device **devs; + struct btrfs_device *device; + int i = 0; + + devs = kmalloc_array(nr_devices, sizeof(*devs), GFP_NOFS | GFP_NOWAIT); + if (!devs) + return ERR_PTR(-ENOMEM); + + rcu_read_lock(); + list_for_each_entry_rcu(device, &fs_devices->devices, dev_list) { + if (!test_bit(BTRFS_DEV_STATE_IN_FS_METADATA, &device->dev_state) || + !device->bdev || + test_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state)) + continue; + + devs[i++] = device->bdev; + + if (i >= nr_devices) + break; + + } + rcu_read_unlock(); + + *num_devs = i; + return devs; +} + +static blk_status_t btrfs_process_encrypted_bio(struct bio *orig_bio, + struct bio *enc_bio) +{ + struct btrfs_bio *bbio; + + /* + * If our bio is from the normal fs_bio_set then we know this is a + * mirror split and we can skip it, we'll get the real bio on the last + * mirror and we can process that one. + */ + if (orig_bio->bi_pool == &fs_bio_set) + return BLK_STS_OK; + + bbio = btrfs_bio(orig_bio); + + if (bio_op(orig_bio) == REQ_OP_READ) { + /* + * We have ->saved_iter based on the orig_bio, so if the block + * layer changes we need to notice this asap so we can update + * our code to handle the new world order. + */ + ASSERT(orig_bio == enc_bio); + return btrfs_check_encrypted_read_bio(bbio, enc_bio); + } + return btrfs_csum_one_bio(bbio, enc_bio, false); +} + +static const union fscrypt_policy *btrfs_get_dummy_policy(struct super_block *sb) +{ + return btrfs_sb(sb)->dummy_enc_policy.policy; +} + +int btrfs_fscrypt_load_extent_info(struct btrfs_inode *inode, + struct btrfs_path *path, + struct btrfs_key *key, + struct extent_map *em) +{ + struct extent_buffer *leaf; + int slot; + unsigned long offset; + u8 ctx[BTRFS_MAX_EXTENT_CTX_SIZE]; + unsigned long size; + struct fscrypt_extent_info *info; + unsigned long nofs_flag; + int ret; + + if (em->disk_bytenr == EXTENT_MAP_HOLE) + return 0; + if (btrfs_extent_map_encryption(em) != BTRFS_ENCRYPTION_FSCRYPT) + return 0; + + key->type = BTRFS_FSCRYPT_CTX_KEY; + ret = btrfs_search_slot(NULL, inode->root, key, path, 0, 0); + leaf = path->nodes[0]; + slot = path->slots[0]; + if (ret) { + btrfs_err(leaf->fs_info, + "missing or error searching encryption context item in leaf: root=%llu block=%llu slot=%d ino=%llu file_offset=%llu, err %i", + btrfs_header_owner(leaf), btrfs_header_bytenr(leaf), slot, + key->objectid, key->offset, ret); + btrfs_release_path(path); + return ret; + } + + size = btrfs_item_size(leaf, slot); + if (size > FSCRYPT_SET_CONTEXT_MAX_SIZE) { + btrfs_err(leaf->fs_info, + "unexpected or corrupted encryption context size in leaf: root=%llu block=%llu slot=%d ino=%llu file_offset=%llu, size %lu (too big)", + btrfs_header_owner(leaf), btrfs_header_bytenr(leaf), slot, + key->objectid, key->offset, size); + btrfs_release_path(path); + return -EIO; + } + + offset = btrfs_item_ptr_offset(leaf, slot), + read_extent_buffer(leaf, ctx, offset, size); + btrfs_release_path(path); + + nofs_flag = memalloc_nofs_save(); + info = fscrypt_load_extent_info(&inode->vfs_inode, ctx, size); + memalloc_nofs_restore(nofs_flag); + if (IS_ERR(info)) + return PTR_ERR(info); + em->fscrypt_info = info; + return 0; +} + +void btrfs_fscrypt_save_extent_info(struct btrfs_path *path, u8 *ctx, unsigned long size) +{ + struct extent_buffer *leaf = path->nodes[0]; + unsigned long offset = btrfs_item_ptr_offset(leaf, path->slots[0]); + + ASSERT(size <= FSCRYPT_SET_CONTEXT_MAX_SIZE); + + write_extent_buffer(leaf, ctx, offset, size); + btrfs_release_path(path); +} + +ssize_t btrfs_fscrypt_context_for_new_extent(struct btrfs_inode *inode, + struct fscrypt_extent_info *info, + u8 *ctx) +{ + ssize_t ret; + + if (!info) + return 0; + + ret = fscrypt_context_for_new_extent(&inode->vfs_inode, info, ctx); + if (ret < 0) { + btrfs_err_rl(inode->root->fs_info, "invalid encrypt context"); + return ret; + } + return ret; +} + +/* + * The block crypto stuff allocates bounce buffers for encryption, so splits at + * BIO_MAX_VECS worth of segments. If we are larger than that number of + * segments then we need to limit the size to the size that BIO_MAX_VECS covers. + */ +int btrfs_fscrypt_bio_length(struct bio *bio, u64 map_length) +{ + unsigned int i = 0; + struct bio_vec bv; + struct bvec_iter iter; + u64 segments_length = 0; + + if (bio_op(bio) != REQ_OP_WRITE) + return map_length; + + bio_for_each_segment(bv, bio, iter) { + segments_length += bv.bv_len; + if (++i == BIO_MAX_VECS) + return segments_length; + } + + return map_length; +} + +int btrfs_decrypt_name(struct btrfs_root *root, struct extent_buffer *eb, + unsigned long name_off, u32 name_len, + u64 parent_ino, struct fscrypt_str *name) +{ + struct btrfs_inode *inode; + struct inode *dir; + struct fscrypt_str iname = FSTR_INIT(NULL, 0); + int ret; + + ASSERT(name_len <= BTRFS_NAME_LEN); + + ret = fscrypt_fname_alloc_buffer(name_len, &iname); + if (ret) + return ret; + + inode = btrfs_iget(parent_ino, root); + if (IS_ERR(inode)) { + ret = PTR_ERR(inode); + goto out; + } + dir = &inode->vfs_inode; + + /* + * Directory isn't encrypted, the name isn't encrypted, we can just copy + * it into the buffer. + */ + if (!IS_ENCRYPTED(dir)) { + read_extent_buffer(eb, name->name, name_off, name_len); + name->len = name_len; + goto out_inode; + } + + read_extent_buffer(eb, iname.name, name_off, name_len); + + ret = fscrypt_prepare_readdir(dir); + if (ret) + goto out_inode; + + ASSERT(inode->i_crypt_info); + ret = fscrypt_fname_disk_to_usr(dir, 0, 0, &iname, name); +out_inode: + iput(dir); +out: + fscrypt_fname_free_buffer(&iname); + return ret; +} + +const struct fscrypt_operations btrfs_fscrypt_ops = { + .inode_info_offs = (int)offsetof(struct btrfs_inode, i_crypt_info) - + (int)offsetof(struct btrfs_inode, vfs_inode), + .has_per_extent_encryption = 1, + .get_context = btrfs_fscrypt_get_context, + .set_context = btrfs_fscrypt_set_context, + .empty_dir = btrfs_fscrypt_empty_dir, + .get_devices = btrfs_fscrypt_get_devices, + .process_bio = btrfs_process_encrypted_bio, + .get_dummy_policy = btrfs_get_dummy_policy, +}; diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h new file mode 100644 index 000000000000..4a1daed90d06 --- /dev/null +++ b/fs/btrfs/fscrypt.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef BTRFS_FSCRYPT_H +#define BTRFS_FSCRYPT_H + +#include +#include "extent_map.h" + +#include "fs.h" + +#ifdef CONFIG_FS_ENCRYPTION +int btrfs_fscrypt_get_disk_name(struct extent_buffer *leaf, + struct btrfs_dir_item *di, + struct fscrypt_str *qstr); + +bool btrfs_fscrypt_match_name(struct fscrypt_name *fname, + struct extent_buffer *leaf, + unsigned long de_name, u32 de_name_len); +int btrfs_fscrypt_load_extent_info(struct btrfs_inode *inode, + struct btrfs_path *path, + struct btrfs_key *key, + struct extent_map *em); +void btrfs_fscrypt_save_extent_info(struct btrfs_path *path, u8 *ctx, unsigned long size); +ssize_t btrfs_fscrypt_context_for_new_extent(struct btrfs_inode *inode, + struct fscrypt_extent_info *info, + u8 *ctx); +int btrfs_fscrypt_bio_length(struct bio *bio, u64 map_length); +int btrfs_decrypt_name(struct btrfs_root *root, struct extent_buffer *eb, + unsigned long name_off, u32 name_len, + u64 parent_ino, struct fscrypt_str *name); + +#else +static inline void btrfs_fscrypt_save_extent_info(struct btrfs_path *path, + u8 *ctx, unsigned long size) { } + +static inline int btrfs_fscrypt_load_extent_info(struct btrfs_inode *inode, + struct btrfs_path *path, + struct btrfs_key *key, + struct extent_map *em) +{ + return 0; +} + +static inline int btrfs_fscrypt_get_disk_name(struct extent_buffer *leaf, + struct btrfs_dir_item *di, + struct fscrypt_str *qstr) +{ + return 0; +} + +static inline bool btrfs_fscrypt_match_name(struct fscrypt_name *fname, + struct extent_buffer *leaf, + unsigned long de_name, + u32 de_name_len) +{ + if (de_name_len != fname_len(fname)) + return false; + return !memcmp_extent_buffer(leaf, fname->disk_name.name, de_name, de_name_len); +} + +static inline ssize_t btrfs_fscrypt_context_for_new_extent(struct btrfs_inode *inode, + struct fscrypt_extent_info *info, + u8 *ctx) +{ + if (!info) + return 0; + return -EINVAL; +} + +static inline u64 btrfs_fscrypt_bio_length(struct bio *bio, u64 map_length) +{ + return map_length; +} + +static inline int btrfs_decrypt_name(struct btrfs_root *root, struct extent_buffer *eb, + unsigned long name_off, u32 name_len, + u64 parent_ino, struct fscrypt_str *name) +{ + return -EINVAL; +} + +#endif /* CONFIG_FS_ENCRYPTION */ + +extern const struct fscrypt_operations btrfs_fscrypt_ops; + +#endif /* BTRFS_FSCRYPT_H */ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 906d5c21ebc4..8a9f80c8364e 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -60,6 +60,7 @@ #include "defrag.h" #include "dir-item.h" #include "file-item.h" +#include "fscrypt.h" #include "uuid-tree.h" #include "ioctl.h" #include "file.h" @@ -1111,6 +1112,7 @@ static void submit_one_async_extent(struct async_chunk *async_chunk, file_extent.num_bytes = async_extent->ram_size; file_extent.offset = 0; file_extent.compression = async_extent->cb->compress_type; + file_extent.fscrypt_info = NULL; async_extent->cb->bbio.bio.bi_iter.bi_sector = ins.objectid >> SECTOR_SHIFT; @@ -1119,10 +1121,12 @@ static void submit_one_async_extent(struct async_chunk *async_chunk, ret = PTR_ERR(em); goto out_free_reserve; } - btrfs_free_extent_map(em); + file_extent.fscrypt_info = em->fscrypt_info; + file_extent.orig_offset = start; ordered = btrfs_alloc_ordered_extent(inode, start, &file_extent, 1U << BTRFS_ORDERED_COMPRESSED); + btrfs_free_extent_map(em); if (IS_ERR(ordered)) { btrfs_drop_extent_map_range(inode, start, end, false); ret = PTR_ERR(ordered); @@ -1248,6 +1252,7 @@ static int cow_one_range(struct btrfs_inode *inode, struct folio *locked_folio, file_extent.ram_bytes = ins->offset; file_extent.offset = 0; file_extent.compression = BTRFS_COMPRESS_NONE; + file_extent.fscrypt_info = NULL; /* * Locked range will be released either during error clean up (inside @@ -1260,10 +1265,12 @@ static int cow_one_range(struct btrfs_inode *inode, struct folio *locked_folio, ret = PTR_ERR(em); goto free_reserved; } - btrfs_free_extent_map(em); + file_extent.fscrypt_info = em->fscrypt_info; + file_extent.orig_offset = file_offset; ordered = btrfs_alloc_ordered_extent(inode, file_offset, &file_extent, 1U << BTRFS_ORDERED_REGULAR); + btrfs_free_extent_map(em); if (IS_ERR(ordered)) { btrfs_drop_extent_map_range(inode, file_offset, cur_end, false); ret = PTR_ERR(ordered); @@ -1930,18 +1937,32 @@ static int nocow_one_range(struct btrfs_inode *inode, struct folio *locked_folio u64 file_pos, bool is_prealloc) { struct btrfs_ordered_extent *ordered; + struct extent_map *em; const u64 len = nocow_args->file_extent.num_bytes; const u64 end = file_pos + len - 1; int ret = 0; btrfs_lock_extent(&inode->io_tree, file_pos, end, cached); - if (is_prealloc) { - struct extent_map *em; + /* + * We only want to do this lookup if we're encrypted, otherwise + * fsrypt_info will be null and we can avoid this lookup. + */ + if (IS_ENCRYPTED(&inode->vfs_inode)) { + em = btrfs_get_extent(inode, NULL, file_pos, len); + if (IS_ERR(em)) { + btrfs_unlock_extent(&inode->io_tree, file_pos, end, cached); + return PTR_ERR(em); + } + nocow_args->file_extent.fscrypt_info = fscrypt_get_extent_info(em->fscrypt_info); + btrfs_free_extent_map(em); + } + if (is_prealloc) { em = btrfs_create_io_em(inode, file_pos, &nocow_args->file_extent, BTRFS_ORDERED_PREALLOC); if (IS_ERR(em)) { + fscrypt_put_extent_info(nocow_args->file_extent.fscrypt_info); ret = PTR_ERR(em); goto error; } @@ -1952,6 +1973,7 @@ static int nocow_one_range(struct btrfs_inode *inode, struct folio *locked_folio is_prealloc ? (1U << BTRFS_ORDERED_PREALLOC) : (1U << BTRFS_ORDERED_NOCOW)); + fscrypt_put_extent_info(nocow_args->file_extent.fscrypt_info); if (IS_ERR(ordered)) { if (is_prealloc) btrfs_drop_extent_map_range(inode, file_pos, end, false); @@ -2188,6 +2210,8 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode, cow_start = (u64)-1; } + nocow_args.file_extent.orig_offset = + found_key.offset - nocow_args.file_extent.offset; ret = nocow_one_range(inode, locked_folio, &cached_state, &nocow_args, cur_offset, extent_type == BTRFS_FILE_EXTENT_PREALLOC); @@ -3036,7 +3060,9 @@ int btrfs_writepage_cow_fixup(struct folio *folio) } static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, - struct btrfs_inode *inode, u64 file_pos, + struct btrfs_inode *inode, + struct fscrypt_extent_info *fscrypt_info, + u64 file_pos, struct btrfs_file_extent_item *stack_fi, const bool update_inode_bytes, u64 qgroup_reserved) @@ -3052,8 +3078,16 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, u64 num_bytes = btrfs_stack_file_extent_num_bytes(stack_fi); u64 ram_bytes = btrfs_stack_file_extent_ram_bytes(stack_fi); struct btrfs_drop_extents_args drop_args = { 0 }; + u8 fscrypt_ctx[FSCRYPT_SET_CONTEXT_MAX_SIZE]; + ssize_t fscrypt_context_size; int ret; + fscrypt_context_size = btrfs_fscrypt_context_for_new_extent(inode, + fscrypt_info, + fscrypt_ctx); + if (fscrypt_context_size < 0) + return (int)fscrypt_context_size; + path = btrfs_alloc_path(); if (!path) return -ENOMEM; @@ -3094,6 +3128,16 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, btrfs_release_path(path); + if (fscrypt_context_size) { + ins.objectid = btrfs_ino(inode); + ins.type = BTRFS_FSCRYPT_CTX_KEY; + ins.offset = file_pos; + ret = btrfs_insert_empty_item(trans, root, path, &ins, fscrypt_context_size); + if (ret) + return ret; + btrfs_fscrypt_save_extent_info(path, fscrypt_ctx, fscrypt_context_size); + } + /* * If we dropped an inline extent here, we know the range where it is * was not marked with the EXTENT_DELALLOC_NEW bit, so we update the @@ -3160,7 +3204,8 @@ static int insert_ordered_extent_file_extent(struct btrfs_trans_handle *trans, btrfs_set_stack_file_extent_num_bytes(&stack_fi, num_bytes); btrfs_set_stack_file_extent_ram_bytes(&stack_fi, ram_bytes); btrfs_set_stack_file_extent_compression(&stack_fi, oe->compress_type); - /* Encryption and other encoding is reserved and all 0 */ + btrfs_set_stack_file_extent_encryption(&stack_fi, oe->encryption_type); + /* Other encoding is reserved and always 0 */ /* * For delalloc, when completing an ordered extent we update the inode's @@ -3172,7 +3217,7 @@ static int insert_ordered_extent_file_extent(struct btrfs_trans_handle *trans, test_bit(BTRFS_ORDERED_ENCODED, &oe->flags) || test_bit(BTRFS_ORDERED_TRUNCATED, &oe->flags); - return insert_reserved_file_extent(trans, oe->inode, + return insert_reserved_file_extent(trans, oe->inode, oe->fscrypt_info, oe->file_offset, &stack_fi, update_inode_bytes, oe->qgroup_rsv); } @@ -4403,7 +4448,7 @@ static void update_time_after_link_or_unlink(struct btrfs_inode *dir) static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans, struct btrfs_inode *dir, struct btrfs_inode *inode, - const struct fscrypt_str *name, + struct fscrypt_name *name, struct btrfs_rename_ctx *rename_ctx) { struct btrfs_root *root = dir->root; @@ -4419,7 +4464,7 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans, if (!path) return -ENOMEM; - di = btrfs_lookup_dir_item(trans, root, path, dir_ino, name, -1); + di = btrfs_lookup_dir_item_fname(trans, root, path, dir_ino, name, -1); if (IS_ERR_OR_NULL(di)) { btrfs_free_path(path); return di ? PTR_ERR(di) : -ENOENT; @@ -4451,11 +4496,13 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans, } } - ret = btrfs_del_inode_ref(trans, root, name, ino, dir_ino, &index); + ret = btrfs_del_inode_ref(trans, root, &name->disk_name, ino, dir_ino, &index); if (unlikely(ret)) { + /* This should print a base-64 encoded name if relevant? */ btrfs_crit(fs_info, "failed to delete reference to %.*s, root %llu inode %llu parent %llu", - name->len, name->name, btrfs_root_id(root), ino, dir_ino); + name->disk_name.len, name->disk_name.name, + btrfs_root_id(root), ino, dir_ino); btrfs_abort_transaction(trans, ret); return ret; } @@ -4476,8 +4523,8 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans, * operations on the log tree, increasing latency for applications. */ if (!rename_ctx) { - btrfs_del_inode_ref_in_log(trans, name, inode, dir); - btrfs_del_dir_entries_in_log(trans, name, dir, index); + btrfs_del_inode_ref_in_log(trans, &name->disk_name, inode, dir); + btrfs_del_dir_entries_in_log(trans, &name->disk_name, dir, index); } /* @@ -4491,7 +4538,7 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans, */ btrfs_run_delayed_iput(fs_info, inode); - btrfs_i_size_write(dir, dir->vfs_inode.i_size - name->len * 2); + btrfs_i_size_write(dir, dir->vfs_inode.i_size - name->disk_name.len * 2); inode_inc_iversion(&inode->vfs_inode); inode_set_ctime_current(&inode->vfs_inode); inode_inc_iversion(&dir->vfs_inode); @@ -4502,7 +4549,7 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans, int btrfs_unlink_inode(struct btrfs_trans_handle *trans, struct btrfs_inode *dir, struct btrfs_inode *inode, - const struct fscrypt_str *name) + struct fscrypt_name *name) { int ret; @@ -4549,11 +4596,9 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry) goto fscrypt_free; } - btrfs_record_unlink_dir(trans, BTRFS_I(dir), BTRFS_I(d_inode(dentry)), - false); + btrfs_record_unlink_dir(trans, BTRFS_I(dir), BTRFS_I(d_inode(dentry)), false); - ret = btrfs_unlink_inode(trans, BTRFS_I(dir), BTRFS_I(d_inode(dentry)), - &fname.disk_name); + ret = btrfs_unlink_inode(trans, BTRFS_I(dir), BTRFS_I(d_inode(dentry)), &fname); if (ret) goto end_trans; @@ -4590,8 +4635,6 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, if (ret) return ret; - /* This needs to handle no-key deletions later on */ - if (btrfs_ino(inode) == BTRFS_FIRST_FREE_OBJECTID) { objectid = btrfs_root_id(inode->root); } else if (btrfs_ino(inode) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID) { @@ -4608,8 +4651,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, goto out; } - di = btrfs_lookup_dir_item(trans, root, path, dir_ino, - &fname.disk_name, -1); + di = btrfs_lookup_dir_item_fname(trans, root, path, dir_ino, &fname, -1); if (IS_ERR_OR_NULL(di)) { ret = di ? PTR_ERR(di) : -ENOENT; goto out; @@ -4635,7 +4677,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, * call btrfs_del_root_ref, and it _shouldn't_ fail. */ if (btrfs_ino(inode) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID) { - di = btrfs_search_dir_index_item(root, path, dir_ino, &fname.disk_name); + di = btrfs_search_dir_index_item(root, path, dir_ino, &fname); if (IS_ERR(di)) { ret = PTR_ERR(di); btrfs_abort_transaction(trans, ret); @@ -4649,7 +4691,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, } else { ret = btrfs_del_root_ref(trans, objectid, btrfs_root_id(root), dir_ino, - &index, &fname.disk_name); + &index, &fname); if (unlikely(ret)) { btrfs_abort_transaction(trans, ret); goto out; @@ -4962,7 +5004,7 @@ static int btrfs_rmdir(struct inode *vfs_dir, struct dentry *dentry) btrfs_record_unlink_dir(trans, dir, inode, false); /* now the directory is empty */ - ret = btrfs_unlink_inode(trans, dir, inode, &fname.disk_name); + ret = btrfs_unlink_inode(trans, dir, inode, &fname); if (!ret) btrfs_i_size_write(inode, 0); out: @@ -5490,6 +5532,10 @@ static int btrfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, if (ret) return ret; + ret = fscrypt_prepare_setattr(dentry, attr); + if (ret) + return ret; + if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) { ret = btrfs_setsize(inode, attr); if (ret) @@ -5746,6 +5792,7 @@ void btrfs_evict_inode(struct inode *inode) */ btrfs_remove_delayed_node(BTRFS_I(inode)); clear_inode: + fscrypt_put_encryption_info(inode); clear_inode(inode); } @@ -5756,36 +5803,23 @@ void btrfs_evict_inode(struct inode *inode) * If no dir entries were found, returns -ENOENT. * If found a corrupted location in dir entry, returns -EUCLEAN. */ -static int btrfs_inode_by_name(struct btrfs_inode *dir, struct dentry *dentry, +static int btrfs_inode_by_name(struct btrfs_inode *dir, struct fscrypt_name *fname, struct btrfs_key *location, u8 *type) { struct btrfs_dir_item *di; BTRFS_PATH_AUTO_FREE(path); struct btrfs_root *root = dir->root; int ret = 0; - struct fscrypt_name fname; path = btrfs_alloc_path(); if (!path) return -ENOMEM; - ret = fscrypt_setup_filename(&dir->vfs_inode, &dentry->d_name, 1, &fname); - if (ret < 0) - return ret; - /* - * fscrypt_setup_filename() should never return a positive value, but - * gcc on sparc/parisc thinks it can, so assert that doesn't happen. - */ - ASSERT(ret == 0); - /* This needs to handle no-key deletions later on */ - di = btrfs_lookup_dir_item(NULL, root, path, btrfs_ino(dir), - &fname.disk_name, 0); - if (IS_ERR_OR_NULL(di)) { - ret = di ? PTR_ERR(di) : -ENOENT; - goto out; - } + di = btrfs_lookup_dir_item_fname(NULL, root, path, btrfs_ino(dir), fname, 0); + if (IS_ERR_OR_NULL(di)) + return di ? PTR_ERR(di) : -ENOENT; btrfs_dir_item_key_to_cpu(path->nodes[0], di, location); if (unlikely(location->type != BTRFS_INODE_ITEM_KEY && @@ -5793,13 +5827,11 @@ static int btrfs_inode_by_name(struct btrfs_inode *dir, struct dentry *dentry, ret = -EUCLEAN; btrfs_warn(root->fs_info, "%s gets something invalid in DIR_ITEM (name %s, directory ino %llu, location " BTRFS_KEY_FMT ")", - __func__, fname.disk_name.name, btrfs_ino(dir), + __func__, fname->usr_fname->name, btrfs_ino(dir), BTRFS_KEY_FMT_VALUE(location)); } if (!ret) *type = btrfs_dir_ftype(path->nodes[0], di); -out: - fscrypt_free_filename(&fname); return ret; } @@ -6066,20 +6098,27 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry) struct btrfs_root *root = BTRFS_I(dir)->root; struct btrfs_root *sub_root = root; struct btrfs_key location = { 0 }; + struct fscrypt_name fname; u8 di_type = 0; int ret = 0; if (dentry->d_name.len > BTRFS_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); - ret = btrfs_inode_by_name(BTRFS_I(dir), dentry, &location, &di_type); - if (ret < 0) + ret = fscrypt_prepare_lookup(dir, dentry, &fname); + if (ret) return ERR_PTR(ret); + ret = btrfs_inode_by_name(BTRFS_I(dir), &fname, &location, &di_type); + if (ret < 0) { + inode = ERR_PTR(ret); + goto cleanup; + } + if (location.type == BTRFS_INODE_ITEM_KEY) { inode = btrfs_iget(location.objectid, root); if (IS_ERR(inode)) - return ERR_CAST(inode); + goto cleanup; /* Do extra check against inode mode with di_type */ if (unlikely(btrfs_inode_type(inode) != di_type)) { @@ -6088,9 +6127,10 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry) inode->vfs_inode.i_mode, btrfs_inode_type(inode), di_type); iput(&inode->vfs_inode); - return ERR_PTR(-EUCLEAN); + inode = ERR_PTR(-EUCLEAN); + goto cleanup; } - return &inode->vfs_inode; + goto cleanup; } ret = fixup_tree_root_location(fs_info, BTRFS_I(dir), dentry, @@ -6105,7 +6145,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry) btrfs_put_root(sub_root); if (IS_ERR(inode)) - return ERR_CAST(inode); + goto cleanup; down_read(&fs_info->cleanup_work_sem); if (!sb_rdonly(inode->vfs_inode.i_sb)) @@ -6117,6 +6157,9 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry) } } +cleanup: + fscrypt_free_filename(&fname); + if (IS_ERR(inode)) return ERR_CAST(inode); @@ -6315,18 +6358,32 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) LIST_HEAD(del_list); int ret; char *name_ptr; - int name_len; + u32 name_len; int entries = 0; int total_len = 0; bool put = false; struct btrfs_key location; + struct fscrypt_str fstr = FSTR_INIT(NULL, 0); + u32 fstr_len = 0; if (!dir_emit_dots(file, ctx)) return 0; + if (BTRFS_I(inode)->flags & BTRFS_INODE_ENCRYPT) { + ret = fscrypt_prepare_readdir(inode); + if (ret) + return ret; + ret = fscrypt_fname_alloc_buffer(BTRFS_NAME_LEN, &fstr); + if (ret) + return ret; + fstr_len = fstr.len; + } + path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; + if (!path) { + ret = -ENOMEM; + goto err_fstr; + } addr = private->filldir_buf; path->reada = READA_FORWARD; @@ -6343,6 +6400,7 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) struct dir_entry *entry; struct extent_buffer *leaf = path->nodes[0]; u8 ftype; + u32 nokey_len; if (found_key.objectid != key.objectid) break; @@ -6356,8 +6414,12 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) continue; di = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item); name_len = btrfs_dir_name_len(leaf, di); - if ((total_len + sizeof(struct dir_entry) + name_len) >= - PAGE_SIZE) { + nokey_len = DIV_ROUND_UP(name_len * 4, 3); + /* + * If name is encrypted, and we don't have the key, we could + * need up to 4/3rds the bytes to print it. + */ + if ((total_len + sizeof(struct dir_entry) + nokey_len) >= PAGE_SIZE) { btrfs_release_path(path); ret = btrfs_filldir(private->filldir_buf, entries, ctx); if (ret) @@ -6371,8 +6433,31 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) ftype = btrfs_dir_flags_to_ftype(btrfs_dir_flags(leaf, di)); entry = addr; name_ptr = (char *)(entry + 1); - read_extent_buffer(leaf, name_ptr, - (unsigned long)(di + 1), name_len); + if (btrfs_dir_flags(leaf, di) & BTRFS_FT_ENCRYPTED) { + struct fscrypt_str oname = FSTR_INIT(name_ptr, nokey_len); + u32 hash = 0, minor_hash = 0; + + read_extent_buffer(leaf, fstr.name, (unsigned long)(di + 1), name_len); + fstr.len = name_len; + /* + * We're iterating through DIR_INDEX items, so we don't + * have the DIR_ITEM hash handy. Only compute it if + * we'll need it -- the nokey name stores it, so that + * we can look up the appropriate item by nokey name + * later on. + */ + if (!fscrypt_has_encryption_key(inode)) { + u64 name_hash = btrfs_name_hash(fstr.name, fstr.len); + hash = name_hash; + minor_hash = name_hash >> 32; + } + ret = fscrypt_fname_disk_to_usr(inode, hash, minor_hash, &fstr, &oname); + if (ret) + goto err; + name_len = oname.len; + } else { + read_extent_buffer(leaf, name_ptr, (unsigned long)(di + 1), name_len); + } put_unaligned(name_len, &entry->name_len); put_unaligned(fs_ftype_to_dtype(ftype), &entry->type); btrfs_dir_item_key_to_cpu(leaf, di, &location); @@ -6392,7 +6477,8 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) if (ret) goto nopos; - if (btrfs_readdir_delayed_dir_index(ctx, &ins_list)) + fstr.len = fstr_len; + if (btrfs_readdir_delayed_dir_index(inode, &fstr, ctx, &ins_list)) goto nopos; /* @@ -6421,6 +6507,8 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) err: if (put) btrfs_readdir_put_delayed_items(BTRFS_I(inode), &ins_list, &del_list); +err_fstr: + fscrypt_fname_free_buffer(&fstr); return ret; } @@ -6524,6 +6612,9 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args, struct inode *inode = args->inode; int ret; + if (fscrypt_is_nokey_name(args->dentry)) + return -ENOKEY; + if (!args->orphan) { ret = fscrypt_setup_filename(dir, &args->dentry->d_name, 0, &args->fname); @@ -6537,6 +6628,12 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args, return ret; } + ret = fscrypt_prepare_new_inode(dir, inode, &args->encrypt); + if (ret) { + btrfs_new_inode_args_destroy(args); + return ret; + } + /* 1 to add inode item */ *trans_num_items = 1; /* 1 to add compression property */ @@ -6553,6 +6650,9 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args, if (dir->i_security) (*trans_num_items)++; #endif + /* 1 to add fscrypt item */ + if (args->encrypt) + (*trans_num_items)++; if (args->orphan) { /* 1 to add orphan item */ (*trans_num_items)++; @@ -6765,6 +6865,11 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans, BTRFS_I(inode)->i_otime_sec = ts.tv_sec; BTRFS_I(inode)->i_otime_nsec = ts.tv_nsec; + if (args->encrypt) { + BTRFS_I(inode)->flags |= BTRFS_INODE_ENCRYPT; + btrfs_sync_inode_flags_to_i_flags(BTRFS_I(inode)); + } + /* * We're going to fill the inode item now, so at this point the inode * must be fully initialized. @@ -6838,6 +6943,13 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans, goto discard; } } + if (args->encrypt) { + ret = fscrypt_set_context(inode, trans); + if (ret) { + btrfs_abort_transaction(trans, ret); + goto discard; + } + } ret = btrfs_add_inode_to_root(BTRFS_I(inode), false); if (WARN_ON(ret)) { @@ -6898,6 +7010,7 @@ int btrfs_add_link(struct btrfs_trans_handle *trans, struct btrfs_root *root = parent_inode->root; u64 ino = btrfs_ino(inode); u64 parent_ino = btrfs_ino(parent_inode); + struct fscrypt_name fname = { .disk_name = *name }; if (unlikely(ino == BTRFS_FIRST_FREE_OBJECTID)) { memcpy(&key, &inode->root->root_key, sizeof(key)); @@ -6945,7 +7058,7 @@ int btrfs_add_link(struct btrfs_trans_handle *trans, int ret2; ret2 = btrfs_del_root_ref(trans, key.objectid, btrfs_root_id(root), - parent_ino, &local_index, name); + parent_ino, &local_index, &fname); if (ret2) btrfs_abort_transaction(trans, ret2); } else if (add_backref) { @@ -7048,9 +7161,13 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir, if (inode->i_nlink >= BTRFS_LINK_MAX) return -EMLINK; + ret = fscrypt_prepare_link(old_dentry, dir, dentry); + if (ret) + return ret; + ret = fscrypt_setup_filename(dir, &dentry->d_name, 0, &fname); if (ret) - goto fail; + return ret; ret = btrfs_set_inode_index(BTRFS_I(dir), &index); if (ret) @@ -7386,6 +7503,12 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode, goto out; } + ret = btrfs_fscrypt_load_extent_info(inode, path, &found_key, em); + if (ret > 0) + ret = -EIO; + if (ret < 0) + goto out; + write_lock(&em_tree->lock); ret = btrfs_add_extent_mapping(inode, &em, start, len); write_unlock(&em_tree->lock); @@ -7582,6 +7705,23 @@ struct extent_map *btrfs_create_io_em(struct btrfs_inode *inode, u64 start, if (type == BTRFS_ORDERED_COMPRESSED) btrfs_extent_map_set_compression(em, file_extent->compression); + if (file_extent->fscrypt_info) { + btrfs_extent_map_set_encryption(em, BTRFS_ENCRYPTION_FSCRYPT); + em->fscrypt_info = fscrypt_get_extent_info(file_extent->fscrypt_info); + } else if (IS_ENCRYPTED(&inode->vfs_inode)) { + struct fscrypt_extent_info *fscrypt_info; + + btrfs_extent_map_set_encryption(em, BTRFS_ENCRYPTION_FSCRYPT); + fscrypt_info = fscrypt_prepare_new_extent(&inode->vfs_inode); + if (IS_ERR(fscrypt_info)) { + btrfs_free_extent_map(em); + return ERR_CAST(fscrypt_info); + } + em->fscrypt_info = fscrypt_info; + } else { + btrfs_extent_map_set_encryption(em, BTRFS_ENCRYPTION_NONE); + } + ret = btrfs_replace_extent_map_range(inode, em, true); if (ret) { btrfs_free_extent_map(em); @@ -8109,6 +8249,9 @@ struct inode *btrfs_alloc_inode(struct super_block *sb) INIT_LIST_HEAD(&ei->delayed_iput); init_rwsem(&ei->i_mmap_lock); +#ifdef CONFIG_FS_ENCRYPTION + ei->i_crypt_info = NULL; +#endif return inode; } @@ -8124,6 +8267,7 @@ void btrfs_test_destroy_inode(struct inode *inode) void btrfs_free_inode(struct inode *inode) { kfree(BTRFS_I(inode)->file_extent_tree); + fscrypt_free_inode(inode); kmem_cache_free(btrfs_inode_cachep, BTRFS_I(inode)); } @@ -8195,8 +8339,7 @@ int btrfs_drop_inode(struct inode *inode) /* the snap/subvol tree is on deleting */ if (btrfs_root_refs(&root->root_item) == 0) return 1; - else - return inode_generic_drop(inode); + return inode_generic_drop(inode) || fscrypt_drop_inode(inode); } static void init_once(void *foo) @@ -8460,7 +8603,7 @@ static int btrfs_rename_exchange(struct inode *old_dir, } else { /* src is an inode */ ret = __btrfs_unlink_inode(trans, BTRFS_I(old_dir), BTRFS_I(old_dentry->d_inode), - old_name, &old_rename_ctx); + &old_fname, &old_rename_ctx); if (unlikely(ret)) { btrfs_abort_transaction(trans, ret); goto out_fail; @@ -8482,7 +8625,7 @@ static int btrfs_rename_exchange(struct inode *old_dir, } else { /* dest is an inode */ ret = __btrfs_unlink_inode(trans, BTRFS_I(new_dir), BTRFS_I(new_dentry->d_inode), - new_name, &new_rename_ctx); + &new_fname, &new_rename_ctx); if (unlikely(ret)) { btrfs_abort_transaction(trans, ret); goto out_fail; @@ -8751,7 +8894,7 @@ static int btrfs_rename(struct mnt_idmap *idmap, } else { ret = __btrfs_unlink_inode(trans, BTRFS_I(old_dir), BTRFS_I(d_inode(old_dentry)), - &old_fname.disk_name, &rename_ctx); + &old_fname, &rename_ctx); if (unlikely(ret)) { btrfs_abort_transaction(trans, ret); goto out_fail; @@ -8776,7 +8919,7 @@ static int btrfs_rename(struct mnt_idmap *idmap, } else { ret = btrfs_unlink_inode(trans, BTRFS_I(new_dir), BTRFS_I(d_inode(new_dentry)), - &new_fname.disk_name); + &new_fname); if (unlikely(ret)) { btrfs_abort_transaction(trans, ret); goto out_fail; @@ -8847,6 +8990,10 @@ static int btrfs_rename2(struct mnt_idmap *idmap, struct inode *old_dir, if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT)) return -EINVAL; + ret = fscrypt_prepare_rename(old_dir, old_dentry, new_dir, new_dentry, flags); + if (ret) + return ret; + if (flags & RENAME_EXCHANGE) ret = btrfs_rename_exchange(old_dir, old_dentry, new_dir, new_dentry); @@ -9041,20 +9188,28 @@ static int btrfs_symlink(struct mnt_idmap *idmap, struct inode *dir, }; unsigned int trans_num_items; int ret; - int name_len; int datasize; unsigned long ptr; struct btrfs_file_extent_item *ei; struct extent_buffer *leaf; + struct fscrypt_str disk_link; + size_t max_len; + u32 name_len = strlen(symname); - name_len = strlen(symname); /* - * Symlinks utilize uncompressed inline extent data, which should not - * reach block size. + * BTRFS_MAX_INLINE_DATA_SIZE() isn't actually telling the truth, we actually + * limit inline data extents to min(BTRFS_MAX_INLINE_DATA_SIZE(), sectorsize), + * so adjust max_len given this wonderful bit of inconsistency. */ - if (name_len > BTRFS_MAX_INLINE_DATA_SIZE(fs_info) || - name_len >= fs_info->sectorsize) - return -ENAMETOOLONG; + max_len = min_t(size_t, BTRFS_MAX_INLINE_DATA_SIZE(fs_info), fs_info->sectorsize); + + /* + * fscrypt sets disk_link.len to be len + 1, including a NUL terminator, + * but we don't store that '\0' character. + */ + ret = fscrypt_prepare_symlink(dir, symname, name_len, max_len + 1, &disk_link); + if (ret) + return ret; inode = new_inode(dir->i_sb); if (!inode) @@ -9063,8 +9218,8 @@ static int btrfs_symlink(struct mnt_idmap *idmap, struct inode *dir, inode->i_op = &btrfs_symlink_inode_operations; inode_nohighmem(inode); inode->i_mapping->a_ops = &btrfs_aops; - btrfs_i_size_write(BTRFS_I(inode), name_len); - inode_set_bytes(inode, name_len); + btrfs_i_size_write(BTRFS_I(inode), disk_link.len - 1); + inode_set_bytes(inode, disk_link.len - 1); new_inode_args.inode = inode; ret = btrfs_new_inode_prepare(&new_inode_args, &trans_num_items); @@ -9091,17 +9246,29 @@ static int btrfs_symlink(struct mnt_idmap *idmap, struct inode *dir, inode = NULL; goto out; } + + if (IS_ENCRYPTED(inode)) { + ret = fscrypt_encrypt_symlink(inode, symname, name_len, &disk_link); + if (ret) { + btrfs_abort_transaction(trans, ret); + btrfs_free_path(path); + discard_new_inode(inode); + inode = NULL; + goto out; + } + } + key.objectid = btrfs_ino(BTRFS_I(inode)); key.type = BTRFS_EXTENT_DATA_KEY; key.offset = 0; - datasize = btrfs_file_extent_calc_inline_size(name_len); + datasize = btrfs_file_extent_calc_inline_size(disk_link.len - 1); ret = btrfs_insert_empty_item(trans, root, path, &key, datasize); if (unlikely(ret)) { btrfs_abort_transaction(trans, ret); btrfs_free_path(path); discard_new_inode(inode); inode = NULL; - goto out; + goto free_name; } leaf = path->nodes[0]; ei = btrfs_item_ptr(leaf, path->slots[0], @@ -9112,14 +9279,17 @@ static int btrfs_symlink(struct mnt_idmap *idmap, struct inode *dir, btrfs_set_file_extent_encryption(leaf, ei, 0); btrfs_set_file_extent_compression(leaf, ei, 0); btrfs_set_file_extent_other_encoding(leaf, ei, 0); - btrfs_set_file_extent_ram_bytes(leaf, ei, name_len); + btrfs_set_file_extent_ram_bytes(leaf, ei, disk_link.len - 1); ptr = btrfs_file_extent_inline_start(ei); - write_extent_buffer(leaf, symname, ptr, name_len); + write_extent_buffer(leaf, disk_link.name, ptr, disk_link.len - 1); btrfs_free_path(path); d_instantiate_new(dentry, inode); ret = 0; +free_name: + if (disk_link.name != (unsigned char *)symname) + kfree(disk_link.name); out: btrfs_end_transaction(trans); btrfs_btree_balance_dirty(fs_info); @@ -9131,13 +9301,39 @@ static int btrfs_symlink(struct mnt_idmap *idmap, struct inode *dir, return ret; } +static const char *btrfs_get_link(struct dentry *dentry, struct inode *inode, + struct delayed_call *done) +{ + struct page *cpage; + const char *paddr; + struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); + + if (!IS_ENCRYPTED(inode)) + return page_get_link(dentry, inode, done); + + if (!dentry) + return ERR_PTR(-ECHILD); + + cpage = read_mapping_page(inode->i_mapping, 0, NULL); + if (IS_ERR(cpage)) + return ERR_CAST(cpage); + + paddr = fscrypt_get_symlink(inode, page_address(cpage), + BTRFS_MAX_INLINE_DATA_SIZE(fs_info), done); + put_page(cpage); + return paddr; +} + static struct btrfs_trans_handle *insert_prealloc_file_extent( struct btrfs_trans_handle *trans_in, struct btrfs_inode *inode, struct btrfs_key *ins, + struct fscrypt_extent_info *fscrypt_info, u64 file_offset) { struct btrfs_file_extent_item stack_fi; + u8 fscrypt_ctx[FSCRYPT_SET_CONTEXT_MAX_SIZE]; + ssize_t fscrypt_context_size; struct btrfs_replace_extent_info extent_info; struct btrfs_trans_handle *trans = trans_in; struct btrfs_path *path; @@ -9154,14 +9350,16 @@ static struct btrfs_trans_handle *insert_prealloc_file_extent( btrfs_set_stack_file_extent_num_bytes(&stack_fi, len); btrfs_set_stack_file_extent_ram_bytes(&stack_fi, len); btrfs_set_stack_file_extent_compression(&stack_fi, BTRFS_COMPRESS_NONE); - /* Encryption and other encoding is reserved and all 0 */ + btrfs_set_stack_file_extent_encryption(&stack_fi, fscrypt_info? BTRFS_ENCRYPTION_FSCRYPT: + BTRFS_ENCRYPTION_NONE); + /* Other encoding is reserved and always 0 */ ret = btrfs_qgroup_release_data(inode, file_offset, len, &qgroup_released); if (ret < 0) return ERR_PTR(ret); if (trans) { - ret = insert_reserved_file_extent(trans, inode, + ret = insert_reserved_file_extent(trans, inode, fscrypt_info, file_offset, &stack_fi, true, qgroup_released); if (ret) @@ -9169,6 +9367,14 @@ static struct btrfs_trans_handle *insert_prealloc_file_extent( return trans; } + fscrypt_context_size = btrfs_fscrypt_context_for_new_extent(inode, + fscrypt_info, + fscrypt_ctx); + if (fscrypt_context_size < 0) { + ret = (int)fscrypt_context_size; + goto free_qgroup; + } + extent_info.disk_offset = start; extent_info.disk_len = len; extent_info.data_offset = 0; @@ -9179,6 +9385,8 @@ static struct btrfs_trans_handle *insert_prealloc_file_extent( extent_info.update_times = true; extent_info.qgroup_reserved = qgroup_released; extent_info.insertions = 0; + extent_info.fscrypt_ctx = fscrypt_ctx; + extent_info.fscrypt_context_size = fscrypt_context_size; path = btrfs_alloc_path(); if (!path) { @@ -9229,6 +9437,9 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode, if (trans) own_trans = false; while (num_bytes > 0) { + struct fscrypt_extent_info *fscrypt_info = NULL; + int encryption_type = BTRFS_ENCRYPTION_NONE; + cur_bytes = min_t(u64, num_bytes, SZ_256M); cur_bytes = max(cur_bytes, min_size); /* @@ -9243,6 +9454,17 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode, if (ret) break; + if (IS_ENCRYPTED(inode)) { + fscrypt_info = fscrypt_prepare_new_extent(inode); + if (IS_ERR(fscrypt_info)) { + btrfs_dec_block_group_reservations(fs_info, ins.objectid); + btrfs_free_reserved_extent(fs_info, ins.objectid, ins.offset, 0); + ret = PTR_ERR(fscrypt_info); + break; + } + encryption_type = BTRFS_ENCRYPTION_FSCRYPT; + } + /* * We've reserved this space, and thus converted it from * ->bytes_may_use to ->bytes_reserved. Any error that happens @@ -9254,7 +9476,7 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode, last_alloc = ins.offset; trans = insert_prealloc_file_extent(trans, BTRFS_I(inode), - &ins, cur_offset); + &ins, fscrypt_info, cur_offset); /* * Now that we inserted the prealloc extent we can finally * decrement the number of reservations in the block group. @@ -9264,6 +9486,7 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode, btrfs_dec_block_group_reservations(fs_info, ins.objectid); if (IS_ERR(trans)) { ret = PTR_ERR(trans); + fscrypt_put_extent_info(fscrypt_info); btrfs_free_reserved_extent(fs_info, ins.objectid, ins.offset, false); break; @@ -9271,6 +9494,7 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode, em = btrfs_alloc_extent_map(); if (!em) { + fscrypt_put_extent_info(fscrypt_info); btrfs_drop_extent_map_range(BTRFS_I(inode), cur_offset, cur_offset + ins.offset - 1, false); btrfs_set_inode_full_sync(BTRFS_I(inode)); @@ -9285,6 +9509,8 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode, em->ram_bytes = ins.offset; em->flags |= EXTENT_FLAG_PREALLOC; em->generation = trans->transid; + em->fscrypt_info = fscrypt_info; + btrfs_extent_map_set_encryption(em, encryption_type); ret = btrfs_replace_extent_map_range(BTRFS_I(inode), em, true); btrfs_free_extent_map(em); @@ -10072,16 +10298,19 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from, file_extent.ram_bytes = ram_bytes; file_extent.offset = encoded->unencoded_offset; file_extent.compression = compression; + file_extent.fscrypt_info = NULL; em = btrfs_create_io_em(inode, start, &file_extent, BTRFS_ORDERED_COMPRESSED); if (IS_ERR(em)) { ret = PTR_ERR(em); goto out_free_reserved; } - btrfs_free_extent_map(em); + file_extent.fscrypt_info = em->fscrypt_info; + file_extent.orig_offset = start - encoded->unencoded_offset; ordered = btrfs_alloc_ordered_extent(inode, start, &file_extent, (1U << BTRFS_ORDERED_ENCODED) | (1U << BTRFS_ORDERED_COMPRESSED)); + btrfs_free_extent_map(em); if (IS_ERR(ordered)) { btrfs_drop_extent_map_range(inode, start, end, false); ret = PTR_ERR(ordered); @@ -10762,7 +10991,7 @@ static const struct inode_operations btrfs_special_inode_operations = { .update_time = btrfs_update_time, }; static const struct inode_operations btrfs_symlink_inode_operations = { - .get_link = page_get_link, + .get_link = btrfs_get_link, .getattr = btrfs_getattr, .setattr = btrfs_setattr, .permission = btrfs_permission, @@ -10772,4 +11001,7 @@ static const struct inode_operations btrfs_symlink_inode_operations = { const struct dentry_operations btrfs_dentry_operations = { .d_delete = btrfs_dentry_delete, +#ifdef CONFIG_FS_ENCRYPTION + .d_revalidate = fscrypt_d_revalidate, +#endif }; diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index a39460bf68a7..873fe08cd19c 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -153,6 +153,8 @@ static unsigned int btrfs_inode_flags_to_fsflags(const struct btrfs_inode *inode iflags |= FS_DIRSYNC_FL; if (flags & BTRFS_INODE_NODATACOW) iflags |= FS_NOCOW_FL; + if (flags & BTRFS_INODE_ENCRYPT) + iflags |= FS_ENCRYPT_FL; if (ro_flags & BTRFS_INODE_RO_VERITY) iflags |= FS_VERITY_FL; @@ -181,12 +183,14 @@ void btrfs_sync_inode_flags_to_i_flags(struct btrfs_inode *inode) new_fl |= S_NOATIME; if (inode->flags & BTRFS_INODE_DIRSYNC) new_fl |= S_DIRSYNC; + if (inode->flags & BTRFS_INODE_ENCRYPT) + new_fl |= S_ENCRYPTED; if (inode->ro_flags & BTRFS_INODE_RO_VERITY) new_fl |= S_VERITY; set_mask_bits(&inode->vfs_inode.i_flags, S_SYNC | S_APPEND | S_IMMUTABLE | S_NOATIME | S_DIRSYNC | - S_VERITY, new_fl); + S_VERITY | S_ENCRYPTED, new_fl); } /* @@ -199,7 +203,7 @@ static int check_fsflags(unsigned int old_flags, unsigned int flags) FS_NOATIME_FL | FS_NODUMP_FL | \ FS_SYNC_FL | FS_DIRSYNC_FL | \ FS_NOCOMP_FL | FS_COMPR_FL | - FS_NOCOW_FL)) + FS_NOCOW_FL | FS_ENCRYPT_FL)) return -EOPNOTSUPP; /* COMPR and NOCOMP on new/old are valid */ @@ -5155,6 +5159,39 @@ long btrfs_ioctl(struct file *file, unsigned int return btrfs_ioctl_get_fslabel(fs_info, argp); case FS_IOC_SETFSLABEL: return btrfs_ioctl_set_fslabel(file, argp); +#ifdef CONFIG_BTRFS_EXPERIMENTAL + case FS_IOC_SET_ENCRYPTION_POLICY: { + if (!IS_ENABLED(CONFIG_FS_ENCRYPTION)) + return -EOPNOTSUPP; + if (sb_rdonly(fs_info->sb)) + return -EROFS; + if (btrfs_fs_incompat(fs_info, RAID56)) { + btrfs_warn(fs_info, "can't enable encryption with RAID5/6"); + return -EINVAL; + } + /* + * If we crash before we commit, nothing encrypted could have + * been written so it doesn't matter whether the encrypted + * state persists. + */ + btrfs_set_fs_incompat(fs_info, ENCRYPT); + return fscrypt_ioctl_set_policy(file, (const void __user *)arg); + } + case FS_IOC_GET_ENCRYPTION_POLICY: + return fscrypt_ioctl_get_policy(file, (void __user *)arg); + case FS_IOC_GET_ENCRYPTION_POLICY_EX: + return fscrypt_ioctl_get_policy_ex(file, (void __user *)arg); + case FS_IOC_ADD_ENCRYPTION_KEY: + return fscrypt_ioctl_add_key(file, (void __user *)arg); + case FS_IOC_REMOVE_ENCRYPTION_KEY: + return fscrypt_ioctl_remove_key(file, (void __user *)arg); + case FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS: + return fscrypt_ioctl_remove_key_all_users(file, (void __user *)arg); + case FS_IOC_GET_ENCRYPTION_KEY_STATUS: + return fscrypt_ioctl_get_key_status(file, (void __user *)arg); + case FS_IOC_GET_ENCRYPTION_NONCE: + return fscrypt_ioctl_get_nonce(file, (void __user *)arg); +#endif /* CONFIG_BTRFS_EXPERIMENTAL */ case FITRIM: return btrfs_ioctl_fitrim(fs_info, argp); case BTRFS_IOC_SNAP_CREATE: diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index e5a24b3ff95e..f796dd7ea925 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -146,9 +146,11 @@ static inline struct rb_node *ordered_tree_search(struct btrfs_inode *inode, } static struct btrfs_ordered_extent *alloc_ordered_extent( - struct btrfs_inode *inode, u64 file_offset, u64 num_bytes, + struct btrfs_inode *inode, + u64 file_offset, u64 orig_offset, u64 num_bytes, u64 ram_bytes, u64 disk_bytenr, u64 disk_num_bytes, - u64 offset, unsigned long flags, int compress_type) + u64 offset, unsigned long flags, int compress_type, + struct fscrypt_extent_info *fscrypt_info) { struct btrfs_ordered_extent *entry; int ret; @@ -192,6 +194,7 @@ static struct btrfs_ordered_extent *alloc_ordered_extent( } entry->file_offset = file_offset; + entry->orig_offset = orig_offset; entry->num_bytes = num_bytes; entry->ram_bytes = ram_bytes; entry->disk_bytenr = disk_bytenr; @@ -208,6 +211,9 @@ static struct btrfs_ordered_extent *alloc_ordered_extent( entry->truncated_len = (u64)-1; entry->qgroup_rsv = qgroup_rsv; entry->flags = flags; + entry->fscrypt_info = fscrypt_get_extent_info(fscrypt_info); + entry->encryption_type = entry->fscrypt_info ? + BTRFS_ENCRYPTION_FSCRYPT : BTRFS_ENCRYPTION_NONE; refcount_set(&entry->refs, 1); init_waitqueue_head(&entry->wait); INIT_LIST_HEAD(&entry->csum_list); @@ -282,6 +288,7 @@ static void insert_ordered_extent(struct btrfs_ordered_extent *entry) * * @inode: Inode that this extent is for. * @file_offset: Logical offset in file where the extent starts. + * @orig_offset: Logical offset of the original extent (PREALLOC or NOCOW) * @num_bytes: Logical length of extent in file. * @ram_bytes: Full length of unencoded data. * @disk_bytenr: Offset of extent on disk. @@ -289,6 +296,7 @@ static void insert_ordered_extent(struct btrfs_ordered_extent *entry) * @offset: Offset into unencoded data where file data starts. * @flags: Flags specifying type of extent (1U << BTRFS_ORDERED_*). * @compress_type: Compression algorithm used for data. + * @fscrypt_info: The fscrypt_extent_info for this extent, if necessary. * * Most of these parameters correspond to &struct btrfs_file_extent_item. The * tree is given a single reference on the ordered extent that was inserted, and @@ -318,19 +326,23 @@ struct btrfs_ordered_extent *btrfs_alloc_ordered_extent( */ if (flags & ((1U << BTRFS_ORDERED_NOCOW) | (1U << BTRFS_ORDERED_PREALLOC))) entry = alloc_ordered_extent(inode, file_offset, + file_extent->orig_offset, file_extent->num_bytes, file_extent->num_bytes, file_extent->disk_bytenr + file_extent->offset, file_extent->num_bytes, 0, flags, - file_extent->compression); + file_extent->compression, + file_extent->fscrypt_info); else entry = alloc_ordered_extent(inode, file_offset, + file_extent->orig_offset, file_extent->num_bytes, file_extent->ram_bytes, file_extent->disk_bytenr, file_extent->disk_num_bytes, file_extent->offset, flags, - file_extent->compression); + file_extent->compression, + file_extent->fscrypt_info); if (!IS_ERR(entry)) insert_ordered_extent(entry); return entry; @@ -629,6 +641,7 @@ void btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry) btrfs_add_delayed_iput(entry->inode); list_for_each_entry_safe(sum, tmp, &entry->csum_list, list) kvfree(sum); + fscrypt_put_extent_info(entry->fscrypt_info); kmem_cache_free(btrfs_ordered_extent_cache, entry); } } @@ -1268,8 +1281,8 @@ struct btrfs_ordered_extent *btrfs_split_ordered_extent( if (WARN_ON_ONCE(ordered->disk_num_bytes != ordered->num_bytes)) return ERR_PTR(-EINVAL); - new = alloc_ordered_extent(inode, file_offset, len, len, disk_bytenr, - len, 0, flags, ordered->compress_type); + new = alloc_ordered_extent(inode, file_offset, ordered->orig_offset, len, len, disk_bytenr, + len, 0, flags, ordered->compress_type, ordered->fscrypt_info); if (IS_ERR(new)) return new; @@ -1306,6 +1319,16 @@ struct btrfs_ordered_extent *btrfs_split_ordered_extent( ordered->disk_num_bytes -= len; ordered->ram_bytes -= len; + /* + * ->orig_offset is the original offset of the original extent, which + * for PREALLOC or NOCOW stays the same, but if we're a regular extent + * that means this is a new extent and thus ->orig_offset must equal + * ->file_offset. This is only important for encryption as we only use + * it for setting the offset for the bio encryption context. + */ + if (test_bit(BTRFS_ORDERED_REGULAR, &ordered->flags)) + ordered->orig_offset = ordered->file_offset; + if (test_bit(BTRFS_ORDERED_IO_DONE, &ordered->flags)) { ASSERT(ordered->bytes_left == 0); new->bytes_left = 0; diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h index 03e12380a2fd..f4ad5ff1a1e2 100644 --- a/fs/btrfs/ordered-data.h +++ b/fs/btrfs/ordered-data.h @@ -106,6 +106,12 @@ struct btrfs_ordered_extent { /* logical offset in the file */ u64 file_offset; + /* + * The original logical offset of the extent, this is for NOCOW and + * PREALLOC extents, otherwise it'll be the same as file_offset. + */ + u64 orig_offset; + /* * These fields directly correspond to the same fields in * btrfs_file_extent_item. @@ -131,6 +137,9 @@ struct btrfs_ordered_extent { /* compression algorithm */ int compress_type; + /* encryption mode */ + u8 encryption_type; + /* Qgroup reserved space */ int qgroup_rsv; @@ -140,6 +149,9 @@ struct btrfs_ordered_extent { /* the inode we belong to */ struct btrfs_inode *inode; + /* the fscrypt_info for this extent, if necessary */ + struct fscrypt_extent_info *fscrypt_info; + /* list of checksums for insertion when the extent io is done */ struct list_head csum_list; @@ -186,6 +198,8 @@ struct btrfs_file_extent { u64 num_bytes; u64 ram_bytes; u64 offset; + u64 orig_offset; + struct fscrypt_extent_info *fscrypt_info; u8 compression; }; diff --git a/fs/btrfs/reflink.c b/fs/btrfs/reflink.c index 49865a463780..ff431c05cb38 100644 --- a/fs/btrfs/reflink.c +++ b/fs/btrfs/reflink.c @@ -421,7 +421,7 @@ static int btrfs_clone(struct inode *src, struct inode *inode, struct btrfs_key new_key; u64 disko = 0, diskl = 0; u64 datao = 0, datal = 0; - u8 comp; + u8 comp, encryption; u64 drop_start; /* Note the key will change type as we walk through the tree */ @@ -464,6 +464,7 @@ static int btrfs_clone(struct inode *src, struct inode *inode, extent = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item); extent_gen = btrfs_file_extent_generation(leaf, extent); + encryption = btrfs_file_extent_encryption(leaf, extent); comp = btrfs_file_extent_compression(leaf, extent); type = btrfs_file_extent_type(leaf, extent); if (type == BTRFS_FILE_EXTENT_REG || @@ -523,6 +524,7 @@ static int btrfs_clone(struct inode *src, struct inode *inode, if (type == BTRFS_FILE_EXTENT_REG || type == BTRFS_FILE_EXTENT_PREALLOC) { struct btrfs_replace_extent_info clone_info; + u8 fscrypt_ctx[FSCRYPT_SET_CONTEXT_MAX_SIZE]; /* * a | --- range to clone ---| b @@ -539,6 +541,43 @@ static int btrfs_clone(struct inode *src, struct inode *inode, datal -= off - key.offset; } + if (encryption == BTRFS_ENCRYPTION_FSCRYPT) { + unsigned long offset; + + key.type = BTRFS_FSCRYPT_CTX_KEY; + ret = btrfs_search_slot(NULL, BTRFS_I(src)->root, &key, path, 0, 0); + if (ret < 0) + goto out; + leaf = path->nodes[0]; + slot = path->slots[0]; + if (ret) { + btrfs_err(leaf->fs_info, + "missing or error searching encryption context item in leaf: root=%llu block=%llu slot=%d ino=%llu file_offset=%llu, err %i", + btrfs_header_owner(leaf), + btrfs_header_bytenr(leaf), slot, + key.objectid, key.offset, ret); + goto out; + } + + size = btrfs_item_size(leaf, slot); + if (size > FSCRYPT_SET_CONTEXT_MAX_SIZE) { + btrfs_err(leaf->fs_info, + "unexpected or corrupted encryption context size in leaf: root=%llu block=%llu slot=%d ino=%llu file_offset=%llu, size %u (too big)", + btrfs_header_owner(leaf), + btrfs_header_bytenr(leaf), slot, + key.objectid, key.offset, size); + ret = -EIO; + goto out; + } + + offset = btrfs_item_ptr_offset(leaf, slot), + read_extent_buffer(leaf, fscrypt_ctx, offset, size); + btrfs_release_path(path); + key.type = BTRFS_EXTENT_DATA_KEY; + } else { + size = 0; + } + clone_info.disk_offset = disko; clone_info.disk_len = diskl; clone_info.data_offset = datao; @@ -547,6 +586,8 @@ static int btrfs_clone(struct inode *src, struct inode *inode, clone_info.extent_buf = buf; clone_info.is_new_extent = false; clone_info.update_times = !no_time_update; + clone_info.fscrypt_ctx = fscrypt_ctx; + clone_info.fscrypt_context_size = size; ret = btrfs_replace_file_extents(BTRFS_I(inode), path, drop_start, new_key.offset + datal - 1, &clone_info, &trans); diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c index d85a09ae1733..9f0f73200889 100644 --- a/fs/btrfs/root-tree.c +++ b/fs/btrfs/root-tree.c @@ -10,6 +10,7 @@ #include "messages.h" #include "transaction.h" #include "disk-io.h" +#include "fscrypt.h" #include "qgroup.h" #include "space-info.h" #include "accessors.h" @@ -328,7 +329,7 @@ int btrfs_del_root(struct btrfs_trans_handle *trans, int btrfs_del_root_ref(struct btrfs_trans_handle *trans, u64 root_id, u64 ref_id, u64 dirid, u64 *sequence, - const struct fscrypt_str *name) + struct fscrypt_name *name) { struct btrfs_root *tree_root = trans->fs_info->tree_root; BTRFS_PATH_AUTO_FREE(path); @@ -350,15 +351,15 @@ int btrfs_del_root_ref(struct btrfs_trans_handle *trans, u64 root_id, if (ret < 0) { return ret; } else if (ret == 0) { + u32 name_len; leaf = path->nodes[0]; ref = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_root_ref); ptr = (unsigned long)(ref + 1); + name_len = btrfs_root_ref_name_len(leaf, ref); if ((btrfs_root_ref_dirid(leaf, ref) != dirid) || - (btrfs_root_ref_name_len(leaf, ref) != name->len) || - memcmp_extent_buffer(leaf, name->name, ptr, name->len)) + !btrfs_fscrypt_match_name(name, leaf, ptr, name_len)) return -ENOENT; - *sequence = btrfs_root_ref_sequence(leaf, ref); ret = btrfs_del_item(trans, tree_root, path); diff --git a/fs/btrfs/root-tree.h b/fs/btrfs/root-tree.h index 8f5739e732b9..e85623dba952 100644 --- a/fs/btrfs/root-tree.h +++ b/fs/btrfs/root-tree.h @@ -23,7 +23,7 @@ int btrfs_add_root_ref(struct btrfs_trans_handle *trans, u64 root_id, const struct fscrypt_str *name); int btrfs_del_root_ref(struct btrfs_trans_handle *trans, u64 root_id, u64 ref_id, u64 dirid, u64 *sequence, - const struct fscrypt_str *name); + struct fscrypt_name *name); int btrfs_del_root(struct btrfs_trans_handle *trans, const struct btrfs_key *key); int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root *root, const struct btrfs_key *key, diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index 89d72d8cb85f..11a3e928a4c2 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -32,6 +32,7 @@ #include "ioctl.h" #include "verity.h" #include "lru_cache.h" +#include "fscrypt.h" /* * Maximum number of references an extent can have in order for us to attempt to @@ -579,13 +580,39 @@ static inline int fs_path_add_path(struct fs_path *p, const struct fs_path *p2) return fs_path_add(p, p2->start, fs_path_len(p2)); } -static int fs_path_add_from_extent_buffer(struct fs_path *p, +static int fs_path_add_from_encrypted(struct btrfs_root *root, + struct fs_path *p, + struct extent_buffer *eb, + unsigned long off, int len, + u64 parent_ino) +{ + struct fscrypt_str fname = FSTR_INIT(NULL, 0); + int ret; + + ret = fscrypt_fname_alloc_buffer(BTRFS_NAME_LEN, &fname); + if (ret) + return ret; + + ret = btrfs_decrypt_name(root, eb, off, len, parent_ino, &fname); + if (!ret) + ret = fs_path_add(p, fname.name, fname.len); + + fscrypt_fname_free_buffer(&fname); + return ret; +} + +static int fs_path_add_from_extent_buffer(struct btrfs_root *root, + struct fs_path *p, struct extent_buffer *eb, - unsigned long off, int len) + unsigned long off, int len, + u64 parent_ino) { int ret; char *prepared; + if (root && btrfs_fs_incompat(root->fs_info, ENCRYPT)) + return fs_path_add_from_encrypted(root, p, eb, off, len, parent_ino); + ret = fs_path_prepare_for_add(p, len, &prepared); if (ret < 0) return ret; @@ -1062,8 +1089,7 @@ static int iterate_inode_ref(struct btrfs_root *root, struct btrfs_path *path, } p->start = start; } else { - ret = fs_path_add_from_extent_buffer(p, eb, name_off, - name_len); + ret = fs_path_add_from_extent_buffer(root, p, eb, name_off, name_len, dir); if (ret < 0) goto out; } @@ -1701,9 +1727,7 @@ static int find_extent_clone(struct send_ctx *sctx, return ret; } -static int read_symlink(struct btrfs_root *root, - u64 ino, - struct fs_path *dest) +static int read_symlink_unencrypted(struct btrfs_root *root, u64 ino, struct fs_path *dest) { int ret; BTRFS_PATH_AUTO_FREE(path); @@ -1761,7 +1785,48 @@ static int read_symlink(struct btrfs_root *root, off = btrfs_file_extent_inline_start(ei); len = btrfs_file_extent_ram_bytes(path->nodes[0], ei); - return fs_path_add_from_extent_buffer(dest, path->nodes[0], off, len); + return fs_path_add_from_extent_buffer(NULL, dest, path->nodes[0], off, len, 0); +} + +static int read_symlink_encrypted(struct btrfs_root *root, u64 ino, struct fs_path *dest) +{ + DEFINE_DELAYED_CALL(done); + const char *buf; + struct folio *folio; + struct btrfs_inode *inode; + int ret = 0; + + inode = btrfs_iget(ino, root); + if (IS_ERR(inode)) + return PTR_ERR(inode); + + folio = read_mapping_folio(inode->vfs_inode.i_mapping, 0, NULL); + if (IS_ERR(folio)) { + iput(&inode->vfs_inode); + return PTR_ERR(folio); + } + + buf = fscrypt_get_symlink(&inode->vfs_inode, folio_address(folio), + BTRFS_MAX_INLINE_DATA_SIZE(root->fs_info), + &done); + folio_put(folio); + iput(&inode->vfs_inode); + + if (IS_ERR(buf)) + return PTR_ERR(buf); + + ret = fs_path_add(dest, buf, strlen(buf)); + do_delayed_call(&done); + return ret; +} + + +static int read_symlink(struct btrfs_root *root, u64 ino, + struct fs_path *dest) +{ + if (btrfs_fs_incompat(root->fs_info, ENCRYPT)) + return read_symlink_encrypted(root, ino, dest); + return read_symlink_unencrypted(root, ino, dest); } /* @@ -1995,18 +2060,19 @@ static int get_first_ref(struct btrfs_root *root, u64 ino, iref = btrfs_item_ptr(path->nodes[0], path->slots[0], struct btrfs_inode_ref); len = btrfs_inode_ref_name_len(path->nodes[0], iref); - ret = fs_path_add_from_extent_buffer(name, path->nodes[0], - (unsigned long)(iref + 1), - len); parent_dir = found_key.offset; + ret = fs_path_add_from_extent_buffer(root, name, path->nodes[0], + (unsigned long)(iref + 1), + len, parent_dir); } else { struct btrfs_inode_extref *extref; extref = btrfs_item_ptr(path->nodes[0], path->slots[0], struct btrfs_inode_extref); len = btrfs_inode_extref_name_len(path->nodes[0], extref); - ret = fs_path_add_from_extent_buffer(name, path->nodes[0], - (unsigned long)&extref->name, len); parent_dir = btrfs_inode_extref_parent(path->nodes[0], extref); + ret = fs_path_add_from_extent_buffer(root, name, path->nodes[0], + (unsigned long)&extref->name, len, + parent_dir); } if (ret < 0) return ret; @@ -5265,6 +5331,38 @@ static int put_file_data(struct send_ctx *sctx, u64 offset, u32 len) return ret; } +static int load_fscrypt_context(struct send_ctx *sctx) +{ + struct btrfs_root *root = sctx->send_root; + struct name_cache_entry *nce; + struct btrfs_inode *dir; + int ret; + + if (!btrfs_fs_incompat(root->fs_info, ENCRYPT)) + return 0; + + /* + * If we're encrypted we need to load the parent inode in order to make + * sure the encryption context is loaded. We have to do this even if + * we're not encrypted, as we need to make sure that we don't violate + * the rule about encrypted children with non-encrypted parents, which + * is enforced by __fscrypt_file_open. + */ + nce = name_cache_search(sctx, sctx->cur_ino, sctx->cur_inode_gen); + if (!nce) { + ASSERT(nce); + return -EINVAL; + } + + dir = btrfs_iget(nce->parent_ino, root); + if (IS_ERR(dir)) + return PTR_ERR(dir); + + ret = __fscrypt_file_open(&dir->vfs_inode, sctx->cur_inode); + iput(&dir->vfs_inode); + return ret; +} + /* * Read some bytes from the current inode/file and send a write command to * user space. @@ -5282,6 +5380,10 @@ static int send_write(struct send_ctx *sctx, u64 offset, u32 len) if (ret < 0) return ret; + ret = load_fscrypt_context(sctx); + if (ret < 0) + return ret; + TLV_PUT_PATH(sctx, BTRFS_SEND_A_PATH, p); TLV_PUT_U64(sctx, BTRFS_SEND_A_FILE_OFFSET, offset); ret = put_file_data(sctx, offset, len); @@ -7993,6 +8095,12 @@ long btrfs_ioctl_send(struct btrfs_root *send_root, const struct btrfs_ioctl_sen if (!capable(CAP_SYS_ADMIN)) return -EPERM; + if (btrfs_fs_incompat(fs_info, ENCRYPT)) { + btrfs_err(fs_info, + "send with encryption enabled isn't currently suported"); + return -EINVAL; + } + /* * The subvolume must remain read-only during send, protect against * making it RW. This also protects against deletion. diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index b26aa9169e83..5072ead57d49 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -49,6 +49,7 @@ #include "tests/btrfs-tests.h" #include "block-group.h" #include "discard.h" +#include "fscrypt.h" #include "qgroup.h" #include "raid56.h" #include "fs.h" @@ -86,6 +87,7 @@ struct btrfs_fs_context { unsigned long compress_type:4; int compress_level; refcount_t refs; + struct fscrypt_dummy_policy dummy_enc_policy; }; static void btrfs_emit_options(struct btrfs_fs_info *info, @@ -124,6 +126,7 @@ enum { Opt_treelog, Opt_user_subvol_rm_allowed, Opt_norecovery, + Opt_test_dummy_encryption, /* Rescue options */ Opt_rescue, @@ -258,6 +261,8 @@ static const struct fs_parameter_spec btrfs_fs_parameters[] = { fsparam_enum("fragment", Opt_fragment, btrfs_parameter_fragment), fsparam_flag("ref_tracker", Opt_ref_tracker), fsparam_flag("ref_verify", Opt_ref_verify), + fsparam_flag("test_dummy_encryption", Opt_test_dummy_encryption), + fsparam_string("test_dummy_encryption", Opt_test_dummy_encryption), #endif {} }; @@ -650,6 +655,23 @@ static int btrfs_parse_param(struct fs_context *fc, struct fs_parameter *param) case Opt_ref_tracker: btrfs_set_opt(ctx->mount_opt, REF_TRACKER); break; + case Opt_test_dummy_encryption: + int ret; + + /* + * We only support v2, so reject any v1 policies. + */ + if (param->type == fs_value_is_string && *param->string && + !strcmp(param->string, "v1")) { + btrfs_info(NULL, "v1 encryption isn't supported"); + return -EINVAL; + } + + btrfs_set_opt(ctx->mount_opt, TEST_DUMMY_ENCRYPTION); + ret = fscrypt_parse_test_dummy_encryption(param, &ctx->dummy_enc_policy); + if (ret) + return ret; + break; #endif default: btrfs_err(NULL, "unrecognized mount option '%s'", param->key); @@ -712,6 +734,12 @@ bool btrfs_check_options(const struct btrfs_fs_info *info, if (btrfs_check_mountopts_zoned(info, mount_opt)) ret = false; + if (btrfs_fs_incompat(info, RAID56) && + btrfs_raw_test_opt(*mount_opt, TEST_DUMMY_ENCRYPTION)) { + btrfs_err(info, "cannot use test_dummy_encryption with RAID5/6"); + ret = false; + } + if (!test_bit(BTRFS_FS_STATE_REMOUNTING, &info->fs_state)) { if (btrfs_raw_test_opt(*mount_opt, SPACE_CACHE)) { btrfs_warn(info, @@ -723,10 +751,10 @@ bool btrfs_check_options(const struct btrfs_fs_info *info, } /* - * This is subtle, we only call this during open_ctree(). We need to pre-load - * the mount options with the on-disk settings. Before the new mount API took - * effect we would do this on mount and remount. With the new mount API we'll - * only do this on the initial mount. + * Because we have an odd set of behavior with turning on and off the space cache + * and free space tree we have to call this before we start the mount operation + * after we load the super, or before we start remount. This is to make sure we + * have the proper free space settings in place if the user didn't specify any. * * This isn't a change in behavior, because we're using the current state of the * file system to set the current mount options. If you mounted with special @@ -734,21 +762,22 @@ bool btrfs_check_options(const struct btrfs_fs_info *info, * settings, because mounting without these features cleared the on-disk * settings, so this being called on re-mount is not needed. */ -void btrfs_set_free_space_cache_settings(struct btrfs_fs_info *fs_info) +void btrfs_set_free_space_cache_settings(struct btrfs_fs_info *fs_info, + unsigned long long *mount_opt) { - if (fs_info->sectorsize != PAGE_SIZE && btrfs_test_opt(fs_info, SPACE_CACHE)) { + if (fs_info->sectorsize != PAGE_SIZE && btrfs_raw_test_opt(*mount_opt, SPACE_CACHE)) { btrfs_info(fs_info, "forcing free space tree for sector size %u with page size %lu", fs_info->sectorsize, PAGE_SIZE); - btrfs_clear_opt(fs_info->mount_opt, SPACE_CACHE); - btrfs_set_opt(fs_info->mount_opt, FREE_SPACE_TREE); + btrfs_clear_opt(*mount_opt, SPACE_CACHE); + btrfs_set_opt(*mount_opt, FREE_SPACE_TREE); } /* * At this point our mount options are populated, so we only mess with * these settings if we don't have any settings already. */ - if (btrfs_test_opt(fs_info, FREE_SPACE_TREE)) + if (btrfs_raw_test_opt(*mount_opt, FREE_SPACE_TREE)) return; if (btrfs_is_zoned(fs_info) && @@ -758,10 +787,10 @@ void btrfs_set_free_space_cache_settings(struct btrfs_fs_info *fs_info) return; } - if (btrfs_test_opt(fs_info, SPACE_CACHE)) + if (btrfs_raw_test_opt(*mount_opt, SPACE_CACHE)) return; - if (btrfs_test_opt(fs_info, NOSPACECACHE)) + if (btrfs_raw_test_opt(*mount_opt, NOSPACECACHE)) return; /* @@ -769,9 +798,9 @@ void btrfs_set_free_space_cache_settings(struct btrfs_fs_info *fs_info) * them ourselves based on the state of the file system. */ if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) - btrfs_set_opt(fs_info->mount_opt, FREE_SPACE_TREE); + btrfs_set_opt(*mount_opt, FREE_SPACE_TREE); else if (btrfs_free_space_cache_v1_active(fs_info)) - btrfs_set_opt(fs_info->mount_opt, SPACE_CACHE); + btrfs_set_opt(*mount_opt, SPACE_CACHE); } static void set_device_specific_options(struct btrfs_fs_info *fs_info) @@ -969,6 +998,7 @@ static int btrfs_fill_super(struct super_block *sb, sb->s_vop = &btrfs_verityops; #endif sb->s_xattr = btrfs_xattr_handlers; + fscrypt_set_ops(sb, &btrfs_fscrypt_ops); sb->s_time_gran = 1; sb->s_iflags |= SB_I_CGROUPWB | SB_I_ALLOW_HSM; @@ -984,6 +1014,9 @@ static int btrfs_fill_super(struct super_block *sb, return ret; } + if (fscrypt_is_dummy_policy_set(&fs_info->dummy_enc_policy)) + btrfs_set_fs_incompat(fs_info, ENCRYPT); + btrfs_emit_options(fs_info, NULL); inode = btrfs_iget(BTRFS_FIRST_FREE_OBJECTID, fs_info->fs_root); @@ -1148,6 +1181,8 @@ static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry) seq_puts(seq, ",ref_verify"); if (btrfs_test_opt(info, REF_TRACKER)) seq_puts(seq, ",ref_tracker"); + if (btrfs_test_opt(info, TEST_DUMMY_ENCRYPTION)) + fscrypt_show_test_dummy_encryption(seq, ',', dentry->d_sb); seq_printf(seq, ",subvolid=%llu", btrfs_root_id(BTRFS_I(d_inode(dentry))->root)); subvol_name = btrfs_get_subvol_name_from_objectid(info, btrfs_root_id(BTRFS_I(d_inode(dentry))->root)); @@ -1413,6 +1448,18 @@ static void btrfs_ctx_to_info(struct btrfs_fs_info *fs_info, struct btrfs_fs_con fs_info->mount_opt = ctx->mount_opt; fs_info->compress_type = ctx->compress_type; fs_info->compress_level = ctx->compress_level; + + /* + * If there's nothing set, or if the fs_info already has one set, don't + * do anything. If the fs_info is set we'll free the dummy one when we + * free the ctx. + */ + if (!fscrypt_is_dummy_policy_set(&ctx->dummy_enc_policy) || + fscrypt_is_dummy_policy_set(&fs_info->dummy_enc_policy)) + return; + + fs_info->dummy_enc_policy = ctx->dummy_enc_policy; + memset(&ctx->dummy_enc_policy, 0, sizeof(ctx->dummy_enc_policy)); } static void btrfs_info_to_ctx(struct btrfs_fs_info *fs_info, struct btrfs_fs_context *ctx) @@ -1466,6 +1513,7 @@ static void btrfs_emit_options(struct btrfs_fs_info *info, btrfs_info_if_set(info, old, IGNOREDATACSUMS, "ignoring data csums"); btrfs_info_if_set(info, old, IGNOREMETACSUMS, "ignoring meta csums"); btrfs_info_if_set(info, old, IGNORESUPERFLAGS, "ignoring unknown super block flags"); + btrfs_info_if_set(info, old, TEST_DUMMY_ENCRYPTION, "test dummy encryption mode enabled"); btrfs_info_if_unset(info, old, NODATASUM, "setting datasum"); btrfs_info_if_unset(info, old, NODATACOW, "setting datacow"); @@ -1496,6 +1544,21 @@ static void btrfs_emit_options(struct btrfs_fs_info *info, btrfs_info(info, "max_inline set to %llu", info->max_inline); } +static bool btrfs_check_test_dummy_encryption(struct fs_context *fc) +{ + struct btrfs_fs_context *ctx = fc->fs_private; + struct btrfs_fs_info *fs_info = btrfs_sb(fc->root->d_sb); + + if (!fscrypt_is_dummy_policy_set(&ctx->dummy_enc_policy)) + return true; + + if (fscrypt_dummy_policies_equal(&fs_info->dummy_enc_policy, &ctx->dummy_enc_policy)) + return true; + + btrfs_warn(fs_info, "Can't set or change test_dummy_encryption on remount"); + return false; +} + static int btrfs_reconfigure(struct fs_context *fc) { struct super_block *sb = fc->root->d_sb; @@ -1517,10 +1580,14 @@ static int btrfs_reconfigure(struct fs_context *fc) sync_filesystem(sb); set_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state); + btrfs_set_free_space_cache_settings(fs_info, &ctx->mount_opt); if (!btrfs_check_options(fs_info, &ctx->mount_opt, fc->sb_flags)) return -EINVAL; + if (!mount_reconfigure && !btrfs_check_test_dummy_encryption(fc)) + return -EINVAL; + ret = btrfs_check_features(fs_info, !(fc->sb_flags & SB_RDONLY)); if (ret < 0) return ret; @@ -2137,6 +2204,7 @@ static void btrfs_free_fs_context(struct fs_context *fc) btrfs_free_fs_info(fs_info); if (ctx && refcount_dec_and_test(&ctx->refs)) { + fscrypt_free_dummy_policy(&ctx->dummy_enc_policy); kfree(ctx->subvol_name); kfree(ctx); } @@ -2561,6 +2629,11 @@ static int __init btrfs_print_mod_info(void) ", fsverity=yes" #else ", fsverity=no" +#endif +#ifdef CONFIG_FS_ENCRYPTION + ", fscrypt=yes" +#else + ", fscrypt=no" #endif ; diff --git a/fs/btrfs/super.h b/fs/btrfs/super.h index f85f8a8a7bfe..84a30b82a3ce 100644 --- a/fs/btrfs/super.h +++ b/fs/btrfs/super.h @@ -16,7 +16,8 @@ bool btrfs_check_options(const struct btrfs_fs_info *info, int btrfs_sync_fs(struct super_block *sb, int wait); char *btrfs_get_subvol_name_from_objectid(struct btrfs_fs_info *fs_info, u64 subvol_objectid); -void btrfs_set_free_space_cache_settings(struct btrfs_fs_info *fs_info); +void btrfs_set_free_space_cache_settings(struct btrfs_fs_info *fs_info, + unsigned long long *mount_opt); static inline struct btrfs_fs_info *btrfs_sb(const struct super_block *sb) { diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index 0d14570c8bc2..3fe57843f902 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -305,6 +305,9 @@ BTRFS_FEAT_ATTR_INCOMPAT(remap_tree, REMAP_TREE); #ifdef CONFIG_FS_VERITY BTRFS_FEAT_ATTR_COMPAT_RO(verity, VERITY); #endif +#ifdef CONFIG_FS_ENCRYPTION +BTRFS_FEAT_ATTR_INCOMPAT(encryption, ENCRYPT); +#endif /* CONFIG_FS_ENCRYPTION */ /* * Features which depend on feature bits and may differ between each fs. @@ -338,6 +341,9 @@ static struct attribute *btrfs_supported_feature_attrs[] = { #ifdef CONFIG_FS_VERITY BTRFS_FEAT_ATTR_PTR(verity), #endif +#ifdef CONFIG_FS_ENCRYPTION + BTRFS_FEAT_ATTR_PTR(encryption), +#endif /* CONFIG_FS_ENCRYPTION */ NULL }; diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c index 1f15d0793a9c..fa20bdf9f1d1 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c @@ -186,6 +186,7 @@ static bool check_prev_ino(struct extent_buffer *leaf, key->type == BTRFS_INODE_EXTREF_KEY || key->type == BTRFS_DIR_INDEX_KEY || key->type == BTRFS_DIR_ITEM_KEY || + key->type == BTRFS_FSCRYPT_CTX_KEY || key->type == BTRFS_EXTENT_DATA_KEY, "key->type=%u", key->type); /* @@ -204,6 +205,39 @@ static bool check_prev_ino(struct extent_buffer *leaf, prev_key->objectid, key->objectid); return false; } + +static int check_fscrypt_context(struct extent_buffer *leaf, + struct btrfs_key *key, int slot, + struct btrfs_key *prev_key) +{ + u32 sectorsize = leaf->fs_info->sectorsize; + u32 item_size = btrfs_item_size(leaf, slot); + + if (unlikely(!IS_ALIGNED(key->offset, sectorsize))) { + file_extent_err(leaf, slot, +"unaligned file_offset for encryption context, have %llu should be aligned to %u", + key->offset, sectorsize); + return -EUCLEAN; + } + + /* + * Previous key must have the same key->objectid (ino). It can be + * XATTR_ITEM, INODE_ITEM, FSCRYPT_INODE_CTX_KEY, or another FSCRYPT_CTX_KEY. + * But if objectids mismatch, it means we have a missing INODE_ITEM. + */ + if (unlikely(!check_prev_ino(leaf, key, slot, prev_key))) + return -EUCLEAN; + + if (unlikely(item_size > BTRFS_MAX_EXTENT_CTX_SIZE)) { + file_extent_err(leaf, slot, + "invalid encryption context size, have %u expect a maximum of %u", + item_size, BTRFS_MAX_EXTENT_CTX_SIZE); + return -EUCLEAN; + } + + return 0; +} + static int check_extent_data_item(struct extent_buffer *leaf, struct btrfs_key *key, int slot, struct btrfs_key *prev_key) @@ -213,6 +247,8 @@ static int check_extent_data_item(struct extent_buffer *leaf, u32 sectorsize = fs_info->sectorsize; u32 item_size = btrfs_item_size(leaf, slot); u64 extent_end; + u8 policy; + u8 fe_type; if (unlikely(!IS_ALIGNED(key->offset, sectorsize))) { file_extent_err(leaf, slot, @@ -243,12 +279,12 @@ static int check_extent_data_item(struct extent_buffer *leaf, SZ_4K); return -EUCLEAN; } - if (unlikely(btrfs_file_extent_type(leaf, fi) >= - BTRFS_NR_FILE_EXTENT_TYPES)) { + + fe_type = btrfs_file_extent_type(leaf, fi); + if (unlikely(fe_type >= BTRFS_NR_FILE_EXTENT_TYPES)) { file_extent_err(leaf, slot, "invalid type for file extent, have %u expect range [0, %u]", - btrfs_file_extent_type(leaf, fi), - BTRFS_NR_FILE_EXTENT_TYPES - 1); + fe_type, BTRFS_NR_FILE_EXTENT_TYPES - 1); return -EUCLEAN; } @@ -264,10 +300,11 @@ static int check_extent_data_item(struct extent_buffer *leaf, BTRFS_NR_COMPRESS_TYPES - 1); return -EUCLEAN; } - if (unlikely(btrfs_file_extent_encryption(leaf, fi))) { + policy = btrfs_file_extent_encryption(leaf, fi); + if (unlikely(policy >= BTRFS_NR_ENCRYPTION_TYPES)) { file_extent_err(leaf, slot, - "invalid encryption for file extent, have %u expect 0", - btrfs_file_extent_encryption(leaf, fi)); + "invalid encryption for file extent, have %u expect range [0, %u]", + policy, BTRFS_NR_ENCRYPTION_TYPES - 1); return -EUCLEAN; } if (btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE) { @@ -296,6 +333,16 @@ static int check_extent_data_item(struct extent_buffer *leaf, return 0; } + if (policy == BTRFS_ENCRYPTION_FSCRYPT) { + /* Only regular and prealloc extents should have an encryption context */ + if (unlikely(fe_type != BTRFS_FILE_EXTENT_REG && + fe_type != BTRFS_FILE_EXTENT_PREALLOC)) { + file_extent_err(leaf, slot, + "invalid type for encrypted file extent, have %u", fe_type); + return -EUCLEAN; + } + } + /* Regular or preallocated extent has fixed item size */ if (unlikely(item_size != sizeof(*fi))) { file_extent_err(leaf, slot, @@ -2196,6 +2243,9 @@ static enum btrfs_tree_block_status check_leaf_item(struct extent_buffer *leaf, case BTRFS_EXTENT_CSUM_KEY: ret = check_csum_item(leaf, key, slot, prev_key); break; + case BTRFS_FSCRYPT_CTX_KEY: + ret = check_fscrypt_context(leaf, key, slot, prev_key); + break; case BTRFS_DIR_ITEM_KEY: case BTRFS_DIR_INDEX_KEY: case BTRFS_XATTR_ITEM_KEY: diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 9123adafa0d1..4336f90e3223 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -30,6 +30,7 @@ #include "print-tree.h" #include "tree-checker.h" #include "delayed-inode.h" +#include "fscrypt.h" #define MAX_CONFLICT_INODES 10 @@ -1045,9 +1046,10 @@ static int unlink_inode_for_log_replay(struct walk_control *wc, const struct fscrypt_str *name) { struct btrfs_trans_handle *trans = wc->trans; + struct fscrypt_name fname = { .disk_name = *name, }; int ret; - ret = btrfs_unlink_inode(trans, dir, inode, name); + ret = btrfs_unlink_inode(trans, dir, inode, &fname); if (ret) { btrfs_abort_log_replay(wc, ret, "failed to unlink inode %llu parent dir %llu name %.*s root %llu", @@ -2927,7 +2929,9 @@ static int replay_one_buffer(struct extent_buffer *eb, continue; /* these keys are simply copied */ - if (wc->log_key.type == BTRFS_XATTR_ITEM_KEY) { + if (wc->log_key.type == BTRFS_XATTR_ITEM_KEY || + wc->log_key.type == BTRFS_FSCRYPT_INODE_CTX_KEY || + wc->log_key.type == BTRFS_FSCRYPT_CTX_KEY) { ret = overwrite_item(wc); if (ret) break; @@ -5144,11 +5148,20 @@ static int log_one_extent(struct btrfs_trans_handle *trans, struct btrfs_file_extent_item fi = { 0 }; struct extent_buffer *leaf; struct btrfs_key key; + u8 fscrypt_ctx[FSCRYPT_SET_CONTEXT_MAX_SIZE]; + ssize_t fscrypt_context_size; enum btrfs_compression_type compress_type; u64 extent_offset = em->offset; u64 block_start = btrfs_extent_map_block_start(em); u64 block_len; int ret; + u8 encryption = btrfs_extent_map_encryption(em); + + fscrypt_context_size = btrfs_fscrypt_context_for_new_extent(inode, + em->fscrypt_info, + fscrypt_ctx); + if (fscrypt_context_size < 0) + return (int)fscrypt_context_size; btrfs_set_stack_file_extent_generation(&fi, trans->transid); if (em->flags & EXTENT_FLAG_PREALLOC) @@ -5170,6 +5183,7 @@ static int log_one_extent(struct btrfs_trans_handle *trans, btrfs_set_stack_file_extent_num_bytes(&fi, em->len); btrfs_set_stack_file_extent_ram_bytes(&fi, em->ram_bytes); btrfs_set_stack_file_extent_compression(&fi, compress_type); + btrfs_set_stack_file_extent_encryption(&fi, encryption); ret = log_extent_csums(trans, inode, log, em, ctx); if (ret) @@ -5212,6 +5226,16 @@ static int log_one_extent(struct btrfs_trans_handle *trans, btrfs_release_path(path); + if (fscrypt_context_size) { + key.objectid = btrfs_ino(inode); + key.type = BTRFS_FSCRYPT_CTX_KEY; + key.offset = em->start; + ret = btrfs_insert_empty_item(trans, log, path, &key, fscrypt_context_size); + if (ret) + return ret; + btrfs_fscrypt_save_extent_info(path, fscrypt_ctx, fscrypt_context_size); + } + return ret; } @@ -5410,7 +5434,7 @@ static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans, */ if (ret) { btrfs_clear_em_logging(inode, em); - btrfs_free_extent_map(em); + btrfs_free_extent_map_safe(tree, em); continue; } @@ -5419,11 +5443,13 @@ static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans, ret = log_one_extent(trans, inode, em, path, ctx); write_lock(&tree->lock); btrfs_clear_em_logging(inode, em); - btrfs_free_extent_map(em); + btrfs_free_extent_map_safe(tree, em); } WARN_ON(!list_empty(&extents)); write_unlock(&tree->lock); + btrfs_free_pending_extent_maps(tree); + if (!ret) ret = btrfs_log_prealloc_extents(trans, inode, path, ctx); if (ret) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index a88e68f90564..d70735d501c4 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -6053,6 +6053,11 @@ struct btrfs_block_group *btrfs_create_chunk(struct btrfs_trans_handle *trans, lockdep_assert_held(&info->chunk_mutex); + if (type & BTRFS_BLOCK_GROUP_RAID56_MASK && btrfs_fs_incompat(info, ENCRYPT)) { + btrfs_warn(info, "RAID5/6 not yet supported on encrypted filesystem"); + return ERR_PTR(-EINVAL); + } + if (!alloc_profile_is_valid(type, 0)) { DEBUG_WARN("invalid alloc profile for type %llu", type); return ERR_PTR(-EINVAL); diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 570a2231c945..8ac9d3626b1d 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -42,6 +42,7 @@ static struct workqueue_struct *fscrypt_read_workqueue; static DEFINE_MUTEX(fscrypt_init_mutex); struct kmem_cache *fscrypt_inode_info_cachep; +struct kmem_cache *fscrypt_extent_info_cachep; void fscrypt_enqueue_decrypt_work(struct work_struct *work) { @@ -402,12 +403,19 @@ static int __init fscrypt_init(void) if (!fscrypt_inode_info_cachep) goto fail_free_queue; + fscrypt_extent_info_cachep = KMEM_CACHE(fscrypt_extent_info, + SLAB_RECLAIM_ACCOUNT); + if (!fscrypt_extent_info_cachep) + goto fail_free_inode_info; + err = fscrypt_init_keyring(); if (err) - goto fail_free_inode_info; + goto fail_free_extent_info; return 0; +fail_free_extent_info: + kmem_cache_destroy(fscrypt_extent_info_cachep); fail_free_inode_info: kmem_cache_destroy(fscrypt_inode_info_cachep); fail_free_queue: diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index 629eb0d72e86..ca73efcf5658 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -27,42 +27,6 @@ */ #define FSCRYPT_FNAME_MIN_MSG_LEN 16 -/* - * struct fscrypt_nokey_name - identifier for directory entry when key is absent - * - * When userspace lists an encrypted directory without access to the key, the - * filesystem must present a unique "no-key name" for each filename that allows - * it to find the directory entry again if requested. Naively, that would just - * mean using the ciphertext filenames. However, since the ciphertext filenames - * can contain illegal characters ('\0' and '/'), they must be encoded in some - * way. We use base64url. But that can cause names to exceed NAME_MAX (255 - * bytes), so we also need to use a strong hash to abbreviate long names. - * - * The filesystem may also need another kind of hash, the "dirhash", to quickly - * find the directory entry. Since filesystems normally compute the dirhash - * over the on-disk filename (i.e. the ciphertext), it's not computable from - * no-key names that abbreviate the ciphertext using the strong hash to fit in - * NAME_MAX. It's also not computable if it's a keyed hash taken over the - * plaintext (but it may still be available in the on-disk directory entry); - * casefolded directories use this type of dirhash. At least in these cases, - * each no-key name must include the name's dirhash too. - * - * To meet all these requirements, we base64url-encode the following - * variable-length structure. It contains the dirhash, or 0's if the filesystem - * didn't provide one; up to 149 bytes of the ciphertext name; and for - * ciphertexts longer than 149 bytes, also the SHA-256 of the remaining bytes. - * - * This ensures that each no-key name contains everything needed to find the - * directory entry again, contains only legal characters, doesn't exceed - * NAME_MAX, is unambiguous unless there's a SHA-256 collision, and that we only - * take the performance hit of SHA-256 on very long filenames (which are rare). - */ -struct fscrypt_nokey_name { - u32 dirhash[2]; - u8 bytes[149]; - u8 sha256[SHA256_DIGEST_SIZE]; -}; /* 189 bytes => 252 bytes base64url-encoded, which is <= NAME_MAX (255) */ - /* * Decoded size of max-size no-key name, i.e. a name that was abbreviated using * the strong hash and thus includes the 'sha256' field. This isn't simply diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 8d3c278a7591..2f5f4e7f8f65 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -66,6 +66,8 @@ #define FSCRYPT_CONTEXT_V1 1 #define FSCRYPT_CONTEXT_V2 2 +#define FSCRYPT_EXTENT_CONTEXT_V1 1 + /* Keep this in sync with include/uapi/linux/fscrypt.h */ #define FSCRYPT_MODE_MAX FSCRYPT_MODE_AES_256_HCTR2 @@ -89,6 +91,25 @@ struct fscrypt_context_v2 { u8 nonce[FSCRYPT_FILE_NONCE_SIZE]; }; +/* + * fscrypt_extent_context - the encryption context of an extent + * + * This is the on-disk information stored for an extent. The nonce is used as a + * KDF input in conjuction with the inode context to derive a per-extent key for + * encryption. This is used only when the filesystem uses per-extent encryption. + * + * With the current implementation, master_key_identifier and encryption mode + * must match the inode context. These are here for future expansion where we + * may want the option of mixing different keys and encryption modes for the + * same file. + */ +struct fscrypt_extent_context { + u8 version; /* FSCRYPT_EXTENT_CONTEXT_V1 */ + u8 encryption_mode; + u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]; + u8 nonce[FSCRYPT_FILE_NONCE_SIZE]; +}; + /* * fscrypt_context - the encryption context of an inode * @@ -323,6 +344,25 @@ struct fscrypt_inode_info { u8 ci_nonce[FSCRYPT_FILE_NONCE_SIZE]; }; +/* + * fscrypt_extent_info - the "encryption key" for an extent. + * + * This contains the derived key for the given extent and the nonce for the + * extent. + */ +struct fscrypt_extent_info { + refcount_t refs; + + /* The derived key for this extent. */ + struct fscrypt_prepared_key prep_key; + + /* The super block that this extent belongs to. */ + struct super_block *sb; + + /* This is the extent's nonce, loaded from the fscrypt_extent_context */ + u8 nonce[FSCRYPT_FILE_NONCE_SIZE]; +}; + typedef enum { FS_DECRYPT = 0, FS_ENCRYPT, @@ -330,6 +370,7 @@ typedef enum { /* crypto.c */ extern struct kmem_cache *fscrypt_inode_info_cachep; +extern struct kmem_cache *fscrypt_extent_info_cachep; int fscrypt_initialize(struct super_block *sb); int fscrypt_crypt_data_unit(const struct fscrypt_inode_info *ci, fscrypt_direction_t rw, u64 index, @@ -397,6 +438,7 @@ void fscrypt_init_hkdf(struct hmac_sha512_key *hkdf, const u8 *master_key, #define HKDF_CONTEXT_INODE_HASH_KEY 7 /* info= */ #define HKDF_CONTEXT_KEY_IDENTIFIER_FOR_HW_WRAPPED_KEY \ 8 /* info= */ +#define HKDF_CONTEXT_PER_EXTENT_ENC_KEY 9 /* info=extent_nonce */ void fscrypt_hkdf_expand(const struct hmac_sha512_key *hkdf, u8 context, const u8 *info, unsigned int infolen, @@ -550,9 +592,12 @@ struct fscrypt_master_key_secret { * * FSCRYPT_KEY_STATUS_INCOMPLETELY_REMOVED * Removal of this key has been initiated, but some inodes that were - * unlocked with it are still in-use. Like ABSENT, ->mk_secret is wiped, - * and the key can no longer be used to unlock inodes. Unlike ABSENT, the - * key is still in the keyring; ->mk_decrypted_inodes is nonempty; and + * unlocked with it are still in-use. + * For filesystems using per-extent encryption ->mk_secret is still + * being kept as the per-extent keys are derived at writeout time. + * Otherwise, like ABSENT, ->mk_secret is wiped, and the key can + * no longer be used to unlock inodes. Unlike ABSENT, the key is + * still in the keyring; ->mk_decrypted_inodes is nonempty; and * ->mk_active_refs > 0, being equal to the size of ->mk_decrypted_inodes. * * This state transitions to ABSENT if ->mk_decrypted_inodes becomes empty, diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index a7a8a3f581a0..3142cf106bde 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c @@ -9,6 +9,37 @@ #include "fscrypt_private.h" +/** + * __fscrypt_file_open() - prepare for filesystem-internal access to a + * possibly-encrypted regular file + * @dir: the inode for the directory via which the file is being accessed + * @inode: the inode being "opened" + * + * This is like fscrypt_file_open(), but instead of taking the 'struct file' + * being opened it takes the parent directory explicitly. This is intended for + * use cases such as "send/receive" which involve the filesystem accessing file + * contents without setting up a 'struct file'. + * + * Return: 0 on success, -ENOKEY if the key is missing, or another -errno code + */ +int __fscrypt_file_open(struct inode *dir, struct inode *inode) +{ + int err; + + err = fscrypt_require_key(inode); + if (err) + return err; + + if (!fscrypt_has_permitted_context(dir, inode)) { + fscrypt_warn(inode, + "Inconsistent encryption context (parent directory: %llu)", + dir->i_ino); + return -EPERM; + } + return 0; +} +EXPORT_SYMBOL_GPL(__fscrypt_file_open); + /** * fscrypt_file_open() - prepare to open a possibly-encrypted regular file * @inode: the inode being opened @@ -60,12 +91,7 @@ int fscrypt_file_open(struct inode *inode, struct file *filp) rcu_read_unlock(); dentry_parent = dget_parent(dentry); - if (!fscrypt_has_permitted_context(d_inode(dentry_parent), inode)) { - fscrypt_warn(inode, - "Inconsistent encryption context (parent directory: %llu)", - d_inode(dentry_parent)->i_ino); - err = -EPERM; - } + err = __fscrypt_file_open(d_inode(dentry_parent), inode); dput(dentry_parent); return err; } diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index 37d42d357925..fde844aaac1a 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -108,8 +108,11 @@ int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci, if (ci->ci_mode->blk_crypto_mode == BLK_ENCRYPTION_MODE_INVALID) return 0; - /* The filesystem must be mounted with -o inlinecrypt */ - if (!(sb->s_flags & SB_INLINECRYPT)) + /* + * The filesystem must be mounted with -o inlinecrypt or have + * has_per_extent_encryption enabled. + */ + if (!(sb->s_flags & SB_INLINECRYPT) && !sb->s_cop->has_per_extent_encryption) return 0; /* @@ -175,7 +178,8 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, err = blk_crypto_init_key(blk_key, key_bytes, key_size, key_type, crypto_mode, fscrypt_get_dun_bytes(ci), - 1U << ci->ci_data_unit_bits); + 1U << ci->ci_data_unit_bits, + sb->s_cop->process_bio); if (err) { fscrypt_err(inode, "error %d initializing blk-crypto key", err); goto fail; @@ -312,6 +316,40 @@ void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode, } EXPORT_SYMBOL_GPL(fscrypt_set_bio_crypt_ctx); +/** + * fscrypt_set_bio_crypt_ctx_from_extent() - prepare a file contents bio for + * inline crypto with extent + * encryption + * @bio: a bio which will eventually be submitted to the file + * @ei: the extent's crypto info + * @pos: the first extent logical offset (in bytes) in the I/O + * @gfp_mask: memory allocation flags - these must be a waiting mask so that + * bio_crypt_set_ctx can't fail. + * + * If the contents of the file should be encrypted (or decrypted) with inline + * encryption, then assign the appropriate encryption context to the bio. + * + * Normally the bio should be newly allocated (i.e. no pages added yet), as + * otherwise fscrypt_mergeable_extent_bio() won't work as intended. + * + * The encryption context will be freed automatically when the bio is freed. + */ +void fscrypt_set_bio_crypt_ctx_from_extent(struct bio *bio, + const struct fscrypt_extent_info *ei, + loff_t pos, gfp_t gfp_mask) +{ + const struct blk_crypto_key *key; + u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE] = {}; + + if (!ei) + return; + key = ei->prep_key.blk_key; + + dun[0] = pos >> key->data_unit_size_bits; + bio_crypt_set_ctx(bio, key, dun, gfp_mask); +} +EXPORT_SYMBOL_GPL(fscrypt_set_bio_crypt_ctx_from_extent); + /** * fscrypt_mergeable_bio() - test whether data can be added to a bio * @bio: the bio being built up @@ -359,6 +397,53 @@ bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode, } EXPORT_SYMBOL_GPL(fscrypt_mergeable_bio); +/** + * fscrypt_mergeable_extent_bio() - test whether data can be added to a bio + * @bio: the bio being built up + * @ei: the fscrypt_extent_info for this extent + * @pos: the next extent logical offset (in bytes) in the I/O + * + * When building a bio which may contain data which should undergo inline + * encryption (or decryption) via fscrypt, filesystems should call this function + * to ensure that the resulting bio contains only contiguous data unit numbers. + * This will return false if the next part of the I/O cannot be merged with the + * bio because either the encryption key would be different or the encryption + * data unit numbers would be discontiguous. + * + * fscrypt_set_bio_crypt_ctx_from_extent() must have already been called on the + * bio. + * + * This function isn't required in cases where crypto-mergeability is ensured in + * another way, such as I/O targeting only a single extent (thus a single key) + * combined with fscrypt_limit_io_blocks() to ensure DUN contiguity. + * + * Return: true iff the I/O is mergeable + */ +bool fscrypt_mergeable_extent_bio(struct bio *bio, + const struct fscrypt_extent_info *ei, + loff_t pos) +{ + const struct bio_crypt_ctx *bc = bio_crypt_ctx(bio); + u64 next_dun[BLK_CRYPTO_DUN_ARRAY_SIZE] = {}; + + if (!ei != !bc) + return false; + if (!bc) + return true; + + /* + * Comparing the key pointers is good enough, as all I/O for each key + * uses the same pointer. I.e., there's currently no need to support + * merging requests where the keys are the same but the pointers differ. + */ + if (bc->bc_key != ei->prep_key.blk_key) + return false; + + next_dun[0] = pos >> bc->bc_key->data_unit_size_bits; + return bio_crypt_dun_is_contiguous(bc, bio->bi_iter.bi_size, next_dun); +} +EXPORT_SYMBOL_GPL(fscrypt_mergeable_extent_bio); + /** * fscrypt_dio_supported() - check whether DIO (direct I/O) is supported on an * inode, as far as encryption is concerned diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index be8e6e8011f2..796e02a0db25 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -110,6 +110,14 @@ void fscrypt_put_master_key_activeref(struct super_block *sb, WARN_ON_ONCE(mk->mk_present); WARN_ON_ONCE(!list_empty(&mk->mk_decrypted_inodes)); + /* We can't wipe the master key secret until the last activeref is + * dropped on the master key with per-extent encryption since the key + * derivation continues to happen as long as there are active refs. + * Wipe it here now that we're done using it. + */ + if (sb->s_cop->has_per_extent_encryption) + wipe_master_key_secret(&mk->mk_secret); + for (i = 0; i <= FSCRYPT_MODE_MAX; i++) { fscrypt_destroy_prepared_key( sb, &mk->mk_direct_keys[i]); @@ -134,7 +142,15 @@ static void fscrypt_initiate_key_removal(struct super_block *sb, struct fscrypt_master_key *mk) { WRITE_ONCE(mk->mk_present, false); - wipe_master_key_secret(&mk->mk_secret); + + /* + * Per-extent encryption requires the master key to stick around until + * writeout has completed as we derive the per-extent keys at writeout + * time. Once the activeref drops to 0 we'll wipe the master secret + * key. + */ + if (!sb->s_cop->has_per_extent_encryption) + wipe_master_key_secret(&mk->mk_secret); fscrypt_put_master_key_activeref(sb, mk); } diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index ce327bfdada4..9fde198ae5e3 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -847,3 +847,167 @@ int fscrypt_drop_inode(struct inode *inode) return !READ_ONCE(ci->ci_master_key->mk_present); } EXPORT_SYMBOL_GPL(fscrypt_drop_inode); + +static struct fscrypt_extent_info * +setup_extent_info(struct inode *inode, const u8 nonce[FSCRYPT_FILE_NONCE_SIZE]) +{ + struct fscrypt_extent_info *ei; + struct fscrypt_inode_info *ci; + struct fscrypt_master_key *mk; + u8 derived_key[FSCRYPT_MAX_RAW_KEY_SIZE]; + int keysize; + int err; + + ci = *fscrypt_inode_info_addr(inode); + mk = ci->ci_master_key; + if (WARN_ON_ONCE(!mk)) + return ERR_PTR(-ENOKEY); + + ei = kmem_cache_zalloc(fscrypt_extent_info_cachep, GFP_KERNEL); + if (!ei) + return ERR_PTR(-ENOMEM); + + refcount_set(&ei->refs, 1); + memcpy(ei->nonce, nonce, FSCRYPT_FILE_NONCE_SIZE); + ei->sb = inode->i_sb; + + keysize = ci->ci_mode->keysize; + down_read(&mk->mk_sem); + /* + * We specifically don't check ->mk_present here because if the inode is + * open and has a reference on the master key then it should be + * available for us to use no matter if mk_present is true or false. + */ + fscrypt_hkdf_expand(&mk->mk_secret.hkdf, HKDF_CONTEXT_PER_EXTENT_ENC_KEY, + ei->nonce, FSCRYPT_FILE_NONCE_SIZE, + derived_key, keysize); + err = fscrypt_prepare_inline_crypt_key(&ei->prep_key, + derived_key, keysize, false, ci); + memzero_explicit(derived_key, keysize); + up_read(&mk->mk_sem); + if (err) { + memzero_explicit(ei, sizeof(*ei)); + kmem_cache_free(fscrypt_extent_info_cachep, ei); + return ERR_PTR(err); + } + return ei; +} + +/** + * fscrypt_prepare_new_extent() - prepare to create a new extent for a file + * @inode: the encrypted inode + * + * If the inode is encrypted, setup the fscrypt_extent_info for a new extent. + * This will include the nonce and the derived key necessary for the extent to + * be encrypted. This is only meant to be used with inline crypto and on inodes + * that need their contents encrypted. + * + * This doesn't persist the new extents encryption context, this is done later + * by calling fscrypt_set_extent_context(). + * + * Return: The newly allocated fscrypt_extent_info on success, -EOPNOTSUPP if + * we're not encrypted, or another -errno code + */ +struct fscrypt_extent_info *fscrypt_prepare_new_extent(struct inode *inode) +{ + u8 nonce[FSCRYPT_FILE_NONCE_SIZE]; + + if (WARN_ON_ONCE(!*fscrypt_inode_info_addr(inode))) + return ERR_PTR(-EOPNOTSUPP); + if (WARN_ON_ONCE(!fscrypt_inode_uses_inline_crypto(inode))) + return ERR_PTR(-EOPNOTSUPP); + + get_random_bytes(nonce, FSCRYPT_FILE_NONCE_SIZE); + return setup_extent_info(inode, nonce); +} +EXPORT_SYMBOL_GPL(fscrypt_prepare_new_extent); + +/** + * fscrypt_load_extent_info() - create an fscrypt_extent_info from the context + * @inode: the inode + * @ctx: the context buffer + * @ctx_size: the size of the context buffer + * + * Create the fscrypt_extent_info and derive the key based on the + * fscrypt_extent_context buffer that is provided. + * + * Return: The newly allocated fscrypt_extent_info on success, -EOPNOTSUPP if + * we're not encrypted, or another -errno code + */ +struct fscrypt_extent_info *fscrypt_load_extent_info(struct inode *inode, + const u8 *ctx, + size_t ctx_size) +{ + struct fscrypt_extent_context extent_ctx; + const struct fscrypt_inode_info *ci = *fscrypt_inode_info_addr(inode); + const struct fscrypt_policy_v2 *policy = &ci->ci_policy.v2; + + if (WARN_ON_ONCE(!ci)) + return ERR_PTR(-EOPNOTSUPP); + if (WARN_ON_ONCE(!fscrypt_inode_uses_inline_crypto(inode))) + return ERR_PTR(-EOPNOTSUPP); + if (ctx_size < sizeof(extent_ctx)) + return ERR_PTR(-EINVAL); + + memcpy(&extent_ctx, ctx, sizeof(extent_ctx)); + + if (extent_ctx.version != FSCRYPT_EXTENT_CONTEXT_V1) { + fscrypt_warn(inode, "Invalid extent encryption context version"); + return ERR_PTR(-EINVAL); + } + + /* + * For now we need to validate that the master key and the encryption + * mode matches what is in the inode. + */ + if (memcmp(extent_ctx.master_key_identifier, + policy->master_key_identifier, + sizeof(extent_ctx.master_key_identifier))) { + fscrypt_warn(inode, "Mismatching master key identifier"); + return ERR_PTR(-EINVAL); + } + + if (extent_ctx.encryption_mode != policy->contents_encryption_mode) { + fscrypt_warn(inode, "Mismatching encryption mode"); + return ERR_PTR(-EINVAL); + } + + return setup_extent_info(inode, extent_ctx.nonce); +} +EXPORT_SYMBOL_GPL(fscrypt_load_extent_info); + +/** + * fscrypt_put_extent_info() - put a reference to a fscrypt_extent_info + * @ei: the fscrypt_extent_info being put or NULL. + * + * Drop a reference and possibly free the fscrypt_extent_info. + * + * Might sleep, since this may call blk_crypto_evict_key() which can sleep. + */ +void fscrypt_put_extent_info(struct fscrypt_extent_info *ei) +{ + if (ei && refcount_dec_and_test(&ei->refs)) { + fscrypt_destroy_prepared_key(ei->sb, &ei->prep_key); + memzero_explicit(ei, sizeof(*ei)); + kmem_cache_free(fscrypt_extent_info_cachep, ei); + } +} +EXPORT_SYMBOL_GPL(fscrypt_put_extent_info); + +/** + * fscrypt_get_extent_info() - get a reference to a fscrypt_extent_info + * @ei: the extent_info to get. + * + * Get a reference on the fscrypt_extent_info. This is useful for file systems + * that need to pass the fscrypt_extent_info through various other structures to + * make lifetime tracking simpler. + * + * Return: the ei with an extra ref, NULL if ei was NULL. + */ +struct fscrypt_extent_info *fscrypt_get_extent_info(struct fscrypt_extent_info *ei) +{ + if (ei) + refcount_inc(&ei->refs); + return ei; +} +EXPORT_SYMBOL_GPL(fscrypt_get_extent_info); diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index 9915e39362db..94dac05c2710 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -211,6 +211,12 @@ static bool fscrypt_supported_v1_policy(const struct fscrypt_policy_v1 *policy, return false; } + if (inode->i_sb->s_cop->has_per_extent_encryption) { + fscrypt_warn(inode, + "v1 policies aren't supported on file systems that use extent encryption"); + return false; + } + return true; } @@ -240,6 +246,11 @@ static bool fscrypt_supported_v2_policy(const struct fscrypt_policy_v2 *policy, count += !!(policy->flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY); count += !!(policy->flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64); count += !!(policy->flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32); + if (count > 0 && inode->i_sb->s_cop->has_per_extent_encryption) { + fscrypt_warn(inode, + "DIRECT_KEY and IV_INO_LBLK_* encryption flags aren't supported on file systems that use extent encryption"); + return false; + } if (count > 1) { fscrypt_warn(inode, "Mutually exclusive encryption flags (0x%02x)", policy->flags); @@ -792,6 +803,42 @@ int fscrypt_set_context(struct inode *inode, void *fs_data) } EXPORT_SYMBOL_GPL(fscrypt_set_context); +/** + * fscrypt_set_extent_context() - Set the fscrypt extent context of a new extent + * @inode: the inode this extent belongs to + * @ei: the fscrypt_extent_info for the given extent + * @buf: the buffer to copy the fscrypt extent context into + * + * This should be called after fscrypt_prepare_new_extent(), using the + * fscrypt_extent_info that was created at that point. + * + * buf should be able to fit up to FSCRYPT_SET_CONTEXT_MAX_SIZE bytes. + * + * Return: the size of the fscrypt_extent_context, errno if the inode has the + * wrong policy version. + */ +ssize_t fscrypt_context_for_new_extent(struct inode *inode, + struct fscrypt_extent_info *ei, u8 *buf) +{ + struct fscrypt_extent_context *ctx = (struct fscrypt_extent_context *)buf; + const struct fscrypt_inode_info *ci = *fscrypt_inode_info_addr(inode); + + BUILD_BUG_ON(sizeof(struct fscrypt_extent_context) > + FSCRYPT_SET_CONTEXT_MAX_SIZE); + + if (WARN_ON_ONCE(ci->ci_policy.version != 2)) + return -EINVAL; + + ctx->version = FSCRYPT_EXTENT_CONTEXT_V1; + ctx->encryption_mode = ci->ci_policy.v2.contents_encryption_mode; + memcpy(ctx->master_key_identifier, + ci->ci_policy.v2.master_key_identifier, + sizeof(ctx->master_key_identifier)); + memcpy(ctx->nonce, ei->nonce, FSCRYPT_FILE_NONCE_SIZE); + return sizeof(struct fscrypt_extent_context); +} +EXPORT_SYMBOL_GPL(fscrypt_context_for_new_extent); + /** * fscrypt_parse_test_dummy_encryption() - parse the test_dummy_encryption mount option * @param: the mount option diff --git a/include/linux/blk-crypto.h b/include/linux/blk-crypto.h index f7c3cb4a342f..34512f6f5086 100644 --- a/include/linux/blk-crypto.h +++ b/include/linux/blk-crypto.h @@ -7,7 +7,7 @@ #define __LINUX_BLK_CRYPTO_H #include -#include +#include #include enum blk_crypto_mode_num { @@ -19,6 +19,14 @@ enum blk_crypto_mode_num { BLK_ENCRYPTION_MODE_MAX, }; +/* + * orig_bio must be the bio that was submitted from the upper layer as the upper + * layer could have used a specific bioset and expect the orig_bio to be from + * its bioset. + */ +typedef blk_status_t (*blk_crypto_process_bio_t)(struct bio *orig_bio, + struct bio *enc_bio); + /* * Supported types of keys. Must be bitflags due to their use in * blk_crypto_profile::key_types_supported. @@ -77,12 +85,14 @@ enum blk_crypto_key_type { * filesystem block size or the disk sector size. * @dun_bytes: the maximum number of bytes of DUN used when using this key * @key_type: the type of this key -- either raw or hardware-wrapped + * @proces_bio: optional callback to process encrypted bios. */ struct blk_crypto_config { enum blk_crypto_mode_num crypto_mode; unsigned int data_unit_size; unsigned int dun_bytes; enum blk_crypto_key_type key_type; + blk_crypto_process_bio_t process_bio; }; /** @@ -150,7 +160,8 @@ int blk_crypto_init_key(struct blk_crypto_key *blk_key, enum blk_crypto_key_type key_type, enum blk_crypto_mode_num crypto_mode, unsigned int dun_bytes, - unsigned int data_unit_size); + unsigned int data_unit_size, + blk_crypto_process_bio_t process_bio); int blk_crypto_start_using_key(struct block_device *bdev, const struct blk_crypto_key *key); diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 54712ec61ffb..9eef488340ea 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -16,6 +16,7 @@ #include #include #include +#include #include /* @@ -32,6 +33,7 @@ union fscrypt_policy; struct fscrypt_inode_info; +struct fscrypt_extent_info; struct fs_parameter; struct seq_file; @@ -54,6 +56,42 @@ struct fscrypt_name { #define fname_name(p) ((p)->disk_name.name) #define fname_len(p) ((p)->disk_name.len) +/* + * struct fscrypt_nokey_name - identifier for directory entry when key is absent + * + * When userspace lists an encrypted directory without access to the key, the + * filesystem must present a unique "no-key name" for each filename that allows + * it to find the directory entry again if requested. Naively, that would just + * mean using the ciphertext filenames. However, since the ciphertext filenames + * can contain illegal characters ('\0' and '/'), they must be encoded in some + * way. We use base64url. But that can cause names to exceed NAME_MAX (255 + * bytes), so we also need to use a strong hash to abbreviate long names. + * + * The filesystem may also need another kind of hash, the "dirhash", to quickly + * find the directory entry. Since filesystems normally compute the dirhash + * over the on-disk filename (i.e. the ciphertext), it's not computable from + * no-key names that abbreviate the ciphertext using the strong hash to fit in + * NAME_MAX. It's also not computable if it's a keyed hash taken over the + * plaintext (but it may still be available in the on-disk directory entry); + * casefolded directories use this type of dirhash. At least in these cases, + * each no-key name must include the name's dirhash too. + * + * To meet all these requirements, we base64url-encode the following + * variable-length structure. It contains the dirhash, or 0's if the filesystem + * didn't provide one; up to 149 bytes of the ciphertext name; and for + * ciphertexts longer than 149 bytes, also the SHA-256 of the remaining bytes. + * + * This ensures that each no-key name contains everything needed to find the + * directory entry again, contains only legal characters, doesn't exceed + * NAME_MAX, is unambiguous unless there's a SHA-256 collision, and that we only + * take the performance hit of SHA-256 on very long filenames (which are rare). + */ +struct fscrypt_nokey_name { + u32 dirhash[2]; + u8 bytes[149]; + u8 sha256[32]; +}; /* 189 bytes => 252 bytes base64url-encoded, which is <= NAME_MAX (255) */ + /* Maximum value for the third parameter of fscrypt_operations.set_context(). */ #define FSCRYPT_SET_CONTEXT_MAX_SIZE 40 @@ -103,6 +141,14 @@ struct fscrypt_operations { */ unsigned int supports_subblock_data_units : 1; + /* + * If set then extent based encryption will be used for this file + * system, and fs/crypto/ will enforce limits on the policies that are + * allowed to be chosen. Currently this means only v2 policies and a + * limited flags are supported. + */ + unsigned int has_per_extent_encryption : 1; + /* * This field exists only for backwards compatibility reasons and should * only be set by the filesystems that are setting it already. It @@ -196,6 +242,19 @@ struct fscrypt_operations { */ struct block_device **(*get_devices)(struct super_block *sb, unsigned int *num_devs); + + /* + * A callback if the file system requires the ability to process the + * encrypted bio, used only with inline encryption. + * + * @orig_bio: the original bio submitted. + * @enc_bio: the encrypted bio. + * + * For writes the enc_bio will be different from the orig_bio, for reads + * they will be the same. For reads we get the bio before it is + * decrypted, for writes we get the bio before it is submitted. + */ + blk_crypto_process_bio_t process_bio; }; int fscrypt_d_revalidate(struct inode *dir, const struct qstr *name, @@ -387,6 +446,8 @@ int fscrypt_ioctl_get_nonce(struct file *filp, void __user *arg); int fscrypt_has_permitted_context(struct inode *parent, struct inode *child); int fscrypt_context_for_new_inode(void *ctx, struct inode *inode); int fscrypt_set_context(struct inode *inode, void *fs_data); +ssize_t fscrypt_context_for_new_extent(struct inode *inode, + struct fscrypt_extent_info *ei, u8 *buf); struct fscrypt_dummy_policy { const union fscrypt_policy *policy; @@ -423,6 +484,11 @@ int fscrypt_prepare_new_inode(struct inode *dir, struct inode *inode, void fscrypt_put_encryption_info(struct inode *inode); void fscrypt_free_inode(struct inode *inode); int fscrypt_drop_inode(struct inode *inode); +struct fscrypt_extent_info *fscrypt_prepare_new_extent(struct inode *inode); +void fscrypt_put_extent_info(struct fscrypt_extent_info *ei); +struct fscrypt_extent_info *fscrypt_get_extent_info(struct fscrypt_extent_info *ei); +struct fscrypt_extent_info *fscrypt_load_extent_info(struct inode *inode, + const u8 *ctx, size_t ctx_size); /* fname.c */ int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname, @@ -455,6 +521,7 @@ int fscrypt_zeroout_range(const struct inode *inode, loff_t pos, /* hooks.c */ int fscrypt_file_open(struct inode *inode, struct file *filp); +int __fscrypt_file_open(struct inode *dir, struct inode *inode); int __fscrypt_prepare_link(struct inode *inode, struct inode *dir, struct dentry *dentry); int __fscrypt_prepare_rename(struct inode *old_dir, struct dentry *old_dentry, @@ -636,6 +703,24 @@ fscrypt_free_dummy_policy(struct fscrypt_dummy_policy *dummy_policy) { } +static inline ssize_t +fscrypt_context_for_new_extent(struct inode *inode, struct fscrypt_extent_info *ei, + u8 *buf) +{ + return -EOPNOTSUPP; +} + +static inline struct fscrypt_extent_info * +fscrypt_load_extent_info(struct inode *inode, const u8 *ctx, size_t ctx_size) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline size_t fscrypt_extent_context_size(struct inode *inode) +{ + return 0; +} + /* keyring.c */ static inline void fscrypt_destroy_keyring(struct super_block *sb) { @@ -688,6 +773,20 @@ static inline int fscrypt_drop_inode(struct inode *inode) return 0; } +static inline struct fscrypt_extent_info * +fscrypt_prepare_new_extent(struct inode *inode) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline void fscrypt_put_extent_info(struct fscrypt_extent_info *ei) { } + +static inline struct fscrypt_extent_info * +fscrypt_get_extent_info(struct fscrypt_extent_info *ei) +{ + return ei; +} + /* fname.c */ static inline int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, @@ -770,6 +869,13 @@ static inline int fscrypt_file_open(struct inode *inode, struct file *filp) return 0; } +static inline int __fscrypt_file_open(struct inode *dir, struct inode *inode) +{ + if (IS_ENCRYPTED(inode)) + return -EOPNOTSUPP; + return 0; +} + static inline int __fscrypt_prepare_link(struct inode *inode, struct inode *dir, struct dentry *dentry) { @@ -868,9 +974,17 @@ bool __fscrypt_inode_uses_inline_crypto(const struct inode *inode); void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode, loff_t pos, gfp_t gfp_mask); +void fscrypt_set_bio_crypt_ctx_from_extent(struct bio *bio, + const struct fscrypt_extent_info *ei, + loff_t pos, gfp_t gfp_mask); + bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode, loff_t pos); +bool fscrypt_mergeable_extent_bio(struct bio *bio, + const struct fscrypt_extent_info *ei, + loff_t pos); + bool fscrypt_dio_supported(struct inode *inode); u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks); @@ -886,6 +1000,11 @@ static inline void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode, loff_t pos, gfp_t gfp_mask) { } +static inline void fscrypt_set_bio_crypt_ctx_from_extent( + struct bio *bio, + const struct fscrypt_extent_info *ei, + loff_t pos, gfp_t gfp_mask) { } + static inline bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode, loff_t pos) @@ -893,6 +1012,14 @@ static inline bool fscrypt_mergeable_bio(struct bio *bio, return true; } +static inline bool fscrypt_mergeable_extent_bio( + struct bio *bio, + const struct fscrypt_extent_info *ei, + loff_t pos) +{ + return true; +} + static inline bool fscrypt_dio_supported(struct inode *inode) { return !fscrypt_needs_contents_encryption(inode); diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h index 9165154a274d..2f6a46e5f4ce 100644 --- a/include/uapi/linux/btrfs.h +++ b/include/uapi/linux/btrfs.h @@ -335,6 +335,7 @@ struct btrfs_ioctl_fs_info_args { #define BTRFS_FEATURE_INCOMPAT_ZONED (1ULL << 12) #define BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2 (1ULL << 13) #define BTRFS_FEATURE_INCOMPAT_RAID_STRIPE_TREE (1ULL << 14) +#define BTRFS_FEATURE_INCOMPAT_ENCRYPT (1ULL << 15) #define BTRFS_FEATURE_INCOMPAT_SIMPLE_QUOTA (1ULL << 16) #define BTRFS_FEATURE_INCOMPAT_REMAP_TREE (1ULL << 17) diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h index cc3b9f7dccaf..26b793396132 100644 --- a/include/uapi/linux/btrfs_tree.h +++ b/include/uapi/linux/btrfs_tree.h @@ -167,6 +167,9 @@ #define BTRFS_VERITY_DESC_ITEM_KEY 36 #define BTRFS_VERITY_MERKLE_ITEM_KEY 37 +#define BTRFS_FSCRYPT_INODE_CTX_KEY 41 +#define BTRFS_FSCRYPT_CTX_KEY 42 + #define BTRFS_ORPHAN_ITEM_KEY 48 /* reserve 2-15 close to the inode for later flexibility */ @@ -431,6 +434,7 @@ static inline __u8 btrfs_dir_flags_to_ftype(__u8 flags) #define BTRFS_INODE_NOATIME (1U << 9) #define BTRFS_INODE_DIRSYNC (1U << 10) #define BTRFS_INODE_COMPRESS (1U << 11) +#define BTRFS_INODE_ENCRYPT (1U << 12) #define BTRFS_INODE_ROOT_ITEM_INIT (1U << 31) @@ -447,6 +451,7 @@ static inline __u8 btrfs_dir_flags_to_ftype(__u8 flags) BTRFS_INODE_NOATIME | \ BTRFS_INODE_DIRSYNC | \ BTRFS_INODE_COMPRESS | \ + BTRFS_INODE_ENCRYPT | \ BTRFS_INODE_ROOT_ITEM_INIT) #define BTRFS_INODE_RO_VERITY (1U << 0) @@ -1075,6 +1080,19 @@ enum { BTRFS_NR_FILE_EXTENT_TYPES = 3, }; +/* + * Currently just the FSCRYPT_SET_CONTEXT_MAX_SIZE, which is larger than the + * current extent context size from fscrypt, so this should give us plenty of + * breathing room for expansion later. + */ +#define BTRFS_MAX_EXTENT_CTX_SIZE 40 + +enum btrfs_encryption_type { + BTRFS_ENCRYPTION_NONE, + BTRFS_ENCRYPTION_FSCRYPT, + BTRFS_NR_ENCRYPTION_TYPES, +}; + struct btrfs_file_extent_item { /* * transaction id that created this extent @@ -1097,8 +1115,14 @@ struct btrfs_file_extent_item { * but not for stat. */ __u8 compression; + + /* + * Type of encryption in use. Unencrypted value is 0. + */ __u8 encryption; - __le16 other_encoding; /* spare for later use */ + + /* spare for later use */ + __le16 other_encoding; /* are we inline data or a real extent? */ __u8 type;