blob: 80771ff9afa66d0a0df7f7238101ca8632f4e624 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <stdbool.h>
#if BUILD_STORAGE_TEST
#define FULL_ASSERT 1
#else
#define FULL_ASSERT 0
#endif
#if FULL_ASSERT
#define full_assert assert
#else
#define full_assert(x) \
do { \
} while (0)
#endif
#include "block_mac.h"
#include "block_set.h"
#include "block_tree.h"
/**
* struct super_block_backup - Backup of root block for file system state
* @flags: Super-block flags for the backup, with the bits in
* SUPER_BLOCK_VERSION_MASK set to 0 (i.e. the backup does not
* contain a version).
* @free: Block and mac of backup free set root node.
* @files: Block and mac of backup files tree root node.
* @checkpoint: Block and mac of the backup checkpoint metadata block, if
* any.
*
* Block numbers and macs in @free and @files are packed as indicated by
* @block_num_size and @mac_size, but unlike other on-disk data, the size of the
* whole field is always the full 24 bytes needed for a 8 byte block number and
* 16 byte mac so this structure is always a fixed size.
*/
struct super_block_backup {
uint32_t flags;
struct block_mac free;
struct block_mac files;
struct block_mac checkpoint;
};
STATIC_ASSERT(sizeof(struct super_block_backup) == 76);
/**
* struct fs - File system state
* @node: List node for fs_list.
* @dev: Main block device.
* @transactions: Transaction list.
* @allocated: List of block sets containing blocks
* allocated by active transactions.
* @free: Block set of free blocks.
* @files: B+ tree of all files.
* @checkpoint: Block and mac of the latest committed
* checkpoint metadata. Points to a block that
* holds the files root and free set at the
* time of the most recent checkpoint.
* @checkpoint_free: Block set of free blocks at the time of the
* last committed checkpoint. A block is only
* free if it is in both @free and
* @checkpoint_free.
* @super_dev: Block device used to store super blocks.
* @readable: %true if the file system is initialized and
* readable. If false, no reads are valid and
* @writable must be %false.
* @writable: %true if the file system may be modified. If
* %false, filesystem contents may be readable,
* but no superblock or block changes are
* permitted.
* @allow_tampering: %false if the filesystem must detect
* tampering of read and write operations.
* %true otherwise. If %false, when a write
* operation is reported as successfully
* completed it should not be possible for
* non-secure code to modify the stored data.
* @key: Key to use for encrypt, decrypt and mac.
* @super_block: Block numbers in @super_dev to store
* super-block in.
* @super_block_version: Last read or written super block version.
* @written_super_block_version: Last written super block version.
* @main_repaired: %true if main file system has been repaired
* since being wiped. In alternate state only
* used to persist this flag in the super
* block.
* @alternate_data: If true, the current superblock is for a
* filesystem with a backing store in an
* alternate data location and @backup contains
* the superblock of the normal filesystem. If
* false, @backup may contain a backup of the
* superblock for an alternate filesystem, but
* it may be outdated.
* @needs_full_scan: %true if an error was detected in this file
* system data and the file system should be
* scanned on the next mount. Persisted to the
* super block so that we can initiate a scan
* the next time we mount the file system.
* @checkpoint_required: %true if a checkpoint must be created before
* committing any changes to the file system.
* @backup: Backup superblock of other filesystem state
* (alternate if @alternate_data is false, main
* otherwise) Should be preserved across all
* filesystem operations after initialization.
* @min_block_num: First block number that can store non
* super blocks.
* @block_num_size: Number of bytes used to store block numbers.
* @mac_size: Number of bytes used to store mac values.
* Must be 16 if @dev is not tamper_detecting.
* @reserved_count: Number of free blocks reserved for active
* transactions.
* @initial_super_block_tr: Internal transaction containing initial
* super block that must be written before any
* other data. If %NULL superblock is already
* a safe state.
* @name: File system name, used to identify the file
* system in debugging and error reporting
* messages.
*/
struct fs {
struct list_node node;
struct block_device* dev;
struct list_node transactions;
struct list_node allocated;
struct block_set free;
struct block_tree files;
struct block_mac checkpoint;
struct block_set checkpoint_free;
struct block_device* super_dev;
bool readable;
bool writable;
bool allow_tampering;
const struct key* key;
data_block_t super_block[2];
unsigned int super_block_version;
unsigned int written_super_block_version;
bool main_repaired;
bool alternate_data;
bool needs_full_scan;
bool checkpoint_required;
struct super_block_backup backup;
data_block_t min_block_num;
size_t block_num_size;
size_t mac_size;
data_block_t reserved_count;
struct transaction* initial_super_block_tr;
const char* name;
};
bool update_super_block(struct transaction* tr,
const struct block_mac* free,
const struct block_mac* files,
const struct block_mac* checkpoint);
void fs_mark_scan_required(struct fs* fs);
/**
* typedef fs_init_flags32_t - Flags that control filesystem clearing and
* backups. These flags may be ORed together.
*
* %FS_INIT_FLAGS_NONE
* No flags set
*
* %FS_INIT_FLAGS_DO_CLEAR
* Unconditionally clear the filesystem, regardless of corruption state.
* %FS_INIT_FLAGS_RECOVERY_* flags are ignored when combined with this flag.
*
* %FS_INIT_FLAGS_RECOVERY_CLEAR_ALLOWED
* Allows clearing of corrupt filesystem.
*
* %FS_INIT_FLAGS_ALTERNATE_DATA
* Indicates that the filesystem is temporarily running on top of an alternate
* location for the @dev block device and rollback should be enforced
* separately from the normal mode.
*
* %FS_INIT_FLAGS_ALLOW_TAMPERING
* Allow this filesystem to be initialized with the super block not stored on
* a tamper-detecting block device. This filesystem WILL NOT detect any
* tampering and a malicious actor may arbitrarily roll it back to any
* previous state.
*
* %FS_INIT_FLAGS_RESTORE_CHECKPOINT
* Restore this filesystem to the current checkpointed state, discarding any
* changes since that checkpoint was made.
*
* %FS_INIT_FLAGS_AUTO_CHECKPOINT
* Automatically create a checkpoint of the filesystem state on mount.
*/
typedef uint32_t fs_init_flags32_t;
#define FS_INIT_FLAGS_NONE 0U
#define FS_INIT_FLAGS_DO_CLEAR (1U << 0)
#define FS_INIT_FLAGS_RECOVERY_CLEAR_ALLOWED (1U << 1)
#define FS_INIT_FLAGS_ALTERNATE_DATA (1U << 2)
#define FS_INIT_FLAGS_ALLOW_TAMPERING (1U << 3)
#define FS_INIT_FLAGS_RESTORE_CHECKPOINT (1U << 4)
#define FS_INIT_FLAGS_AUTO_CHECKPOINT (1U << 5)
#define FS_INIT_FLAGS_MASK \
(FS_INIT_FLAGS_DO_CLEAR | FS_INIT_FLAGS_RECOVERY_CLEAR_ALLOWED | \
FS_INIT_FLAGS_ALTERNATE_DATA | FS_INIT_FLAGS_ALLOW_TAMPERING | \
FS_INIT_FLAGS_RESTORE_CHECKPOINT | FS_INIT_FLAGS_AUTO_CHECKPOINT)
int fs_init(struct fs* fs,
const char* name,
const struct key* key,
struct block_device* dev,
struct block_device* super_dev,
fs_init_flags32_t flags);
static inline bool fs_is_repaired(struct fs* fs) {
return fs->main_repaired && !fs->alternate_data;
}
static inline bool fs_is_readable(struct fs* fs) {
return fs->readable;
}
static inline bool fs_is_writable(struct fs* fs) {
return fs->writable;
}
/**
* enum fs_check_result - Result of a filesystem check
* @FS_CHECK_NO_ERROR: No error was enountered in the checked blocks.
* @FS_CHECK_INVALID_BLOCK: A MAC mismatch error or invalid block was
* encountered while trying to load a block in the
* file-system. This type of error may indicate that
* the non-secure data is out of sync with the RPMB
* superblock. The file-system is likely corrupt.
* @FS_CHECK_INVALID_FREE_SET: The free set was not internally valid or invalid
* blocks were encountered in the free set tree.
* @FS_CHECK_INVALID_FILE_TREE: The file tree was not internally valid but no
* invalid blocks were encountered.
* @FS_CHECK_UNKNOWN: An unknown error was encountered while checking the
* file-system. The file-system may not be entirely
* readable or valid.
*/
enum fs_check_result {
FS_CHECK_NO_ERROR = 0,
FS_CHECK_INVALID_BLOCK,
FS_CHECK_INVALID_FREE_SET,
FS_CHECK_INVALID_FILE_TREE,
FS_CHECK_UNKNOWN,
};
/**
* fs_check_full - Check the file system tree
* @fs: File system state object.
*
* Walk the filesystem tree and visit each file, checking the file tree and each
* file block map for consistency.
*
* Returns @fs_check_result.FS_CHECK_NO_ERROR if no corruption was encountered
* or any encountered corruption was repaired. Returns another @fs_check_result
* variant describing the error if the filesystem remains corrupted after this
* operation. Errors are prioritized in the following order (highest to lowest):
* %FS_CHECK_INVALID_BLOCK (except in the free set, which is reported
* separately), %FS_CHECK_INVALID_FILE_TREE, %FS_CHECK_INVALID_FREE_SET,
* %FS_CHECK_UNKNOWN. This ordering is intended to allow callers to
* differentiate between invalid blocks that indicate corruption and possibly
* transient communication errors with the storage proxy.
*/
enum fs_check_result fs_check_full(struct fs* fs);
/**
* fs_check_quick - Quickly check the file-system tree
* @fs: File system state object.
*
* Perform a basic check that the file-system roots are valid. Suitable for use
* while mounting file-systems where we don't want to pay the cost to walk the
* entire file-system tree.
*
* Returns @fs_check_result.FS_CHECK_NO_ERROR if no corruption was encountered,
* or another @fs_check_result variant describing the error.
*/
enum fs_check_result fs_check_quick(struct fs* fs);
/**
* fs_check - Check the file system tree
* @fs: File system state object.
*
* If the filesystem was not previously marked as requiring a full scan, perform
* a quick check (i.e. behave the same as fs_check_quick()). If the file system
* has been marked as potentially having an error, do a full scan using
* fs_check_full().
*
* Returns an @fs_check_result variant, see fs_check_quick() and fs_check_full()
* for details.
*/
enum fs_check_result fs_check(struct fs* fs);
void fs_file_tree_init(const struct fs* fs, struct block_tree* tree);
void fs_unknown_super_block_state_all(void);
void write_current_super_block(struct fs* fs, bool reinitialize);
void fs_destroy(struct fs* fs);
/**
* fs_fail_all_transactions - Fail all pending transactions in all filesystems
*
* This functions fails any pending transactions that have not already failed.
*/
void fs_fail_all_transactions(void);