android_kernel_samsung_sm8650/fs/incfs/format.h
Paul Lawrence 6d18d83e6c ANDROID: Incremental fs: Build merkle tree when enabling verity
For incfs files that were created without a merkle tree, enabling verity
requires building a merkle tree first. Although this is the same logic
as verity performs, it is not that easy to reconcile the two given that
incfs has the merkle tree potentially when verity is not enabled.

Bug: 160634504
Test: incfs_test passes
Signed-off-by: Paul Lawrence <paullawrence@google.com>

Change-Id: Ia15a4051fa3362820846d65859e3af76b77f8cc4
2021-02-10 14:46:45 -08:00

408 lines
13 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright 2018 Google LLC
*/
/*
* Overview
* --------
* The backbone of the incremental-fs ondisk format is an append only linked
* list of metadata blocks. Each metadata block contains an offset of the next
* one. These blocks describe files and directories on the
* file system. They also represent actions of adding and removing file names
* (hard links).
*
* Every time incremental-fs instance is mounted, it reads through this list
* to recreate filesystem's state in memory. An offset of the first record in
* the metadata list is stored in the superblock at the beginning of the backing
* file.
*
* Most of the backing file is taken by data areas and blockmaps.
* Since data blocks can be compressed and have different sizes,
* single per-file data area can't be pre-allocated. That's why blockmaps are
* needed in order to find a location and size of each data block in
* the backing file. Each time a file is created, a corresponding block map is
* allocated to store future offsets of data blocks.
*
* Whenever a data block is given by data loader to incremental-fs:
* - A data area with the given block is appended to the end of
* the backing file.
* - A record in the blockmap for the given block index is updated to reflect
* its location, size, and compression algorithm.
* Metadata records
* ----------------
* incfs_blockmap - metadata record that specifies size and location
* of a blockmap area for a given file. This area
* contains an array of incfs_blockmap_entry-s.
* incfs_file_signature - metadata record that specifies where file signature
* and its hash tree can be found in the backing file.
*
* incfs_file_attr - metadata record that specifies where additional file
* attributes blob can be found.
*
* Metadata header
* ---------------
* incfs_md_header - header of a metadata record. It's always a part
* of other structures and served purpose of metadata
* bookkeeping.
*
* +-----------------------------------------------+ ^
* | incfs_md_header | |
* | 1. type of body(BLOCKMAP, FILE_ATTR..) | |
* | 2. size of the whole record header + body | |
* | 3. CRC the whole record header + body | |
* | 4. offset of the previous md record |]------+
* | 5. offset of the next md record (md link) |]---+
* +-----------------------------------------------+ |
* | Metadata record body with useful data | |
* +-----------------------------------------------+ |
* +--->
*
* Other ondisk structures
* -----------------------
* incfs_super_block - backing file header
* incfs_blockmap_entry - a record in a blockmap area that describes size
* and location of a data block.
* Data blocks dont have any particular structure, they are written to the
* backing file in a raw form as they come from a data loader.
*
* Backing file layout
* -------------------
*
*
* +-------------------------------------------+
* | incfs_file_header |]---+
* +-------------------------------------------+ |
* | metadata |<---+
* | incfs_file_signature |]---+
* +-------------------------------------------+ |
* ......................... |
* +-------------------------------------------+ | metadata
* +------->| blockmap area | | list links
* | | [incfs_blockmap_entry] | |
* | | [incfs_blockmap_entry] | |
* | | [incfs_blockmap_entry] | |
* | +--[| [incfs_blockmap_entry] | |
* | | | [incfs_blockmap_entry] | |
* | | | [incfs_blockmap_entry] | |
* | | +-------------------------------------------+ |
* | | ......................... |
* | | +-------------------------------------------+ |
* | | | metadata |<---+
* +----|--[| incfs_blockmap |]---+
* | +-------------------------------------------+ |
* | ......................... |
* | +-------------------------------------------+ |
* +-->| data block | |
* +-------------------------------------------+ |
* ......................... |
* +-------------------------------------------+ |
* | metadata |<---+
* | incfs_file_attr |
* +-------------------------------------------+
*/
#ifndef _INCFS_FORMAT_H
#define _INCFS_FORMAT_H
#include <linux/types.h>
#include <linux/kernel.h>
#include <uapi/linux/incrementalfs.h>
#include "internal.h"
#define INCFS_MAX_NAME_LEN 255
#define INCFS_FORMAT_V1 1
#define INCFS_FORMAT_CURRENT_VER INCFS_FORMAT_V1
enum incfs_metadata_type {
INCFS_MD_NONE = 0,
INCFS_MD_BLOCK_MAP = 1,
INCFS_MD_FILE_ATTR = 2,
INCFS_MD_SIGNATURE = 3,
INCFS_MD_STATUS = 4,
INCFS_MD_VERITY_SIGNATURE = 5,
};
enum incfs_file_header_flags {
INCFS_FILE_MAPPED = 1 << 1,
};
/* Header included at the beginning of all metadata records on the disk. */
struct incfs_md_header {
__u8 h_md_entry_type;
/*
* Size of the metadata record.
* (e.g. inode, dir entry etc) not just this struct.
*/
__le16 h_record_size;
/*
* Was: CRC32 of the metadata record.
* (e.g. inode, dir entry etc) not just this struct.
*/
__le32 h_unused1;
/* Offset of the next metadata entry if any */
__le64 h_next_md_offset;
/* Was: Offset of the previous metadata entry if any */
__le64 h_unused2;
} __packed;
/* Backing file header */
struct incfs_file_header {
/* Magic number: INCFS_MAGIC_NUMBER */
__le64 fh_magic;
/* Format version: INCFS_FORMAT_CURRENT_VER */
__le64 fh_version;
/* sizeof(incfs_file_header) */
__le16 fh_header_size;
/* INCFS_DATA_FILE_BLOCK_SIZE */
__le16 fh_data_block_size;
/* File flags, from incfs_file_header_flags */
__le32 fh_flags;
union {
/* Standard incfs file */
struct {
/* Offset of the first metadata record */
__le64 fh_first_md_offset;
/* Full size of the file's content */
__le64 fh_file_size;
/* File uuid */
incfs_uuid_t fh_uuid;
};
/* Mapped file - INCFS_FILE_MAPPED set in fh_flags */
struct {
/* Offset in original file */
__le64 fh_original_offset;
/* Full size of the file's content */
__le64 fh_mapped_file_size;
/* Original file's uuid */
incfs_uuid_t fh_original_uuid;
};
};
} __packed;
enum incfs_block_map_entry_flags {
INCFS_BLOCK_COMPRESSED_LZ4 = 1,
INCFS_BLOCK_COMPRESSED_ZSTD = 2,
/* Reserve 3 bits for compression alg */
INCFS_BLOCK_COMPRESSED_MASK = 7,
};
/* Block map entry pointing to an actual location of the data block. */
struct incfs_blockmap_entry {
/* Offset of the actual data block. Lower 32 bits */
__le32 me_data_offset_lo;
/* Offset of the actual data block. Higher 16 bits */
__le16 me_data_offset_hi;
/* How many bytes the data actually occupies in the backing file */
__le16 me_data_size;
/* Block flags from incfs_block_map_entry_flags */
__le16 me_flags;
} __packed;
/* Metadata record for locations of file blocks. Type = INCFS_MD_BLOCK_MAP */
struct incfs_blockmap {
struct incfs_md_header m_header;
/* Base offset of the array of incfs_blockmap_entry */
__le64 m_base_offset;
/* Size of the map entry array in blocks */
__le32 m_block_count;
} __packed;
/*
* Metadata record for file signature. Type = INCFS_MD_SIGNATURE
*
* The signature stored here is the APK V4 signature data blob. See the
* definition of incfs_new_file_args::signature_info for an explanation of this
* blob. Specifically, it contains the root hash, but it does *not* contain
* anything that the kernel treats as a signature.
*
* When FS_IOC_ENABLE_VERITY is called on a file without this record, an APK V4
* signature blob and a hash tree are added to the file, and then this metadata
* record is created to record their locations.
*/
struct incfs_file_signature {
struct incfs_md_header sg_header;
__le32 sg_sig_size; /* The size of the signature. */
__le64 sg_sig_offset; /* Signature's offset in the backing file */
__le32 sg_hash_tree_size; /* The size of the hash tree. */
__le64 sg_hash_tree_offset; /* Hash tree offset in the backing file */
} __packed;
/* In memory version of above */
struct incfs_df_signature {
u32 sig_size;
u64 sig_offset;
u32 hash_size;
u64 hash_offset;
};
struct incfs_status {
struct incfs_md_header is_header;
__le32 is_data_blocks_written; /* Number of data blocks written */
__le32 is_hash_blocks_written; /* Number of hash blocks written */
__le32 is_dummy[6]; /* Spare fields */
} __packed;
/*
* Metadata record for verity signature. Type = INCFS_MD_VERITY_SIGNATURE
*
* This record will only exist for verity-enabled files with signatures. Verity
* enabled files without signatures do not have this record. This signature is
* checked by fs-verity identically to any other fs-verity signature.
*/
struct incfs_file_verity_signature {
struct incfs_md_header vs_header;
/* The size of the signature */
__le32 vs_size;
/* Signature's offset in the backing file */
__le64 vs_offset;
} __packed;
/* In memory version of above */
struct incfs_df_verity_signature {
u32 size;
u64 offset;
};
/* State of the backing file. */
struct backing_file_context {
/* Protects writes to bc_file */
struct mutex bc_mutex;
/* File object to read data from */
struct file *bc_file;
/*
* Offset of the last known metadata record in the backing file.
* 0 means there are no metadata records.
*/
loff_t bc_last_md_record_offset;
/*
* Credentials to set before reads/writes
* Note that this is a pointer to the mount_info mi_owner field so
* there is no need to get/put the creds
*/
const struct cred *bc_cred;
};
struct metadata_handler {
loff_t md_record_offset;
loff_t md_prev_record_offset;
void *context;
union {
struct incfs_md_header md_header;
struct incfs_blockmap blockmap;
struct incfs_file_signature signature;
struct incfs_status status;
struct incfs_file_verity_signature verity_signature;
} md_buffer;
int (*handle_blockmap)(struct incfs_blockmap *bm,
struct metadata_handler *handler);
int (*handle_signature)(struct incfs_file_signature *sig,
struct metadata_handler *handler);
int (*handle_status)(struct incfs_status *sig,
struct metadata_handler *handler);
int (*handle_verity_signature)(struct incfs_file_verity_signature *s,
struct metadata_handler *handler);
};
#define INCFS_MAX_METADATA_RECORD_SIZE \
sizeof_field(struct metadata_handler, md_buffer)
/* Backing file context management */
struct mount_info;
struct backing_file_context *incfs_alloc_bfc(struct mount_info *mi,
struct file *backing_file);
void incfs_free_bfc(struct backing_file_context *bfc);
/* Writing stuff */
int incfs_write_blockmap_to_backing_file(struct backing_file_context *bfc,
u32 block_count);
int incfs_write_fh_to_backing_file(struct backing_file_context *bfc,
incfs_uuid_t *uuid, u64 file_size);
int incfs_write_mapping_fh_to_backing_file(struct backing_file_context *bfc,
incfs_uuid_t *uuid, u64 file_size, u64 offset);
int incfs_write_data_block_to_backing_file(struct backing_file_context *bfc,
struct mem_range block,
int block_index, loff_t bm_base_off,
u16 flags);
int incfs_write_hash_block_to_backing_file(struct backing_file_context *bfc,
struct mem_range block,
int block_index,
loff_t hash_area_off,
loff_t bm_base_off,
loff_t file_size);
int incfs_write_signature_to_backing_file(struct backing_file_context *bfc,
struct mem_range sig, u32 tree_size,
loff_t *tree_offset, loff_t *sig_offset);
int incfs_write_status_to_backing_file(struct backing_file_context *bfc,
loff_t status_offset,
u32 data_blocks_written,
u32 hash_blocks_written);
int incfs_write_verity_signature_to_backing_file(
struct backing_file_context *bfc, struct mem_range signature,
loff_t *offset);
/* Reading stuff */
int incfs_read_file_header(struct backing_file_context *bfc,
loff_t *first_md_off, incfs_uuid_t *uuid,
u64 *file_size, u32 *flags);
int incfs_read_blockmap_entry(struct backing_file_context *bfc, int block_index,
loff_t bm_base_off,
struct incfs_blockmap_entry *bm_entry);
int incfs_read_blockmap_entries(struct backing_file_context *bfc,
struct incfs_blockmap_entry *entries,
int start_index, int blocks_number,
loff_t bm_base_off);
int incfs_read_next_metadata_record(struct backing_file_context *bfc,
struct metadata_handler *handler);
ssize_t incfs_kread(struct backing_file_context *bfc, void *buf, size_t size,
loff_t pos);
ssize_t incfs_kwrite(struct backing_file_context *bfc, const void *buf,
size_t size, loff_t pos);
#endif /* _INCFS_FORMAT_H */