408 lines
13 KiB
C
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 \
|
||
|
FIELD_SIZEOF(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 */
|